Compare commits
2 Commits
2.0.0-rc.3
...
2.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
f34fdaf162 | |||
6c951a5e67 |
43
.github/ISSUE_TEMPLATE.md
vendored
43
.github/ISSUE_TEMPLATE.md
vendored
@ -1,33 +1,38 @@
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
```
|
||||
**Note: for support questions, please use one of these channels:** https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.
|
||||
|
||||
* **I'm submitting a ... **
|
||||
[ ] bug report
|
||||
[ ] feature request
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
|
||||
**Current behavior**
|
||||
[ ] support request => Please do not submit support request here, see note at the top of this template.
|
||||
|
||||
|
||||
**Expected/desired behavior**
|
||||
|
||||
|
||||
**Reproduction of the problem**
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
* **Do you want to request a *feature* or report a *bug*?**
|
||||
|
||||
|
||||
|
||||
**What is the expected behavior?**
|
||||
* **What is the current behavior?**
|
||||
|
||||
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
* **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem** via
|
||||
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
|
||||
|
||||
|
||||
**Please tell us about your environment:**
|
||||
* **What is the expected behavior?**
|
||||
|
||||
* **Angular version:** 2.0.0-rc.X
|
||||
|
||||
* **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 ]
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5 | Dart]
|
||||
|
||||
|
||||
* **What is the motivation / use case for changing the behavior?**
|
||||
|
||||
|
||||
|
||||
* **Please tell us about your environment:**
|
||||
|
||||
- Angular version: 2.0.0-beta.X
|
||||
- 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 ]
|
||||
- Language: [all | TypeScript X.X | ES6/7 | ES5 | Dart]
|
||||
|
||||
|
||||
|
||||
* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc)
|
||||
|
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,36 +1,24 @@
|
||||
**Please check if the PR fulfills these requirements**
|
||||
* **Please check if the PR fulfills these requirements**
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit-message-format
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
|
||||
**What kind of change does this PR introduce?** (check one with "x")
|
||||
```
|
||||
[ ] Bugfix
|
||||
[ ] Feature
|
||||
[ ] Code style update (formatting, local variables)
|
||||
[ ] Refactoring (no functional changes, no api changes)
|
||||
[ ] Build related changes
|
||||
[ ] CI related changes
|
||||
[ ] Other... Please describe:
|
||||
```
|
||||
|
||||
**What is the current behavior?** (You can also link to an open issue here)
|
||||
* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)
|
||||
|
||||
|
||||
|
||||
**What is the new behavior?**
|
||||
* **What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?** (check one with "x")
|
||||
```
|
||||
[ ] Yes
|
||||
[ ] No
|
||||
```
|
||||
|
||||
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
|
||||
* **What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
**Other information**:
|
||||
|
||||
* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
||||
|
||||
|
||||
|
||||
* **Other information**:
|
||||
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -21,6 +21,11 @@ tmp
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
# Or type definitions we mirror from github
|
||||
# (NB: these lines are removed in publish-build-artifacts.sh)
|
||||
**/typings/**/*.d.ts
|
||||
**/typings/tsd.cached.json
|
||||
|
||||
# Include when developing application packages.
|
||||
pubspec.lock
|
||||
.c9
|
||||
@ -44,6 +49,3 @@ npm-debug.log
|
||||
|
||||
# built dart payload tests
|
||||
/modules_dart/payload/**/build
|
||||
|
||||
# rollup-test output
|
||||
/modules/rollup-test/dist/
|
||||
|
269
.travis.yml
269
.travis.yml
@ -3,194 +3,135 @@ sudo: false
|
||||
node_js:
|
||||
- '5.4.1'
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
# needed to install g++ that is used by npms's native modules
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
# - $HOME/.pub-cache
|
||||
- $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
|
||||
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
|
||||
|
||||
# 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
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- 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.
|
||||
- CI_MODE=js
|
||||
- CI_MODE=e2e
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
- 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
|
||||
- MODE=payload
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "CI_MODE=saucelabs_optional"
|
||||
- env: "CI_MODE=browserstack_optional"
|
||||
- env: "MODE=saucelabs_optional"
|
||||
- env: "MODE=browserstack_optional"
|
||||
# Tracked in https://github.com/angular/angular/issues/7050
|
||||
- env: "MODE=typescript_next"
|
||||
|
||||
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:
|
||||
- ./scripts/ci-lite/install.sh
|
||||
- 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:
|
||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||
- 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:
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
- 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
|
||||
|
||||
|
||||
#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=
|
||||
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=
|
||||
|
748
CHANGELOG.md
748
CHANGELOG.md
@ -1,735 +1,3 @@
|
||||
<a name="2.0.0-rc.2"></a>
|
||||
# 2.0.0-rc.2 (2016-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Animation:** Problem with decimals using commas as decimal separation ([5f3d02b](https://github.com/angular/angular/commit/5f3d02b)), closes [#6335](https://github.com/angular/angular/issues/6335) [#6338](https://github.com/angular/angular/issues/6338)
|
||||
* **animations:** Ensure AUTO styles are cleared at the end of the state-change animation ([55860e1](https://github.com/angular/angular/commit/55860e1)), closes [#9014](https://github.com/angular/angular/issues/9014) [#9015](https://github.com/angular/angular/issues/9015)
|
||||
* **animations:** Ensure the web-animations driver converts style props to camel-case ([4d51158](https://github.com/angular/angular/commit/4d51158)), closes [#9111](https://github.com/angular/angular/issues/9111) [#9112](https://github.com/angular/angular/issues/9112)
|
||||
* **bootstrap:** Swap coreBootstrap() and coreLoadAndBootstrap() arguments ([f95a604](https://github.com/angular/angular/commit/f95a604))
|
||||
* **browser:** Platform code cleanup ([75e6dfb](https://github.com/angular/angular/commit/75e6dfb))
|
||||
* **build:** Change publish-build-artifacts.sh to work with new packaging system ([d414734](https://github.com/angular/angular/commit/d414734))
|
||||
* **build:** Declare the secure GITHUB_TOKEN_ANGULAR for package publishing from Travis ([ff40072](https://github.com/angular/angular/commit/ff40072))
|
||||
* **build:** Fix an error in package publishing step where the script errors when a UMD bundl ([bac1a6e](https://github.com/angular/angular/commit/bac1a6e))
|
||||
* **build:** Fix broken e2e test Travis task by running the right variation of sed on Travis ([267d864](https://github.com/angular/angular/commit/267d864))
|
||||
* **build:** Force a compatible baseURL for systemjs-builder ([e0c83f6](https://github.com/angular/angular/commit/e0c83f6)), closes [#7167](https://github.com/angular/angular/issues/7167) [#7360](https://github.com/angular/angular/issues/7360)
|
||||
* **build:** Hook up publish-build-artifacts to Travis ([97a1084](https://github.com/angular/angular/commit/97a1084))
|
||||
* **build:** Release compiler_cli packages along with rest of @angular packages and use ANGUL ([9a05ca9](https://github.com/angular/angular/commit/9a05ca9))
|
||||
* **build:** update API spec to include the return value. ([b60eecf](https://github.com/angular/angular/commit/b60eecf))
|
||||
* **ci:** extra API in public_api_spec ([f154e2c](https://github.com/angular/angular/commit/f154e2c))
|
||||
* **ci:** incorrect import ([cb980d3](https://github.com/angular/angular/commit/cb980d3))
|
||||
* **ci:** make ci fail when compiler integration test fails ([5941c92](https://github.com/angular/angular/commit/5941c92a31aee3b7e0300f5ab91acb3c68b95171))
|
||||
* **codegen:** codegen all files in the program, not just roots ([0d71345](https://github.com/angular/angular/commit/0d71345)), closes [#8475](https://github.com/angular/angular/issues/8475)
|
||||
* **compiler:** Support for comment finishing with multiple dashes ([60a2ba8](https://github.com/angular/angular/commit/60a2ba8)), closes [#7119](https://github.com/angular/angular/issues/7119)
|
||||
* **compiler:** Added support for '* as m' style imports. (#9077) ([e178ee4](https://github.com/angular/angular/commit/e178ee4))
|
||||
* **compiler:** Added unit test to ReflectorHost and fixed issues (#9052) ([0658eb4](https://github.com/angular/angular/commit/0658eb4)), closes [(#9052](https://github.com/(/issues/9052)
|
||||
* **compiler:** allow --noImplicitAny ([817ddfa](https://github.com/angular/angular/commit/817ddfa))
|
||||
* **compiler:** allow decorators defined in the same file ([c1154b3](https://github.com/angular/angular/commit/c1154b3))
|
||||
* **compiler:** emit correct types for literal arrays and maps. ([a81923b](https://github.com/angular/angular/commit/a81923b))
|
||||
* **compiler:** have CSS parser support nested parentheses inside functions ([ceac045](https://github.com/angular/angular/commit/ceac045)), closes [#7580](https://github.com/angular/angular/issues/7580)
|
||||
* **compiler:** Improved error reporting of the static reflector. ([cf3548a](https://github.com/angular/angular/commit/cf3548a)), closes [#8978](https://github.com/angular/angular/issues/8978) [#9011](https://github.com/angular/angular/issues/9011)
|
||||
* **compiler:** properly report missing DI tokens (#9065) ([3aca5ff](https://github.com/angular/angular/commit/3aca5ff)), closes [#8245](https://github.com/angular/angular/issues/8245)
|
||||
* **compiler:** Reflector generates imports for '..' relative modules. ([35ea02f](https://github.com/angular/angular/commit/35ea02f)), closes [#9003](https://github.com/angular/angular/issues/9003) [#9004](https://github.com/angular/angular/issues/9004)
|
||||
* **compiler:** report errors for queries without selectors (#9018) ([057abef](https://github.com/angular/angular/commit/057abef)), closes [#4489](https://github.com/angular/angular/issues/4489)
|
||||
* **compiler:** support lifecycle hooks in compiler_cli ([7150ace](https://github.com/angular/angular/commit/7150ace))
|
||||
* **compiler:** support string tokens with `.` inside. ([67c80fb](https://github.com/angular/angular/commit/67c80fb)), closes [#8178](https://github.com/angular/angular/issues/8178)
|
||||
* **compiler:** throw an error if variable with the same name is already defined. (#7209) ([9036f78](https://github.com/angular/angular/commit/9036f78))
|
||||
* **compiler_cli:** allow to use builtin directives like `NgIf`, … ([edec158](https://github.com/angular/angular/commit/edec158)), closes [#8454](https://github.com/angular/angular/issues/8454)
|
||||
* **compiler_cli:** normalize used directives ([ff36b03](https://github.com/angular/angular/commit/ff36b03)), closes [#8677](https://github.com/angular/angular/issues/8677)
|
||||
* **Control:** Support select multiple with Control class (#8069) ([84f859d](https://github.com/angular/angular/commit/84f859d))
|
||||
* **core:** accurate dev mode message for dart (#8403) ([19e6538](https://github.com/angular/angular/commit/19e6538))
|
||||
* **core:** don’t detach nested view containers when destroying a view ([e2b1e15](https://github.com/angular/angular/commit/e2b1e15)), closes [#8458](https://github.com/angular/angular/issues/8458) [#8471](https://github.com/angular/angular/issues/8471)
|
||||
* **core:** fix build ([3ff20cd](https://github.com/angular/angular/commit/3ff20cd))
|
||||
* **core:** fix type of `DebugNode.properties` (#8964) ([ddd2ac4](https://github.com/angular/angular/commit/ddd2ac4)), closes [(#8964](https://github.com/(/issues/8964)
|
||||
* **core:** Keep core exports separate from core/testing exports. ([f4f6b87](https://github.com/angular/angular/commit/f4f6b87))
|
||||
* **core:** Keep core exports separate from core/testing exports. (#8930) ([21fc1bb](https://github.com/angular/angular/commit/21fc1bb))
|
||||
* **core:** remove @internal annotation from PLATFORM_CORE_PROVIDERS ([2ab1085](https://github.com/angular/angular/commit/2ab1085)), closes [#8819](https://github.com/angular/angular/issues/8819)
|
||||
* **core:** QueryList documentation (#8976) ([b160ada](https://github.com/angular/angular/commit/b160ada))
|
||||
* **di:** type error in InvalidProviderError ([c43636f](https://github.com/angular/angular/commit/c43636f)), closes [#7729](https://github.com/angular/angular/issues/7729)
|
||||
* **doc:** Add missing comma in example (#8769) ([00475f2](https://github.com/angular/angular/commit/00475f2))
|
||||
* **docs:** Fix a missing opening bracket (#8331) ([d75f928](https://github.com/angular/angular/commit/d75f928)), closes [(#8331](https://github.com/(/issues/8331)
|
||||
* **DomRegistry:** fix svg support ([307d105](https://github.com/angular/angular/commit/307d105))
|
||||
* **facade:** change EventEmitter to be sync by default (#8761) ([e5904f4](https://github.com/angular/angular/commit/e5904f4))
|
||||
* **forms:** radio buttons with different names should not share state ([6dc88f5](https://github.com/angular/angular/commit/6dc88f5)), closes [#7051](https://github.com/angular/angular/issues/7051)
|
||||
* **forms:** rename old forms folder to forms-deprecated ([515a8e0](https://github.com/angular/angular/commit/515a8e0))
|
||||
* **forms:** update accessor value when native select value changes ([7a2ce7f](https://github.com/angular/angular/commit/7a2ce7f)), closes [#8710](https://github.com/angular/angular/issues/8710)
|
||||
* **forms:** update value and validity when controls are added ([50acb96](https://github.com/angular/angular/commit/50acb96)), closes [#8826](https://github.com/angular/angular/issues/8826)
|
||||
* **forms:** separate ngModelGroup from formGroupName ([5c0cfde](https://github.com/angular/angular/commit/5c0cfdee48ba5aa48528a1c20ffd99318ee716ae))
|
||||
* **HTMLParser:** properly report errors for not properly closed tags (#8999) ([6f281ab](https://github.com/angular/angular/commit/6f281ab)), closes [(#8999](https://github.com/(/issues/8999) [#7849](https://github.com/angular/angular/issues/7849)
|
||||
* **http:** remove peerDep on @angular/common ([29c2dcf](https://github.com/angular/angular/commit/29c2dcf))
|
||||
* **http:** respect custom Content-Type header in XHRConnection (#9131) ([537e99b](https://github.com/angular/angular/commit/537e99b)), closes [#9130](https://github.com/angular/angular/issues/9130)
|
||||
* **http:** Set response.ok ([9234035](https://github.com/angular/angular/commit/9234035)), closes [#6390](https://github.com/angular/angular/issues/6390) [#6503](https://github.com/angular/angular/issues/6503)
|
||||
* **Location:** make Location#platformStrategy:LocationStrategy property private ([e93b3d2](https://github.com/angular/angular/commit/e93b3d2))
|
||||
* **metadata:** Allow spacing in multiple selectors (#7418) ([b2e804c](https://github.com/angular/angular/commit/b2e804c))
|
||||
* **ngc:** depend on correct tsc-wrapped package ([16ef21d](https://github.com/angular/angular/commit/16ef21d))
|
||||
* **ngSwitch:** use switchCase instead of switchWhen (#9076) ([e1fcab7](https://github.com/angular/angular/commit/e1fcab7))
|
||||
* **ngUpgrade:** prevent digest already in progress (#9054) ([7cefec7](https://github.com/angular/angular/commit/7cefec7))
|
||||
* **pipes:** handle undefined value in slice ([83c19a1](https://github.com/angular/angular/commit/83c19a1)), closes [#7152](https://github.com/angular/angular/issues/7152)
|
||||
* **platform-browser:** fix rollup config ([f4b9728](https://github.com/angular/angular/commit/f4b9728))
|
||||
* **platform-browser:** split dynamic bits in platform-browser into platform-browser-dynamic ([6fc267f](https://github.com/angular/angular/commit/6fc267f22ccb392f2df9808d0415bc690b77a82f))
|
||||
* **platform-server:** should declare it's dependency on parse5 via package.json ([9485f5a](https://github.com/angular/angular/commit/9485f5a))
|
||||
* **platform-server:** correctly import private DOMTestComponentRenderer ([7afee97](https://github.com/angular/angular/commit/7afee97d1b837cdc808b0a5e5206c306f5700263))
|
||||
* **playground:** fix WebWorker single_thread example ([29c77df](https://github.com/angular/angular/commit/29c77df))
|
||||
* **query:** set fixed `@ViewChild` / `@ContentChild` right after the view is created ([c3d2459](https://github.com/angular/angular/commit/c3d2459)), closes [#9040](https://github.com/angular/angular/issues/9040)
|
||||
* **renderer:** remove unecessary setElementStyles method ([e504d4e](https://github.com/angular/angular/commit/e504d4e)), closes [#9000](https://github.com/angular/angular/issues/9000) [#9009](https://github.com/angular/angular/issues/9009)
|
||||
* **Renderer:** update signatures to make RenderDebugInfo optional ([b7b5678](https://github.com/angular/angular/commit/b7b5678)), closes [#8466](https://github.com/angular/angular/issues/8466) [#8859](https://github.com/angular/angular/issues/8859)
|
||||
* **Request:** Change Request.text's return type to string ([b2e0946](https://github.com/angular/angular/commit/b2e0946)), closes [#8138](https://github.com/angular/angular/issues/8138)
|
||||
* **router:** Added pushState fallback for IE 9 browser. ([bab6023](https://github.com/angular/angular/commit/bab6023)), closes [#6506](https://github.com/angular/angular/issues/6506) [#7929](https://github.com/angular/angular/issues/7929)
|
||||
* **router:** browser back and forward buttons not working correctly. ([595bcdd](https://github.com/angular/angular/commit/595bcdd)), closes [#8524](https://github.com/angular/angular/issues/8524) [#8532](https://github.com/angular/angular/issues/8532)
|
||||
* **router:** don't mark the RouterOutletMap as internal ([45de65b](https://github.com/angular/angular/commit/45de65b))
|
||||
* **router:** ensuring MatchedUrl pass query params ([7d853dd](https://github.com/angular/angular/commit/7d853dd))
|
||||
* **router:** openning links in new tab ([fa2ce81](https://github.com/angular/angular/commit/fa2ce81)), closes [#5908](https://github.com/angular/angular/issues/5908) [#6806](https://github.com/angular/angular/issues/6806) [#7749](https://github.com/angular/angular/issues/7749) [#8806](https://github.com/angular/angular/issues/8806) [#8821](https://github.com/angular/angular/issues/8821)
|
||||
* **router:** provide a top-level route segment for injection ([b8136cc](https://github.com/angular/angular/commit/b8136cc))
|
||||
* **router:** replace state when path is equal to current path (#8766) ([b2a7fd0](https://github.com/angular/angular/commit/b2a7fd0))
|
||||
* **Router:** do not kill event-emitter on navigation failure ([cbeeff2](https://github.com/angular/angular/commit/cbeeff2)), closes [#7692](https://github.com/angular/angular/issues/7692) [#7532](https://github.com/angular/angular/issues/7532) [#7692](https://github.com/angular/angular/issues/7692)
|
||||
* **Router:** replace state when normalized path is equal to current normalized path ([2bf21e1](https://github.com/angular/angular/commit/2bf21e1)), closes [#7829](https://github.com/angular/angular/issues/7829) [#7897](https://github.com/angular/angular/issues/7897)
|
||||
* **scripts:** fix: correct failing to push into builds repo on rerun ([17f317d](https://github.com/angular/angular/commit/17f317d31efbd68bc6bec73f56c8ce039825d23b))
|
||||
* **security:** support XSSI prefixes with and without commas. ([729dc3b](https://github.com/angular/angular/commit/729dc3b))
|
||||
* **test-runner:** make karma internal reporter compatible with 0.13.20 (#8977) ([fe8a7b0](https://github.com/angular/angular/commit/fe8a7b0))
|
||||
* **testing:** add discardPeriodicTasks to be used with fakeAsync (#8629) ([0cb93a4](https://github.com/angular/angular/commit/0cb93a4)), closes [#8616](https://github.com/angular/angular/issues/8616)
|
||||
* **tests:** Execute the security specs only once ([9634e8d](https://github.com/angular/angular/commit/9634e8d))
|
||||
* **travis:** pin the version of tsickle for offline_compiler_test ([7aa1790](https://github.com/angular/angular/commit/7aa1790))
|
||||
* **tsickle:** put the tsickle support code at EOF ([3cfe281](https://github.com/angular/angular/commit/3cfe281))
|
||||
* **typings:** remove rxjs workaround ([798bfac](https://github.com/angular/angular/commit/798bfac)), closes [#7198](https://github.com/angular/angular/issues/7198)
|
||||
* **upgrade:** allow deeper nesting of ng2 components/directives (#8949) ([48bf349](https://github.com/angular/angular/commit/48bf349))
|
||||
* **upgrade:** allow functions for template and templateUrl (#9022) ([a19c4e8](https://github.com/angular/angular/commit/a19c4e8))
|
||||
* **upgrade:** Ensure upgrade adapter works on angular.js 1.2 (#8647) ([cbc8d0a](https://github.com/angular/angular/commit/cbc8d0a))
|
||||
* **upgrade:** fallback to root ng2 injector when element is compiled outside the document (#86 ([db82906](https://github.com/angular/angular/commit/db82906))
|
||||
* **upgrade:** make bindings available on $scope in controller & link function (#8645) ([6cdc53c](https://github.com/angular/angular/commit/6cdc53c))
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** provide support for offline compilation ([fa0718b](https://github.com/angular/angular/commit/fa0718b))
|
||||
* **animations:** support styling of the default animation state ([36d25f2](https://github.com/angular/angular/commit/36d25f2)), closes [#9013](https://github.com/angular/angular/issues/9013)
|
||||
* **build:** Added a version stamp in .metadata.json files. ([2d8f776](https://github.com/angular/angular/commit/2d8f776)), closes [#8974](https://github.com/angular/angular/issues/8974) [#8981](https://github.com/angular/angular/issues/8981)
|
||||
* **ChangeDetectorRef:** make detectChanges() correct ([6028368](https://github.com/angular/angular/commit/6028368)), closes [#8599](https://github.com/angular/angular/issues/8599)
|
||||
* **common:** DatePipe supports ISO string ([abc266f](https://github.com/angular/angular/commit/abc266f)), closes [#7794](https://github.com/angular/angular/issues/7794)
|
||||
* **common/datePipe:** change date formatter to use correct pattern closes #7008 (#8154) ([324f014](https://github.com/angular/angular/commit/324f014)), closes [#7008](https://github.com/angular/angular/issues/7008) [(#8154](https://github.com/(/issues/8154)
|
||||
* **compiler:** Add support for limited function calls in metadata ([5504ca1](https://github.com/angular/angular/commit/5504ca1e389f7bfd74604b8573ddaab3e9f07b0d))
|
||||
* **compiler:** Add support for `<ng-container>` ([0dbff55](https://github.com/angular/angular/commit/0dbff55bc6750653c5f8decc06d07e7269e3d6a5))
|
||||
* **ComponentResolver:** Add a SystemJS resolver for compiled apps (#9145) ([a6e5ddc](https://github.com/angular/angular/commit/a6e5ddc))
|
||||
* **core:** add a component resolver that can load components lazily using system.js ([1a0aea6](https://github.com/angular/angular/commit/1a0aea6))
|
||||
* **core:** introduce support for animations ([5e0f8cf](https://github.com/angular/angular/commit/5e0f8cf)), closes [#8734](https://github.com/angular/angular/issues/8734)
|
||||
* **core/linker:** add SimpleChanges type to lifecycle_hooks to simplify OnChanges signature ([0a872ff](https://github.com/angular/angular/commit/0a872ff)), closes [#8557](https://github.com/angular/angular/issues/8557)
|
||||
* **debug:** collect styles and classes for the DebugElement ([155b882](https://github.com/angular/angular/commit/155b882))
|
||||
* **enableDebugTools:** return ComponentRef ([4086b49](https://github.com/angular/angular/commit/4086b49))
|
||||
* **forms:** add new forms folder ([4c39eac](https://github.com/angular/angular/commit/4c39eac))
|
||||
* **forms:** add the submitted flag to NgForm and NgFormModel directives ([420e83a](https://github.com/angular/angular/commit/420e83a)), closes [#2960](https://github.com/angular/angular/issues/2960) [#7449](https://github.com/angular/angular/issues/7449)
|
||||
* **forms:** allow ngModel to register with parent form ([4ed6cf7](https://github.com/angular/angular/commit/4ed6cf7))
|
||||
* **forms:** compose validator fns automatically if arrays ([61960c5](https://github.com/angular/angular/commit/61960c51a3b21d1cfba523f53016f6284182d4e3))
|
||||
* **forms:** support setting control name in ngModelOptions ([a191e96](https://github.com/angular/angular/commit/a191e9697c32062eda06cd1f1cfd856d89c16026))
|
||||
* **forms:** add easy way to switch between forms modules ([22916bb](https://github.com/angular/angular/commit/22916bb5d1abf2818d7d8d99d39605af251f42e4))
|
||||
* **HtmlLexer:** add support for alphabetic cases ([43148d8](https://github.com/angular/angular/commit/43148d8))
|
||||
* **http:** added withCredentials support ([95af14b](https://github.com/angular/angular/commit/95af14b)), closes [#7281](https://github.com/angular/angular/issues/7281) [#7281](https://github.com/angular/angular/issues/7281)
|
||||
* **http:** automatically set request Content-Type header based on body type ([0f0a8ad](https://github.com/angular/angular/commit/0f0a8ad)), closes [#7310](https://github.com/angular/angular/issues/7310)
|
||||
* **http:** implement Response.prototype.toString() to make for a nicer error message ([89f6108](https://github.com/angular/angular/commit/89f6108)), closes [#7511](https://github.com/angular/angular/issues/7511)
|
||||
* **http:** set the statusText property from the XMLHttpRequest instance ([3019140](https://github.com/angular/angular/commit/3019140)), closes [#4162](https://github.com/angular/angular/issues/4162)
|
||||
* **i18n:** extract messages ([ac11567](https://github.com/angular/angular/commit/ac11567))
|
||||
* **i18n:** support implicit tags/attributes ([3e5716e](https://github.com/angular/angular/commit/3e5716e))
|
||||
* **i18n:** generate error on unknown cases ([5267115](https://github.com/angular/angular/commit/5267115)), closes [#9094](https://github.com/angular/angular/issues/9094)
|
||||
* **i18n:** Add file paths to error messages ([fe01e2e](https://github.com/angular/angular/commit/fe01e2efb7e21ed0331e403a7508ac7fca946108))
|
||||
* **metadata:** emit all methods ([29700aa](https://github.com/angular/angular/commit/29700aa))
|
||||
* **NgTemplateOutlet:** add context to NgTemplateOutlet ([164a091](https://github.com/angular/angular/commit/164a091)), closes [#9042](https://github.com/angular/angular/issues/9042)
|
||||
* **NgZone:** isStable ([587c119](https://github.com/angular/angular/commit/587c119)), closes [#8108](https://github.com/angular/angular/issues/8108)
|
||||
* **platform-browser-dynamic:** re-add a deprecated platform-browser-dynamic ([172a566](https://github.com/angular/angular/commit/172a566))
|
||||
* **platform-browser-dynamic:** fix public exports for web-worker related symbols ([6e62217](https://github.com/angular/angular/commit/6e62217))
|
||||
* **regex_url_paths:** add `regex_group_names` to handle consistency with serializers ([ce013a3](https://github.com/angular/angular/commit/ce013a3)), closes [#7554](https://github.com/angular/angular/issues/7554) [#7694](https://github.com/angular/angular/issues/7694)
|
||||
* **renderer:** add a `setElementStyles` method ([1ac38bd](https://github.com/angular/angular/commit/1ac38bd))
|
||||
* **router:** export RouterLink and RouterOutlet (#8912) ([1c92903](https://github.com/angular/angular/commit/1c92903))
|
||||
* **router:** update router to support lazy loading ([0f1465b](https://github.com/angular/angular/commit/0f1465b))
|
||||
* **SchemaRegistry:** add Node.textContent ([3b80ab5](https://github.com/angular/angular/commit/3b80ab5)), closes [#8413](https://github.com/angular/angular/issues/8413)
|
||||
* **security:** add an HTML sanitizer. ([f86edae](https://github.com/angular/angular/commit/f86edae))
|
||||
* **security:** add tests for style sanitisation. ([7b6c4d5](https://github.com/angular/angular/commit/7b6c4d5))
|
||||
* **security:** add tests for URL sanitization. ([7a524e3](https://github.com/angular/angular/commit/7a524e3))
|
||||
* **security:** allow data: URLs for images and videos. ([dd50124](https://github.com/angular/angular/commit/dd50124))
|
||||
* **security:** allow url(...) style values. ([15ae710](https://github.com/angular/angular/commit/15ae710)), closes [#8514](https://github.com/angular/angular/issues/8514)
|
||||
* **security:** Automatic XSRF handling. ([4d793c4](https://github.com/angular/angular/commit/4d793c4))
|
||||
* **security:** complete DOM security schema. ([040b101](https://github.com/angular/angular/commit/040b101))
|
||||
* **security:** document iframe src to be TRUSTED_URL. ([3463047](https://github.com/angular/angular/commit/3463047))
|
||||
* add minified bundles ([9175a04](https://github.com/angular/angular/commit/9175a04))
|
||||
* **security:** expose the safe value types. ([50c9bed](https://github.com/angular/angular/commit/50c9bed)), closes [#8568](https://github.com/angular/angular/issues/8568)
|
||||
* **security:** fill in missing security contexts. ([67ed2e2](https://github.com/angular/angular/commit/67ed2e2))
|
||||
* **security:** strip XSSI prefix from XHR responses. ([df1b1f6](https://github.com/angular/angular/commit/df1b1f6))
|
||||
* **security:** support transform CSS functions for sanitization. ([8b1b427](https://github.com/angular/angular/commit/8b1b427)), closes [#8514](https://github.com/angular/angular/issues/8514)
|
||||
* **security:** warn users when sanitizing in dev mode. ([3e68b7e](https://github.com/angular/angular/commit/3e68b7e)), closes [#8522](https://github.com/angular/angular/issues/8522)
|
||||
* **shadow_css:** add encapsulation support for CSS @supports at-rule ([cb84cbf](https://github.com/angular/angular/commit/cb84cbf)), closes [#7944](https://github.com/angular/angular/issues/7944)
|
||||
* **ViewEncapsulation:** default ViewEncapsulation to configurable ([f93512b](https://github.com/angular/angular/commit/f93512b)), closes [#7883](https://github.com/angular/angular/issues/7883)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
* Location#platformStrategy property was previously accidentally exported as public
|
||||
If any application requires access to the current location strategy, it should be accessed via DI instead
|
||||
by injecting the LocationStrategy token.
|
||||
The likelihood of anyone actually depending on this property is very low.
|
||||
|
||||
* DirectiveNormalizer takes new constructor arguments, `config:CompilerConfig`.
|
||||
|
||||
* `Parser` constructor required new parameter `config: CompilerConfig` as second argument.
|
||||
|
||||
* Bundles are now in the bundles/ subdirectory within each package
|
||||
|
||||
* HTML, style values, and URLs are now automatically sanitized. Values that do not match are escaped
|
||||
or ignored. When binding a URL or style property that would get ignored, bind to a value
|
||||
explicitly marked as safe instead by injection the DOM sanitization service:
|
||||
|
||||
```
|
||||
class MyComponent {
|
||||
constructor(sanitizer: DomSanitizationService) {
|
||||
// ONLY DO THIS FOR VALUES YOU KNOW TO BE SAFE! NEVER ALLOW USER DATA IN THIS!
|
||||
this.safeStyleValue = sanitizer.bypassSecurityTrustStyle('rotate(90deg)');
|
||||
// then bind to `safeStyleValue` in your template.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `PLATFORM_PIPES` and `PLATFORM_DIRECTIVES` now are fields on `CompilerConfig`.
|
||||
Instead of providing a binding to these tokens, provide a binding for `CompilerConfig` instead.
|
||||
|
||||
* `CompilerConfig` used to take positional arguments and now takes named arguments.
|
||||
|
||||
### Deprecation
|
||||
|
||||
* `Parse5DomAdapter` will no longer be exported from `@angular/platform-server` as a public API as of RC.3.
|
||||
A new function called `serverBootstrap()` will be provided, which will automatically set the correct
|
||||
`document` during platform initialization.
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert fix(compiler): support string tokens with `.` inside. ([cc86fee](https://github.com/angular/angular/commit/cc86fee))
|
||||
|
||||
<a name="2.0.0-rc.1"></a>
|
||||
# 2.0.0-rc.1 (2016-05-03)
|
||||
|
||||
### Known Issues
|
||||
|
||||
*** SECURITY WARNING ***
|
||||
Contextual escaping is not yet implemented in Angular 2. This will be fixed in the upcoming RC.
|
||||
In the meantime make sure to correctly escape all values that go into the DOM.
|
||||
*** SECURITY WARNING ***
|
||||
|
||||
- source maps for umd bundles are missing
|
||||
- `Ruler` service is not being reexported via `@angular/platform-browser`
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** calculate the right moduleUrl ([3a40cb1](https://github.com/angular/angular/commit/3a40cb1))
|
||||
* **compiler:** don’t emit metadata for generated files ([43e0fa5](https://github.com/angular/angular/commit/43e0fa5))
|
||||
* **compiler:** fix where pipes live ([dd6e0cf](https://github.com/angular/angular/commit/dd6e0cf)), closes [#8408](https://github.com/angular/angular/issues/8408)
|
||||
* **compiler:** use absolute paths for comparing module urls ([52a6ba7](https://github.com/angular/angular/commit/52a6ba7))
|
||||
* **compiler:** use rootDirs compilerOption to affect genDir layout. ([a033f83](https://github.com/angular/angular/commit/a033f83))
|
||||
* **docs:** upgrade deprecated ngFor-Syntax ([27a7b51](https://github.com/angular/angular/commit/27a7b51))
|
||||
* **router:** add support for ../ ([89704e0](https://github.com/angular/angular/commit/89704e0))
|
||||
* **testing:** Check for pending macrotasks in ComponentFixture.whenStable() and ComponentFixtu ([509f4ec](https://github.com/angular/angular/commit/509f4ec)), closes [#8389](https://github.com/angular/angular/issues/8389)
|
||||
* **router-deprecated:** inheriting from RouterOutlet works now
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** make RouterLink accept single values ([b625f24](https://github.com/angular/angular/commit/b625f24))
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-rc.0"></a>
|
||||
# 2.0.0-rc.0 (2016-05-02)
|
||||
|
||||
This is the first release candidate that contains repackaging of Angular into individual packages one per each feature area.
|
||||
|
||||
All of the packages are now distributed under the @angular npm scope. This changes how Angular is installed via npm and how you import the code.
|
||||
|
||||
To install Angular for a browser application please use:
|
||||
|
||||
```
|
||||
npm install --save @angular/core @angular/compiler @angular/common @angular/platform-browser @angular/platform-browser-dynamic rxjs@5.0.0-beta.6 zone.js@0.6.12
|
||||
```
|
||||
|
||||
To import various symbols please adjust the paths in the following way:
|
||||
|
||||
- `angular2/core` -> `@angular/core`
|
||||
- `angular2/compiler` -> `@angular/compiler`
|
||||
- `angular2/common` -> `@angular/common`
|
||||
- `angular2/platform/browser` -> `@angular/platform-browser` (applications with precompiled templates) + `@angular/platform-browser-dynamic` (applications that compile templates on the fly)
|
||||
- `angular2/platform/server` -> `@angular/platform-server`
|
||||
- `angular2/testing` -> `@angular/core/testing` (it/describe/..) + `@angular/compiler/testing` (TestComponentBuilder) + `@angular/platform-browser/testing`
|
||||
- `angular2/upgrade` -> `@angular/upgrade`
|
||||
- `angular2/http` -> `@angular/http`
|
||||
- `angular2/router` -> `@angular/router-deprecated` (snapshot of the component router from beta.17 for backwards compatibility)
|
||||
- new package: `@angular/router` - component router with several [breaking changes](https://docs.google.com/document/d/1WLSNV3V1AKdwLwRiLuN7JqbPBKQ_S5quRlcT5LPIldw/edit#heading=h.blfh5ya9sf5r)
|
||||
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** introduce template context ([cacdead](https://github.com/angular/angular/commit/cacdead)), closes [#8321](https://github.com/angular/angular/issues/8321)
|
||||
* **core:** support the decorator data that tsickle produces ([b6fd811](https://github.com/angular/angular/commit/b6fd811))
|
||||
* **di:** support map literals as providers ([46cd868](https://github.com/angular/angular/commit/46cd868))
|
||||
* **offline compiler:** a replacement for tsc that compiles templates ([78946fe](https://github.com/angular/angular/commit/78946fe))
|
||||
* **offline compiler:** add metadata emit ([072446a](https://github.com/angular/angular/commit/072446a))
|
||||
* **router:** add CanDeactivate ([deba804](https://github.com/angular/angular/commit/deba804))
|
||||
* **router:** add link that support only absolute urls ([fa5bfe4](https://github.com/angular/angular/commit/fa5bfe4))
|
||||
* **router:** add Router and RouterOutlet to support aux routes ([6e1fed4](https://github.com/angular/angular/commit/6e1fed4))
|
||||
* **router:** add RouterLink ([de56dd5](https://github.com/angular/angular/commit/de56dd5))
|
||||
* **router:** add RouterUrlSerializer ([79830f1](https://github.com/angular/angular/commit/79830f1))
|
||||
* **router:** add RouteTree and UrlTree as aliases to Tree<RouteSegment> and Tree<UrlSegment> ([277b1fc](https://github.com/angular/angular/commit/277b1fc))
|
||||
* **router:** add support for wildcards ([8836219](https://github.com/angular/angular/commit/8836219))
|
||||
* **router:** adds an example app using the new router ([602641d](https://github.com/angular/angular/commit/602641d))
|
||||
* **router:** change location when navigating ([560cc14](https://github.com/angular/angular/commit/560cc14))
|
||||
* **router:** implement relative navigation ([e5b87e5](https://github.com/angular/angular/commit/e5b87e5))
|
||||
* **router:** implements support for router-link-active ([ec4ca0e](https://github.com/angular/angular/commit/ec4ca0e))
|
||||
* **router:** listen to location changes ([62a0809](https://github.com/angular/angular/commit/62a0809)), closes [#8362](https://github.com/angular/angular/issues/8362)
|
||||
* **router:** set router-link-active when RouterLink is active ([4fe0f1f](https://github.com/angular/angular/commit/4fe0f1f)), closes [#8376](https://github.com/angular/angular/issues/8376)
|
||||
* **router:** update recognize to handle matrix parameters ([446657b](https://github.com/angular/angular/commit/446657b))
|
||||
* **router:** update recognize to support aux routes ([d35c109](https://github.com/angular/angular/commit/d35c109))
|
||||
* **router:** update url parser to handle aux routes ([fad3b64](https://github.com/angular/angular/commit/fad3b64))
|
||||
* **testing:** Use NgZone in TestComponentBuilder. ([769835e](https://github.com/angular/angular/commit/769835e)), closes [#8301](https://github.com/angular/angular/issues/8301)
|
||||
* **tests:** add ROUTER_FAKE_PROVIDERS to angular2/alt_router/router_testing_providers ([0f1b370](https://github.com/angular/angular/commit/0f1b370))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **metadata:** Preserve Provider expressions ([7c0d497](https://github.com/angular/angular/commit/7c0d497))
|
||||
* **codegen:** event handler has boolean return type ([ca40ef5](https://github.com/angular/angular/commit/ca40ef5))
|
||||
* **compiler:** fix cross view references and providers with `useValue`. ([f114d6c](https://github.com/angular/angular/commit/f114d6c)), closes [#8366](https://github.com/angular/angular/issues/8366)
|
||||
* **compiler:** project using the right directive as component. ([0f774df](https://github.com/angular/angular/commit/0f774df)), closes [#8344](https://github.com/angular/angular/issues/8344)
|
||||
* **compiler:** support css stylesheets in offline compiler ([00d3b60](https://github.com/angular/angular/commit/00d3b60))
|
||||
* **compiler:** support empty array and map literals. ([11955f9](https://github.com/angular/angular/commit/11955f9)), closes [#8336](https://github.com/angular/angular/issues/8336)
|
||||
* **compiler_cli:** make sure the generated code gets compiled via tic ([163d80a](https://github.com/angular/angular/commit/163d80a))
|
||||
* **core:** check components if an event handler inside of an embedded view fires. ([4d691b6](https://github.com/angular/angular/commit/4d691b6)), closes [#8242](https://github.com/angular/angular/issues/8242)
|
||||
* **core:** return the ChangeDetectorRef of the component also for embedded views. ([351f24e](https://github.com/angular/angular/commit/351f24e))
|
||||
* **metadata:** expose Providers in metadata ([8bf6ef6](https://github.com/angular/angular/commit/8bf6ef6))
|
||||
* **perf:** don’t use `try/catch` in production mode ([b1a9e44](https://github.com/angular/angular/commit/b1a9e44)), closes [#8338](https://github.com/angular/angular/issues/8338)
|
||||
* **router:** canDeactivate should not change the url when returns false ([76d6f5f](https://github.com/angular/angular/commit/76d6f5f)), closes [#8360](https://github.com/angular/angular/issues/8360)
|
||||
* **router:** create a route tree when creating the router service ([ca13f1c](https://github.com/angular/angular/commit/ca13f1c)), closes [#8365](https://github.com/angular/angular/issues/8365)
|
||||
* **typescript:** strip abstract keyword from properties in .d.ts ([a84c2d7](https://github.com/angular/angular/commit/a84c2d7)), closes [#8339](https://github.com/angular/angular/issues/8339)
|
||||
|
||||
|
||||
|
||||
### OTHER BREAKING CHANGES
|
||||
|
||||
|
||||
* - ViewRef.changeDetectorRef was removed as using ChangeDetectorRefs
|
||||
for EmbeddedViewRefs does not make sense. Use ComponentRef.changeDetectorRef
|
||||
or inject ChangeDetectorRef instead.
|
||||
|
||||
* - Before, a `EmbeddedViewRef` used to have methods for
|
||||
setting variables. Now, a user has to pass in a context
|
||||
object that represents all variables when an `EmbeddedViewRef`
|
||||
should be created.
|
||||
- `ViewContainerRef.createEmbeddedViewRef` now takes
|
||||
a context object as 2nd argument.
|
||||
- `EmbeddedViewRef.setLocal` and `getLocal` have been removed.
|
||||
Use `EmbeddedViewRef.context` to access the context.
|
||||
- `DebugNode.locals` has been removed. Use the new methods `DebugElement.references`
|
||||
to get the references that are present on this element,
|
||||
or `DebugElement.context` to get the context of the `EmbeddedViewRef` or the component to which the element belongs.
|
||||
* - Depending on if you are using precompiled templates or you are compiling templates on the fly, the setup for the base test providers has changed:
|
||||
|
||||
Before:
|
||||
```js
|
||||
// Somewhere in test setup
|
||||
import {setBaseTestProviders} from 'angular2/testing';
|
||||
import {
|
||||
TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_APPLICATION_PROVIDERS
|
||||
} from 'angular2/platform/testing/browser';
|
||||
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_APPLICATION_PROVIDERS);
|
||||
```
|
||||
After (applications that compile templates on the fly):
|
||||
```js
|
||||
// Somewhere in the test setup
|
||||
import {setBaseTestProviders} from '@angular/core/testing';
|
||||
import {
|
||||
TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
setBaseTestProviders(TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
|
||||
```
|
||||
|
||||
After (applications with precompiled templates):
|
||||
```js
|
||||
// Somewhere in the test setup
|
||||
import {setBaseTestProviders} from '@angular/core/testing';
|
||||
import {
|
||||
TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_STATIC_APPLICATION_PROVIDERS
|
||||
} from '@angular/platform-browser/testing';
|
||||
setBaseTestProviders(TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_STATIC_APPLICATION_PROVIDERS);
|
||||
```
|
||||
|
||||
|
||||
<a name="2.0.0-beta.17"></a>
|
||||
# 2.0.0-beta.17 (2016-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **changelog:** fix changelog script. ([c209836](https://github.com/angular/angular/commit/c209836))
|
||||
* **compiler:** Allow templates to access variables that are declared afterwards. ([1e8864c](https://github.com/angular/angular/commit/1e8864c)), closes [#8261](https://github.com/angular/angular/issues/8261)
|
||||
* **core:** properly evaluate expressions with conditional and boolean operators ([1ad2a02](https://github.com/angular/angular/commit/1ad2a02)), closes [#8235](https://github.com/angular/angular/issues/8235) [#8244](https://github.com/angular/angular/issues/8244) [#8282](https://github.com/angular/angular/issues/8282)
|
||||
* **metadata:** Do not attach module names to metadata. ([d964888](https://github.com/angular/angular/commit/d964888)), closes [#8225](https://github.com/angular/angular/issues/8225) [#8082](https://github.com/angular/angular/issues/8082) [#8256](https://github.com/angular/angular/issues/8256)
|
||||
* **testing:** allow test component builder to override directives from lists ([ff2ae7a](https://github.com/angular/angular/commit/ff2ae7a)), closes [#7397](https://github.com/angular/angular/issues/7397) [#8217](https://github.com/angular/angular/issues/8217)
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** ElementSchema now has explicit DOM schema information ([d327ac4](https://github.com/angular/angular/commit/d327ac4)), closes [#8179](https://github.com/angular/angular/issues/8179)
|
||||
* **core:** separate refs from vars. ([d2efac1](https://github.com/angular/angular/commit/d2efac1)), closes [#7158](https://github.com/angular/angular/issues/7158) [#8264](https://github.com/angular/angular/issues/8264)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
The reference `#...` now always means `ref-`.
|
||||
|
||||
**Before:**
|
||||
- Outside of `ngFor`, a `#...` meant a reference.
|
||||
- Inside of `ngFor`, it meant a local variable.
|
||||
|
||||
This pattern was confusing.
|
||||
|
||||
**After:**
|
||||
|
||||
- `<template #abc>` now defines a reference to a TemplateRef, instead of an input variable used inside of the template.
|
||||
- Inside of structural directives that declare local variables, such as `*ngFor`, usage of `#...` is deprecated. Use `let` instead.
|
||||
- `<div *ngFor="#item of items">` now becomes `<div *ngFor="let item of items">`
|
||||
- `var-...` is deprecated.
|
||||
- use `#` or a `ref-` outside of `*ngFor`
|
||||
- for `ngFor`, use the syntax: `<template ngFor let-... [ngForOf]="...">`
|
||||
|
||||
|
||||
<a name="2.0.0-beta.16"></a>
|
||||
# 2.0.0-beta.16 (2016-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular_1_router:** Removed arrow function from module template ([d094a85](https://github.com/angular/angular/commit/d094a85)), closes [#8076](https://github.com/angular/angular/issues/8076)
|
||||
* **build:** ignore Dart warnings for external code. ([4140405](https://github.com/angular/angular/commit/4140405))
|
||||
* **codegen:** add explicit any to class fields ([c8d00dc](https://github.com/angular/angular/commit/c8d00dc)), closes [#8204](https://github.com/angular/angular/issues/8204) [#8205](https://github.com/angular/angular/issues/8205)
|
||||
* **compiler:** only call pure pipes if their input changed. ([8db6215](https://github.com/angular/angular/commit/8db6215))
|
||||
* **compiler:** properly implement pure pipes and change pipe syntax ([152a117](https://github.com/angular/angular/commit/152a117))
|
||||
* **compiler:** support string tokens with `.` inside. ([cc86fee](https://github.com/angular/angular/commit/cc86fee))
|
||||
* **compiler:** use DI order for change detection order. ([67d05eb](https://github.com/angular/angular/commit/67d05eb)), closes [#8198](https://github.com/angular/angular/issues/8198)
|
||||
* **core:** various minor compiler fixes ([2f70457](https://github.com/angular/angular/commit/2f70457)), closes [#8162](https://github.com/angular/angular/issues/8162)
|
||||
* **forms:** ensure select model updates in firefox and ie ([c3daccd](https://github.com/angular/angular/commit/c3daccd)), closes [#6573](https://github.com/angular/angular/issues/6573) [#8148](https://github.com/angular/angular/issues/8148)
|
||||
* **forms:** improve error message when ngFormModel is missing a form ([12837e1](https://github.com/angular/angular/commit/12837e1)), closes [#8136](https://github.com/angular/angular/issues/8136) [#8143](https://github.com/angular/angular/issues/8143)
|
||||
* **forms:** number input should report null when blank ([e69cb40](https://github.com/angular/angular/commit/e69cb40)), closes [#6932](https://github.com/angular/angular/issues/6932) [#8141](https://github.com/angular/angular/issues/8141)
|
||||
* **metadata:** emit metadata rooted at 'angular2' ([9889c21](https://github.com/angular/angular/commit/9889c21)), closes [#8144](https://github.com/angular/angular/issues/8144) [#8147](https://github.com/angular/angular/issues/8147)
|
||||
* **release:** Fix the package.json zone.js requirement to 0.6.12 ([6103aa0](https://github.com/angular/angular/commit/6103aa0))
|
||||
* **tests:** remove payload size check ([22c05b0](https://github.com/angular/angular/commit/22c05b0))
|
||||
* **transformers:** support `query.read` ([386cc5d](https://github.com/angular/angular/commit/386cc5d)), closes [#8172](https://github.com/angular/angular/issues/8172)
|
||||
* **upgrade:** clean up scope when element is destroyed ([0fc9ec2](https://github.com/angular/angular/commit/0fc9ec2)), closes [#8102](https://github.com/angular/angular/issues/8102)
|
||||
|
||||
### Features
|
||||
|
||||
* **codegen:** produce `.ngfactory.dart/ts` files instead of `.template.dart/ts` files. ([c06b0a2](https://github.com/angular/angular/commit/c06b0a2))
|
||||
* **core:** add `Query.read` and remove `DynamicComponentLoader.loadIntoLocation`. ([efbd446](https://github.com/angular/angular/commit/efbd446))
|
||||
* **core:** introduce ComponentFactory. ([0c600cf](https://github.com/angular/angular/commit/0c600cf))
|
||||
* **core:** separate reflective injector from Injector interface ([0a7d10b](https://github.com/angular/angular/commit/0a7d10b))
|
||||
* **core:** support importUri in StaticReflector ([3e11422](https://github.com/angular/angular/commit/3e11422)), closes [#8195](https://github.com/angular/angular/issues/8195)
|
||||
* **core:** support non reflective bootstrap. ([9092ac7](https://github.com/angular/angular/commit/9092ac7))
|
||||
* **html_lexer:** support special forms used by i18n { exp, plural, =0 {} } ([7f29766](https://github.com/angular/angular/commit/7f29766))
|
||||
* **html_parser:** support special forms used by i18n { exp, plural, =0 {} } ([7c9717b](https://github.com/angular/angular/commit/7c9717b))
|
||||
* **i18n:** add custom placeholder names ([bb9fb21](https://github.com/angular/angular/commit/bb9fb21)), closes [#7799](https://github.com/angular/angular/issues/7799) [#8057](https://github.com/angular/angular/issues/8057)
|
||||
* **i18n:** add support for nested expansion forms ([c6244d1](https://github.com/angular/angular/commit/c6244d1)), closes [#7977](https://github.com/angular/angular/issues/7977)
|
||||
* **i18n:** support plural and gender special forms ([88b0a23](https://github.com/angular/angular/commit/88b0a23))
|
||||
* **Location:** out of router and into platform/common ([b602bd8](https://github.com/angular/angular/commit/b602bd8)), closes [#7962](https://github.com/angular/angular/issues/7962)
|
||||
* **NgTemplateOutlet:** add NgTemplateOutlet directive ([f4e6994](https://github.com/angular/angular/commit/f4e6994)), closes [#7615](https://github.com/angular/angular/issues/7615) [#8021](https://github.com/angular/angular/issues/8021)
|
||||
* **router:** add Router and RouterOutlet ([5a897cf](https://github.com/angular/angular/commit/5a897cf)), closes [#8173](https://github.com/angular/angular/issues/8173)
|
||||
* **router:** add router metadata ([ef67a0c](https://github.com/angular/angular/commit/ef67a0c))
|
||||
* **router:** add UrlSegment, RouteSegment, and Tree ([90a1f7d](https://github.com/angular/angular/commit/90a1f7d))
|
||||
* **router:** implement recognizer ([ef6163e](https://github.com/angular/angular/commit/ef6163e))
|
||||
* **router:** implement RouterUrlParser ([f698567](https://github.com/angular/angular/commit/f698567))
|
||||
* **test:** Implement fakeAsync using the FakeAsyncTestZoneSpec from zone.js. ([bab81a9](https://github.com/angular/angular/commit/bab81a9)), closes [#8142](https://github.com/angular/angular/issues/8142)
|
||||
* **tests:** manage asynchronous tests using zones ([8490921](https://github.com/angular/angular/commit/8490921)), closes [#7735](https://github.com/angular/angular/issues/7735)
|
||||
* **view_compiler:** codegen DI and Queries ([2b34c88](https://github.com/angular/angular/commit/2b34c88)), closes [#6301](https://github.com/angular/angular/issues/6301) [#6567](https://github.com/angular/angular/issues/6567)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* - pipes now take a variable number of arguments, and not an array that contains all arguments.
|
||||
|
||||
* inject can no longer wrap fakeAsync while fakeAsync can wrap inject. So the order in existing tests with inject and fakeAsync has to be switched as follows:
|
||||
Before:
|
||||
```
|
||||
inject([...], fakeAsync((...) => {...}))
|
||||
```
|
||||
After:
|
||||
```
|
||||
fakeAsync(inject([...], (...) => {...}))
|
||||
```
|
||||
You will also need to add the dependency
|
||||
`'node_modules/zone.js/dist/fake-async-test.js'`
|
||||
as a served file in your Karma or other test configuration.
|
||||
|
||||
* - Injector was renamed into `ReflectiveInjector`,
|
||||
as `Injector` is only an abstract class with one method on it
|
||||
- `Injector.getOptional()` was changed into `Injector.get(token, notFoundValue)`
|
||||
to make implementing injectors simpler
|
||||
- `ViewContainerRef.createComponent` now takes an `Injector`
|
||||
instead of `ResolvedProviders`. If a reflective injector
|
||||
should be used, create one before calling this method.
|
||||
(e.g. via `ReflectiveInjector.resolveAndCreate(…)`.
|
||||
|
||||
* - `DynamicComponentLoader.loadIntoLocation` has been removed. Use `@ViewChild(‘myVar’, {read: ViewContainerRef})` to get hold of a `ViewContainerRef` at an element with variable `myVar`. Then call `DynamicComponentLoader.loadNextToLocation`.
|
||||
- `DynamicComponentLoader.loadNextToLocation` now takes a `ViewContainerRef` instead of an `ElementRef`.
|
||||
- `AppViewManager` is renamed into `ViewUtils` and is a mere private utility service.
|
||||
|
||||
* - `Compiler` is renamed to `ComponentResolver`,
|
||||
`Compiler.compileInHost` has been renamed to `ComponentResolver.resolveComponent`.
|
||||
- `ComponentRef.dispose` is renamed to `ComponentRef.destroy`
|
||||
- `ViewContainerRef.createHostView` is renamed to `ViewContainerRef.createComponent`
|
||||
- `ComponentFixture_` has been removed, the class `ComponentFixture`
|
||||
can now be created directly as it is no more using private APIs.
|
||||
|
||||
* `Location` and other related providers have been moved out of `router` and into `platform/common`. `BrowserPlatformLocation` is not meant to be used directly however advanced configurations may use it via the following import change.
|
||||
Before:
|
||||
```
|
||||
import {
|
||||
PlatformLocation,
|
||||
Location,
|
||||
LocationStrategy,
|
||||
HashLocationStrategy,
|
||||
PathLocationStrategy,
|
||||
APP_BASE_HREF}
|
||||
from 'angular2/router';
|
||||
import {BrowserPlatformLocation} from 'angular2/src/router/location/browser_platform_location';
|
||||
```
|
||||
After:
|
||||
```
|
||||
import {
|
||||
PlatformLocation,
|
||||
Location,
|
||||
LocationStrategy,
|
||||
HashLocationStrategy,
|
||||
PathLocationStrategy,
|
||||
APP_BASE_HREF}
|
||||
from 'angular2/platform/common';
|
||||
import {BrowserPlatformLocation} from 'angular2/src/platform/browser/location/browser_platform_location';
|
||||
```
|
||||
|
||||
* `injectAsync` is now deprecated. Instead, use the `async` function
|
||||
to wrap any asynchronous tests.
|
||||
|
||||
You will also need to add the dependency
|
||||
`'node_modules/zone.js/dist/async-test.js'`
|
||||
as a served file in your Karma or other test configuration.
|
||||
|
||||
Before:
|
||||
```
|
||||
it('should wait for returned promises', injectAsync([FancyService], (service) => {
|
||||
return service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
|
||||
}));
|
||||
it('should wait for returned promises', injectAsync([], () => {
|
||||
return somePromise.then(() => { expect(true).toEqual(true); });
|
||||
}));
|
||||
```
|
||||
After:
|
||||
```
|
||||
it('should wait for returned promises', async(inject([FancyService], (service) => {
|
||||
service.getAsyncValue().then((value) => { expect(value).toEqual('async value'); });
|
||||
})));
|
||||
// Note that if there is no injection, we no longer need `inject` OR `injectAsync`.
|
||||
it('should wait for returned promises', async(() => {
|
||||
somePromise.then(() => { expect(true).toEqual(true); });
|
||||
}));
|
||||
```
|
||||
|
||||
* - Renderer:
|
||||
* renderComponent method is removed form `Renderer`, only present on `RootRenderer`
|
||||
* Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor
|
||||
now take the DebugInfo directly.
|
||||
- Query semantics:
|
||||
* Queries don't work with dynamically loaded components.
|
||||
* e.g. for router-outlet: loaded components can't be queries via @ViewQuery,
|
||||
but router-outlet emits an event `activate` now that emits the activated component
|
||||
- Exception classes and the context inside changed (renamed fields)
|
||||
- DebugElement.attributes is an Object and not a Map in JS any more
|
||||
- ChangeDetectorGenConfig was renamed into CompilerConfig
|
||||
- AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer
|
||||
are removed, use the methods in ViewContainerRef instead
|
||||
- Change detection order changed:
|
||||
* 1. dirty check component inputs
|
||||
* 2. dirty check content children
|
||||
* 3. update render nodes
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.15"></a>
|
||||
# 2.0.0-beta.15 (2016-04-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **7837:** MetadataCollector takes no parameters for the constructor. ([c17dc1c](https://github.com/angular/angular/commit/c17dc1c)), closes [#7838](https://github.com/angular/angular/issues/7838)
|
||||
* **7987:** Incremental build works with new trees ([08b2956](https://github.com/angular/angular/commit/08b2956)), closes [#7989](https://github.com/angular/angular/issues/7989)
|
||||
* **build:** ignore dart warnings `The name … is shown, but not used` ([01e6b8c](https://github.com/angular/angular/commit/01e6b8c)), closes [#8045](https://github.com/angular/angular/issues/8045)
|
||||
* **payload:** increase payload size limit temporarily ([28e657d](https://github.com/angular/angular/commit/28e657d))
|
||||
* **RouterLink:** ignore optional parameters when checking for active routes ([5e2bc5c](https://github.com/angular/angular/commit/5e2bc5c)), closes [#6459](https://github.com/angular/angular/issues/6459) [#7834](https://github.com/angular/angular/issues/7834)
|
||||
* **select:** set value individually from ngModel ([e1e44a9](https://github.com/angular/angular/commit/e1e44a9)), closes [#7975](https://github.com/angular/angular/issues/7975) [#7978](https://github.com/angular/angular/issues/7978)
|
||||
* **upgrade:** make upgradeAdapter upgrade angular 1 components correctly ([247964a](https://github.com/angular/angular/commit/247964a)), closes [#7951](https://github.com/angular/angular/issues/7951)
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** Add an implementation for XHR that uses a template cache to load template files. ([a596b88](https://github.com/angular/angular/commit/a596b88)), closes [#7940](https://github.com/angular/angular/issues/7940)
|
||||
* **gestures:** allow override of Hammer default configuration ([6cbf990](https://github.com/angular/angular/commit/6cbf990)), closes [#7924](https://github.com/angular/angular/issues/7924)
|
||||
* **ngFor:** Support convenience view local in ngFor ([ccff175](https://github.com/angular/angular/commit/ccff175)), closes [#8013](https://github.com/angular/angular/issues/8013)
|
||||
* **parser:** TemplateParser.tryParse() returns both the AST and errors ([226e662](https://github.com/angular/angular/commit/226e662)), closes [#7858](https://github.com/angular/angular/issues/7858)
|
||||
* **transformers:** changes transformers to collect information about providers and resolve identifi ([3b60503](https://github.com/angular/angular/commit/3b60503))
|
||||
* **transformers:** special case Profiler ([83b8f59](https://github.com/angular/angular/commit/83b8f59))
|
||||
* **typescript:** update to 1.9 nightly. ([3412aba](https://github.com/angular/angular/commit/3412aba)), closes [#8003](https://github.com/angular/angular/issues/8003)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* In Dart files, `import 'package:angular2/bootstrap.dart'` no longer works.
|
||||
Instead, use `import 'package:angular2/platform/browser.dart'`.
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "chore(format): update to latest formatter" ([60727c4](https://github.com/angular/angular/commit/60727c4))
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.14"></a>
|
||||
# 2.0.0-beta.14 (2016-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **forms:** support both value and ng-value ([8db97b0](https://github.com/angular/angular/commit/8db97b0))
|
||||
* **router:** allow forward slashes in query parameters ([4902244](https://github.com/angular/angular/commit/4902244)), closes [#7824](https://github.com/angular/angular/issues/7824)
|
||||
* **select:** support objects as select values ([74e2bd7](https://github.com/angular/angular/commit/74e2bd7)), closes [#4843](https://github.com/angular/angular/issues/4843) [#7842](https://github.com/angular/angular/issues/7842)
|
||||
* **select:** update name from ng-value to ngValue ([3ca6df8](https://github.com/angular/angular/commit/3ca6df8)), closes [#7939](https://github.com/angular/angular/issues/7939)
|
||||
* **upgrade:** leak when angular1 destroys element ([9be04f8](https://github.com/angular/angular/commit/9be04f8)), closes [#6401](https://github.com/angular/angular/issues/6401) [#7935](https://github.com/angular/angular/issues/7935)
|
||||
|
||||
### Features
|
||||
|
||||
* **dart/transform:** Avoid `print` in transformer code. ([e310bee](https://github.com/angular/angular/commit/e310bee)), closes [#7855](https://github.com/angular/angular/issues/7855)
|
||||
* **static-reflector:** Added StaticReflector ([0dbf959](https://github.com/angular/angular/commit/0dbf959))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.13"></a>
|
||||
# 2.0.0-beta.13 (2016-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **build:** MetadataCollector correctly collects property metadata ([111afcd](https://github.com/angular/angular/commit/111afcd)), closes [#7772](https://github.com/angular/angular/issues/7772) [#7773](https://github.com/angular/angular/issues/7773)
|
||||
* **codegen:** stringify using an opaque ID when toString contains parens. ([90c87fa](https://github.com/angular/angular/commit/90c87fa)), closes [#7825](https://github.com/angular/angular/issues/7825)
|
||||
* **ngFor:** give more instructive error when binding to non-iterable ([49527ab](https://github.com/angular/angular/commit/49527ab))
|
||||
* **Router:** handling of special chars in dynamic segments ([0bcfcde](https://github.com/angular/angular/commit/0bcfcde)), closes [#7804](https://github.com/angular/angular/issues/7804)
|
||||
* **upgrade:** make ngUpgrade work with testability API ([430f367](https://github.com/angular/angular/commit/430f367)), closes [#7603](https://github.com/angular/angular/issues/7603)
|
||||
|
||||
### Features
|
||||
|
||||
* **build:** Persisting decorator metadata ([ae876d1](https://github.com/angular/angular/commit/ae876d1))
|
||||
* **compiler:** assert that Component.style is an array ([6de68e2](https://github.com/angular/angular/commit/6de68e2)), closes [#7559](https://github.com/angular/angular/issues/7559)
|
||||
* **compiler:** Resolvers now use DI to create reflector ([506f4ce](https://github.com/angular/angular/commit/506f4ce)), closes [#7762](https://github.com/angular/angular/issues/7762)
|
||||
* **Compiler:** Allow overriding the projection selector ([aa966f5](https://github.com/angular/angular/commit/aa966f5)), closes [#6303](https://github.com/angular/angular/issues/6303) [#7742](https://github.com/angular/angular/issues/7742)
|
||||
* **dart:** Add a dev-mode check for undeclared lifecycle interfaces ([1c20a62](https://github.com/angular/angular/commit/1c20a62)), closes [#6849](https://github.com/angular/angular/issues/6849)
|
||||
* **facade:** add ListWrapper.flatten ([a1880c3](https://github.com/angular/angular/commit/a1880c3))
|
||||
* **facade:** add RegExpWrapper.replaceAll to replace all matches using the provided function ([91999e0](https://github.com/angular/angular/commit/91999e0))
|
||||
* **html_parser:** change HtmlElementAst to store both the start and the end positions ([17c8ec8](https://github.com/angular/angular/commit/17c8ec8))
|
||||
* **i18n:** implement an i18n-aware html parser ([d272f96](https://github.com/angular/angular/commit/d272f96)), closes [#7738](https://github.com/angular/angular/issues/7738)
|
||||
* **i18n:** implement xmb deserialization ([d7e1175](https://github.com/angular/angular/commit/d7e1175))
|
||||
* **i18n:** reexport I18nHtmlParser through the i18n barrel ([d2ca7d8](https://github.com/angular/angular/commit/d2ca7d8))
|
||||
* **i18n:** update I18nHtmlParser to accept parsed messages ([756121a](https://github.com/angular/angular/commit/756121a))
|
||||
* **i18n:** update transformers to read a xmb file when provided and use I18nHtmlParser in t ([8430927](https://github.com/angular/angular/commit/8430927)), closes [#7790](https://github.com/angular/angular/issues/7790)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* For static content projection, elements with *-directives are now matched against the element itself vs the template before.
|
||||
|
||||
`<p *ngIf="condition" foo></p>`
|
||||
|
||||
Before:
|
||||
```html
|
||||
// Use the implicit template for projection
|
||||
<ng-content select="template"></ng-content>
|
||||
```
|
||||
|
||||
After:
|
||||
```html
|
||||
// Use the actual element for projection
|
||||
<ng-content select="p[foo]"></ng-content>
|
||||
```
|
||||
|
||||
<a name="2.0.0-beta.12"></a>
|
||||
# 2.0.0-beta.12 (2016-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular_1_router:** ng-link is generating wrong hrefs ([69c1405](https://github.com/angular/angular/commit/69c1405)), closes [#7423](https://github.com/angular/angular/issues/7423)
|
||||
* **angular1_router:** support link generation with custom hashPrefixes ([0f8efce](https://github.com/angular/angular/commit/0f8efce))
|
||||
* **package.json:** remove es6-promise from the peerDependency list ([8b67b07](https://github.com/angular/angular/commit/8b67b07))
|
||||
|
||||
### Features
|
||||
|
||||
* **dart/transform:** Use angular2/platform/browser as bootstrap lib ([b6507e3](https://github.com/angular/angular/commit/b6507e3)), closes [#7647](https://github.com/angular/angular/issues/7647)
|
||||
|
||||
|
||||
<a name="2.0.0-beta.11"></a>
|
||||
# 2.0.0-beta.11 (2016-03-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* make sure that Zone does not show up in angular2.d.ts ([d4e9b55](https://github.com/angular/angular/commit/d4e9b55fb69d87f948d02905d34fc78221adb11a))
|
||||
* **common:** remove @internal annotation on SwitchView ([967ae3e](https://github.com/angular/angular/commit/967ae3e)), closes [#7657](https://github.com/angular/angular/issues/7657)
|
||||
* **router:** RouterOutlet loads component twice in a race condition ([2f581ff](https://github.com/angular/angular/commit/2f581ff)), closes [#7497](https://github.com/angular/angular/issues/7497) [#7545](https://github.com/angular/angular/issues/7545)
|
||||
|
||||
### Features
|
||||
|
||||
* **i18n:** add a simple dart script extracting all i18n messages from a package ([8326ab3](https://github.com/angular/angular/commit/8326ab3)), closes [#7620](https://github.com/angular/angular/issues/7620)
|
||||
* **i18n:** create i18n barrel ([a7fe983](https://github.com/angular/angular/commit/a7fe983))
|
||||
* **i18n:** implement xmb serializer ([e1f8e54](https://github.com/angular/angular/commit/e1f8e54))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
`@View()` annotation (previously deprecated) has been removed. Apps should use the `@Component()` decorator instead.
|
||||
|
||||
|
||||
<a name="2.0.0-beta.10"></a>
|
||||
# 2.0.0-beta.10 (2016-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **change_detection:** fix a memory leak ([128acbb](https://github.com/angular/angular/commit/128acbb))
|
||||
* **closure:** don't throw from top-level ([5824866](https://github.com/angular/angular/commit/5824866))
|
||||
* **router:** handle URL that does not match a route ([8e3e450](https://github.com/angular/angular/commit/8e3e450)), closes [#7349](https://github.com/angular/angular/issues/7349) [#7203](https://github.com/angular/angular/issues/7203)
|
||||
* **router/instruction:** ensure toLinkUrl includes extra params ([0d58b13](https://github.com/angular/angular/commit/0d58b13)), closes [#7367](https://github.com/angular/angular/issues/7367)
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** change html parser to preserve comments ([70d18b5](https://github.com/angular/angular/commit/70d18b5))
|
||||
* **core:** introduce a CSS lexer/parser ([b72bab4](https://github.com/angular/angular/commit/b72bab4))
|
||||
* **core:** introduce a CSS lexer/parser ([293fa55](https://github.com/angular/angular/commit/293fa55))
|
||||
* **facade:** add .values to StringMapWrapper ([f1796d6](https://github.com/angular/angular/commit/f1796d6))
|
||||
* **i18n:** add ngPlural directive ([df1f78e](https://github.com/angular/angular/commit/df1f78e))
|
||||
* **i18n:** implement a simple version of message extractor ([095db67](https://github.com/angular/angular/commit/095db67)), closes [#7454](https://github.com/angular/angular/issues/7454)
|
||||
* **shadow_css:** support `/deep/` and `>>>` ([cb38d72](https://github.com/angular/angular/commit/cb38d72)), closes [#7562](https://github.com/angular/angular/issues/7562) [#7563](https://github.com/angular/angular/issues/7563)
|
||||
* **TAG_DEFINITIONS:** include <meta> and <base> ([2c7c3e3](https://github.com/angular/angular/commit/2c7c3e3)), closes [#7455](https://github.com/angular/angular/issues/7455)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
Removed deprecated API from NgZone
|
||||
- `NgZone.overrideOnTurnStart`
|
||||
- `NgZone.overrideOnTurnDone`
|
||||
- `NgZone.overrideOnEventDone`
|
||||
- `NgZone.overrideOnErrorHandler`
|
||||
|
||||
Rename NgZone API
|
||||
- `NgZone.onTurnStart` => `NgZone.onUnstable`
|
||||
- `NgZone.onTurnDone` => `NgZone.onMicrotaskEmpty`
|
||||
- `NgZone.onEventDone` => `NgZone.onStable`
|
||||
|
||||
|
||||
<a name="2.0.0-beta.9"></a>
|
||||
# 2.0.0-beta.9 (2016-03-09)
|
||||
|
||||
@ -771,6 +39,18 @@ is:
|
||||
}
|
||||
```
|
||||
|
||||
* The recently added binding of the current router to the current component
|
||||
has been renamed from `router` to `$router`.
|
||||
So now the recommended set up for your bindings in your routed component
|
||||
is:
|
||||
```js
|
||||
{
|
||||
...
|
||||
bindings: {
|
||||
$router: '<'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -1088,7 +368,7 @@ setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
`platform_pipes`.
|
||||
* `Compiler.compileInHost` now returns a `HostViewFactoryRef`
|
||||
* Component view is not yet created when component constructor is called.
|
||||
-> use `ngOnInit` lifecycle callback to access the view of a component
|
||||
-> use `onInit` lifecycle callback to access the view of a component
|
||||
* `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef`
|
||||
* `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access
|
||||
the root nodes of an embedded view
|
||||
@ -1366,7 +646,7 @@ Use `angular-polyfills.js` instead.
|
||||
}
|
||||
```
|
||||
|
||||
or [check angular2's package.json](https://github.com/angular/angular/blob/master/package.json) for the latest `dependencies`
|
||||
or [check angular2's package.json](https://github.com/angular/angular/blob/master/package.json#L34) for the latest peer dependencies
|
||||
|
||||
|
||||
<a name="2.0.0-alpha.48"></a>
|
||||
|
@ -166,19 +166,6 @@ The **header** is mandatory and the **scope** of the header is optional.
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
|
||||
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||
|
||||
```
|
||||
docs(changelog): update change log to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
|
||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||
```
|
||||
|
||||
### Revert
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
@ -238,8 +225,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[github]: https://github.com/angular/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[runnable]: http://runnable.com
|
||||
[runnable]: http://runnable.com/
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
||||
|
38
DEVELOPER.md
38
DEVELOPER.md
@ -32,17 +32,17 @@ following products on your development machine:
|
||||
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* *Optional*: [Dart](https://www.dartlang.org) (version `>=1.13.2 <2.0.0`), specifically the Dart SDK and
|
||||
* *Optional*: [Dart](https://www.dartlang.org) (version ` >=1.13.2 <2.0.0`), specifically the Dart-SDK and
|
||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||
the Dart VM). Visit Dart's [Downloads page](https://www.dartlang.org/downloads) page for
|
||||
instructions. You can also download both **stable** and **dev** channel versions from the
|
||||
[download archive](https://www.dartlang.org/downloads/archive/). In that case, on Windows, Dart
|
||||
must be added to the `PATH` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN`
|
||||
environment variable must be created, pointing to the executable (e.g.
|
||||
`path-to-dartium-folder\chrome.exe`).
|
||||
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html).
|
||||
You can also download both **stable** and **dev** channel versions from the [download
|
||||
archive](https://www.dartlang.org/tools/download-archive). In that case, on Windows, Dart must be added
|
||||
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
|
||||
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
|
||||
|
||||
|
||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||
to execute the selenium standalone server for e2e testing.
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
@ -183,8 +183,8 @@ Karma is run against the new output.
|
||||
much easier to debug. `xit` and `xdescribe` can also be useful to exclude a test and a group of
|
||||
tests respectively.
|
||||
|
||||
**Note**: **watch mode** needs symlinks to work, so if you're using Windows, ensure you have the
|
||||
rights to built them in your operating system. On Windows, only administrators can create symbolic links by default, but you may change the policy. (see [here](https://technet.microsoft.com/library/cc766301.aspx?f=255&MSPPError=-2147217396).)
|
||||
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
|
||||
rights to built them in your operating system.
|
||||
|
||||
### Unit tests with Sauce Labs or Browser Stack
|
||||
|
||||
@ -238,17 +238,17 @@ repository, allowing many tools and editors to share our settings.
|
||||
|
||||
To check the formatting of your code, run
|
||||
|
||||
gulp lint
|
||||
gulp check-format
|
||||
|
||||
Note that the continuous build on CircleCI will fail the build if files aren't formatted according
|
||||
to the style guide.
|
||||
Note that the continuous build on Travis runs `gulp enforce-format`. Unlike the `check-format` task,
|
||||
this will actually fail the build if files aren't formatted according to the style guide.
|
||||
|
||||
Your life will be easier if you include the formatter in your standard workflow. Otherwise, you'll
|
||||
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
|
||||
to some whitespace difference.
|
||||
|
||||
* Use `gulp format` to format everything.
|
||||
* Use `gulp lint` to check if your code is `clang-format` clean. This also gives
|
||||
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple).
|
||||
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
|
||||
you a command line to format your code.
|
||||
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
|
||||
touched.
|
||||
@ -256,14 +256,10 @@ to some whitespace difference.
|
||||
commit a change. In the angular repo, run
|
||||
|
||||
```
|
||||
$ echo -e '#!/bin/sh\nexec git clang-format --style file' > .git/hooks/pre-commit
|
||||
$ echo -e '#!/bin/sh\nexec git clang-format' > .git/hooks/pre-commit
|
||||
$ chmod u+x !$
|
||||
```
|
||||
|
||||
**NOTE**: To use ```git clang-format``` use have to make sure that ```git-clang-format``` is in your
|
||||
```PATH```. The easiest way is probably to just ```npm install -g clang-format``` as it comes with
|
||||
```git-clang-format```.
|
||||
|
||||
* **WebStorm** can run clang-format on the current file.
|
||||
1. Under Preferences, open Tools > External Tools.
|
||||
1. Plus icon to Create Tool
|
||||
|
14
README.md
14
README.md
@ -1,20 +1,18 @@
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://circleci.com/gh/angular/angular/tree/master)
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://badge.fury.io/js/angular2)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
Angular
|
||||
=========
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
|
||||
Angular 2 is currently in **Release Candidate**.
|
||||
Angular 2 is currently in **Beta**.
|
||||
|
||||
## Quickstart
|
||||
|
||||
|
@ -14,7 +14,7 @@ Ctrl + Shift + j.
|
||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||
|
||||
```typescript
|
||||
import {enableDebugTools} from '@angular/platform-browser';
|
||||
import {enableDebugTools} from 'angular2/platform/browser';
|
||||
|
||||
bootstrap(Application).then((appRef) => {
|
||||
enableDebugTools(appRef);
|
||||
@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
@View({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
|
@ -4,16 +4,13 @@
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
@ -24,7 +21,8 @@ var CIconfiguration = {
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
// TODO(mlaval): iOS9 deactivated as not reliable, reactivate after https://github.com/angular/angular/issues/5408
|
||||
'iOS9': { unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
@ -38,7 +36,7 @@ var customLaunchers = {
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '50'
|
||||
version: '46'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -53,7 +51,7 @@ var customLaunchers = {
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '45'
|
||||
version: '42'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
@ -121,9 +119,9 @@ var customLaunchers = {
|
||||
},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'MicrosoftEdge',
|
||||
browserName: 'microsoftedge',
|
||||
platform: 'Windows 10',
|
||||
version: '13.10586'
|
||||
version: '20.10240'
|
||||
},
|
||||
'SL_ANDROID4.1': {
|
||||
base: 'SauceLabs',
|
||||
@ -299,7 +297,12 @@ module.exports = {
|
||||
customLaunchers: customLaunchers,
|
||||
sauceAliases: sauceAliases,
|
||||
browserstackAliases: browserstackAliases
|
||||
};
|
||||
}
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
|
||||
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
|
||||
}
|
||||
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
|
120
build.sh
120
build.sh
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
|
||||
|
||||
rm -rf ./dist/all/
|
||||
mkdir -p ./dist/all/
|
||||
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
|
||||
echo "====== Copying files needed for e2e tests ====="
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/es6-shim/es6-shim.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
TSC="node dist/tools/@angular/tsc-wrapped/src/main"
|
||||
$TSC -p modules/tsconfig.json
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
|
||||
for PACKAGE in \
|
||||
core \
|
||||
compiler \
|
||||
common \
|
||||
forms \
|
||||
platform-browser \
|
||||
platform-browser-dynamic \
|
||||
platform-server \
|
||||
http \
|
||||
router \
|
||||
router-deprecated \
|
||||
upgrade \
|
||||
compiler-cli
|
||||
do
|
||||
SRCDIR=./modules/@angular/${PACKAGE}
|
||||
DESTDIR=./dist/packages-dist/${PACKAGE}
|
||||
UMD_ES6_PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json
|
||||
else
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es5.json
|
||||
fi
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
|
||||
|
||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [[ ${TRAVIS} ]]; then
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
else
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
fi
|
||||
|
||||
if [[ ${PACKAGE} != compiler-cli ]]; then
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== (esm)COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$(npm bin)/tsc --emitDecoratorMetadata -p ${SRCDIR}/tsconfig-es2015.json
|
||||
else
|
||||
echo "====== (esm)COMPILING: $TSC -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es2015.json
|
||||
fi
|
||||
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
mkdir ${DESTDIR}/bundles
|
||||
|
||||
if [[ ${PACKAGE} != router ]]; then
|
||||
(
|
||||
cd ${SRCDIR}
|
||||
echo "..." # here just to have grep match something and not exit with 1
|
||||
../../../node_modules/.bin/rollup -c rollup.config.js
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMD_ES5_PATH} \
|
||||
--target es5 \
|
||||
--lib "es6,dom" \
|
||||
--allowJs \
|
||||
${UMD_ES6_PATH}
|
||||
|
||||
rm ${UMD_ES6_PATH}
|
||||
|
||||
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
|
||||
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
|
||||
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
fi
|
||||
fi
|
||||
done
|
12
circle.yml
12
circle.yml
@ -5,7 +5,17 @@ machine:
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
override:
|
||||
- npm install:
|
||||
environment:
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This is not hidden using https://circleci.com/docs/fork-pr-builds#details
|
||||
# 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)
|
||||
TSD_GITHUB_TOKEN: ef474500309daea53d5991b3079159a29520a40b
|
||||
|
||||
test:
|
||||
override:
|
||||
- gulp lint
|
||||
- npm run build
|
||||
|
1573
gulpfile.js
1573
gulpfile.js
File diff suppressed because it is too large
Load Diff
1583
gulpfile.js.old
1583
gulpfile.js.old
File diff suppressed because it is too large
Load Diff
@ -11,37 +11,28 @@ module.exports = function(config) {
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the System loader, in `test-main.js`.
|
||||
{pattern: 'dist/all/@angular/**/*.js', included: false, watched: true},
|
||||
{pattern: 'dist/js/dev/es5/**', included: false, watched: false},
|
||||
|
||||
'node_modules/es6-shim/es6-shim.js',
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
// zone-microtask must be included first as it contains a Promise monkey patch
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/fake-async-test.js',
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'shims_for_IE.js',
|
||||
'modules/angular2/src/testing/shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false}
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/examples/**',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js'
|
||||
],
|
||||
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
|
||||
@ -51,6 +42,7 @@ module.exports = function(config) {
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-dart',
|
||||
internalAngularReporter
|
||||
],
|
||||
|
||||
@ -61,12 +53,11 @@ module.exports = function(config) {
|
||||
reporters: ['internal-angular'],
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
retryLimit: 3,
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
options: {
|
||||
'selenium-version': '2.53.0',
|
||||
'selenium-version': '2.48.2',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
@ -76,23 +67,19 @@ module.exports = function(config) {
|
||||
browserStack: {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 3,
|
||||
retryLimit: 1,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
port: 9876
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||
if (process.env.MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
@ -102,7 +89,7 @@ module.exports = function(config) {
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
|
||||
if (process.env.CI_MODE.startsWith('browserstack')) {
|
||||
if (process.env.MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
export 'index.dart';
|
@ -1,5 +0,0 @@
|
||||
export * from './src/pipes';
|
||||
export * from './src/directives';
|
||||
export * from './src/forms-deprecated';
|
||||
export * from './src/common_directives';
|
||||
export * from './src/location';
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "@angular/common",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"jsnext:main": "esm/index.js",
|
||||
"typings": "index.d.ts",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/esm/index.js',
|
||||
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
|
||||
format: 'umd',
|
||||
moduleName: 'ng.common',
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Subject': 'Rx',
|
||||
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
|
||||
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
|
||||
'rxjs/Observable': 'Rx'
|
||||
},
|
||||
plugins: [
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
]
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
import {AfterContentInit, Attribute, ContentChildren, Directive, Input, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {Map} from '../facade/collection';
|
||||
import {NumberWrapper, isPresent} from '../facade/lang';
|
||||
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
const _CATEGORY_DEFAULT = 'other';
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgLocalization { abstract getPluralCategory(value: any): string; }
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
||||
*
|
||||
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
|
||||
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
|
||||
* switch expression.
|
||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
||||
* expression.
|
||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly.
|
||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* value matches aren't found and the value maps to its category using the `getPluralCategory`
|
||||
* function provided.
|
||||
*
|
||||
* If no matching views are found for a switch expression, inner elements marked
|
||||
* `[ngPluralCase]="other"` will be displayed.
|
||||
*
|
||||
* ```typescript
|
||||
* class MyLocalization extends NgLocalization {
|
||||
* getPluralCategory(value: any) {
|
||||
* if(value < 5) {
|
||||
* return 'few';
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [{provide: NgLocalization, useClass: MyLocalization}]
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngPlural]="value">
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* <template ngPluralCase="other">there is some number</template>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [NgPlural, NgPluralCase]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef) {
|
||||
this._view = new SwitchView(viewContainer, template);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
export class NgPlural implements AfterContentInit {
|
||||
private _switchValue: number;
|
||||
private _activeView: SwitchView;
|
||||
private _caseViews = new Map<any, SwitchView>();
|
||||
@ContentChildren(NgPluralCase) cases: QueryList<NgPluralCase> = null;
|
||||
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
@Input()
|
||||
set ngPlural(value: number) {
|
||||
this._switchValue = value;
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.cases.forEach((pluralCase: NgPluralCase): void => {
|
||||
this._caseViews.set(this._formatValue(pluralCase), pluralCase._view);
|
||||
});
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
var view: SwitchView = this._caseViews.get(this._switchValue);
|
||||
if (!isPresent(view)) view = this._getCategoryView(this._switchValue);
|
||||
|
||||
this._activateView(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
if (isPresent(this._activeView)) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
if (!isPresent(view)) return;
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getCategoryView(value: number): SwitchView {
|
||||
var category: string = this._localization.getPluralCategory(value);
|
||||
var categoryView: SwitchView = this._caseViews.get(category);
|
||||
return isPresent(categoryView) ? categoryView : this._caseViews.get(_CATEGORY_DEFAULT);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_isValueView(pluralCase: NgPluralCase): boolean { return pluralCase.value[0] === '='; }
|
||||
|
||||
/** @internal */
|
||||
_formatValue(pluralCase: NgPluralCase): any {
|
||||
return this._isValueView(pluralCase) ? this._stripValue(pluralCase.value) : pluralCase.value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_stripValue(value: string): number { return NumberWrapper.parseInt(value.substring(1), 10); }
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||
* available within the `TemplateRef`.
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* ### Syntax
|
||||
* - `<template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression"></template>`
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
export class NgTemplateOutlet {
|
||||
private _viewRef: EmbeddedViewRef<any>;
|
||||
private _context: Object;
|
||||
private _templateRef: TemplateRef<any>;
|
||||
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
@Input()
|
||||
set ngOutletContext(context: Object) {
|
||||
if (this._context !== context) {
|
||||
this._context = context;
|
||||
if (isPresent(this._viewRef)) {
|
||||
this.createView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
|
||||
if (this._templateRef !== templateRef) {
|
||||
this._templateRef = templateRef;
|
||||
this.createView();
|
||||
}
|
||||
}
|
||||
|
||||
private createView() {
|
||||
if (isPresent(this._viewRef)) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (isPresent(this._templateRef)) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../facade/src
|
@ -1,51 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This module is used for handling user input, by defining and building a {@link ControlGroup} that
|
||||
* consists of
|
||||
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used
|
||||
* to read information
|
||||
* from the form DOM elements.
|
||||
*
|
||||
* Forms providers are not included in default providers; you must import these providers
|
||||
* explicitly.
|
||||
*/
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {RadioControlRegistry} from './forms-deprecated/directives/radio_control_value_accessor';
|
||||
import {FormBuilder} from './forms-deprecated/form_builder';
|
||||
|
||||
export {FORM_DIRECTIVES, RadioButtonState} from './forms-deprecated/directives';
|
||||
export {AbstractControlDirective} from './forms-deprecated/directives/abstract_control_directive';
|
||||
export {CheckboxControlValueAccessor} from './forms-deprecated/directives/checkbox_value_accessor';
|
||||
export {ControlContainer} from './forms-deprecated/directives/control_container';
|
||||
export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms-deprecated/directives/control_value_accessor';
|
||||
export {DefaultValueAccessor} from './forms-deprecated/directives/default_value_accessor';
|
||||
export {Form} from './forms-deprecated/directives/form_interface';
|
||||
export {NgControl} from './forms-deprecated/directives/ng_control';
|
||||
export {NgControlGroup} from './forms-deprecated/directives/ng_control_group';
|
||||
export {NgControlName} from './forms-deprecated/directives/ng_control_name';
|
||||
export {NgControlStatus} from './forms-deprecated/directives/ng_control_status';
|
||||
export {NgForm} from './forms-deprecated/directives/ng_form';
|
||||
export {NgFormControl} from './forms-deprecated/directives/ng_form_control';
|
||||
export {NgFormModel} from './forms-deprecated/directives/ng_form_model';
|
||||
export {NgModel} from './forms-deprecated/directives/ng_model';
|
||||
export {NgSelectOption, SelectControlValueAccessor} from './forms-deprecated/directives/select_control_value_accessor';
|
||||
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms-deprecated/directives/validators';
|
||||
export {FormBuilder} from './forms-deprecated/form_builder';
|
||||
export {AbstractControl, Control, ControlArray, ControlGroup} from './forms-deprecated/model';
|
||||
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms-deprecated/validators';
|
||||
|
||||
|
||||
/**
|
||||
* Shorthand set of providers used for building Angular forms.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* bootstrap(MyApp, [FORM_PROVIDERS]);
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry];
|
@ -1,33 +0,0 @@
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
import {AbstractControl} from '../model';
|
||||
|
||||
|
||||
/**
|
||||
* Base class for control directives.
|
||||
*
|
||||
* Only used internally in the forms module.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class AbstractControlDirective {
|
||||
get control(): AbstractControl { return unimplemented(); }
|
||||
|
||||
get value(): any { return isPresent(this.control) ? this.control.value : null; }
|
||||
|
||||
get valid(): boolean { return isPresent(this.control) ? this.control.valid : null; }
|
||||
|
||||
get errors(): {[key: string]: any} {
|
||||
return isPresent(this.control) ? this.control.errors : null;
|
||||
}
|
||||
|
||||
get pristine(): boolean { return isPresent(this.control) ? this.control.pristine : null; }
|
||||
|
||||
get dirty(): boolean { return isPresent(this.control) ? this.control.dirty : null; }
|
||||
|
||||
get touched(): boolean { return isPresent(this.control) ? this.control.touched : null; }
|
||||
|
||||
get untouched(): boolean { return isPresent(this.control) ? this.control.untouched : null; }
|
||||
|
||||
get path(): string[] { return null; }
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {Form} from './form_interface';
|
||||
|
||||
|
||||
/**
|
||||
* A directive that contains multiple {@link NgControl}s.
|
||||
*
|
||||
* Only used by the forms module.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export class ControlContainer extends AbstractControlDirective {
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Get the form to which this container belongs.
|
||||
*/
|
||||
get formDirective(): Form { return null; }
|
||||
|
||||
/**
|
||||
* Get the path to this container.
|
||||
*/
|
||||
get path(): string[] { return null; }
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
/**
|
||||
* A bridge between a control and a native element.
|
||||
*
|
||||
* A `ControlValueAccessor` abstracts the operations of writing a new value to a
|
||||
* DOM element representing an input control.
|
||||
*
|
||||
* Please see {@link DefaultValueAccessor} for more information.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface ControlValueAccessor {
|
||||
/**
|
||||
* Write a new value to the element.
|
||||
*/
|
||||
writeValue(obj: any): void;
|
||||
|
||||
/**
|
||||
* Set the function to be called when the control receives a change event.
|
||||
*/
|
||||
registerOnChange(fn: any): void;
|
||||
|
||||
/**
|
||||
* Set the function to be called when the control receives a touch event.
|
||||
*/
|
||||
registerOnTouched(fn: any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to provide a {@link ControlValueAccessor} for form controls.
|
||||
*
|
||||
* See {@link DefaultValueAccessor} for how to implement one.
|
||||
* @experimental
|
||||
*/
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken =
|
||||
/*@ts2dart_const*/ new OpaqueToken('NgValueAccessor');
|
@ -1,24 +0,0 @@
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
/**
|
||||
* A base class that all control directive extend.
|
||||
* It binds a {@link Control} object to a DOM element.
|
||||
*
|
||||
* Used internally by Angular forms.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgControl extends AbstractControlDirective {
|
||||
name: string = null;
|
||||
valueAccessor: ControlValueAccessor = null;
|
||||
|
||||
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
|
||||
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
|
||||
|
||||
abstract viewToModelUpdate(newValue: any): void;
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter, ObservableWrapper} from '../../facade/async';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const formControlBinding: any =
|
||||
/*@ts2dart_const*/ /* @ts2dart_Provider */ {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgModel)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a domain model to a form control.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* `ngModel` binds an existing domain model to a form control. For a
|
||||
* two-way binding, use `[(ngModel)]` to ensure the model updates in
|
||||
* both directions.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/R3UX5qDaUqFO2VYR0UzH?p=preview))
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: "search-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `<input type='text' [(ngModel)]="searchQuery">`
|
||||
* })
|
||||
* class SearchComp {
|
||||
* searchQuery: string;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngModel]:not([ngControl]):not([ngFormControl])',
|
||||
providers: [formControlBinding],
|
||||
inputs: ['model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgModel extends NgControl implements OnChanges {
|
||||
/** @internal */
|
||||
_control = new Control();
|
||||
/** @internal */
|
||||
_added = false;
|
||||
update = new EventEmitter();
|
||||
model: any;
|
||||
viewModel: any;
|
||||
|
||||
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[],
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||
valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this._added) {
|
||||
setUpControl(this._control, this);
|
||||
this._control.updateValueAndValidity({emitEvent: false});
|
||||
this._added = true;
|
||||
}
|
||||
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this._control.updateValue(this.model);
|
||||
this.viewModel = this.model;
|
||||
}
|
||||
}
|
||||
|
||||
get control(): Control { return this._control; }
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._asyncValidators);
|
||||
}
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
ObservableWrapper.callEmit(this.update, newValue);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
library angular2.core.forms.normalize_validators;
|
||||
|
||||
import 'package:angular2/src/common/forms/directives/validators.dart' show Validator;
|
||||
|
||||
Function normalizeValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Function normalizeAsyncValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import {AbstractControl} from '../model';
|
||||
|
||||
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||
|
||||
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <ValidatorFn>validator;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => Promise.resolve((<Validator>validator).validate(c));
|
||||
} else {
|
||||
return <AsyncValidatorFn>validator;
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {MapWrapper} from '../../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const SELECT_VALUE_ACCESSOR: any = /*@ts2dart_const*/ /*@ts2dart_Provider*/ {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (!isPrimitive(value)) value = 'Object';
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(':')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*
|
||||
* Note: We have to listen to the 'change' event because 'input' events aren't fired
|
||||
* for selects in Firefox and IE:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
|
||||
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'select:not([multiple])[ngControl],select:not([multiple])[ngFormControl],select:not([multiple])[ngModel]',
|
||||
host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, any> = new Map<string, any>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
var valueString = _buildValueString(this._getOptionId(value), value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (valueString: string) => {
|
||||
this.value = valueString;
|
||||
fn(this._getOptionValue(valueString));
|
||||
};
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(): string { return (this._idCounter++).toString(); }
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id), value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let value = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(value) ? value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <select ngControl="city">
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectOption implements OnDestroy {
|
||||
id: string;
|
||||
|
||||
constructor(
|
||||
private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectControlValueAccessor) {
|
||||
if (isPresent(this._select)) this.id = this._select._registerOption();
|
||||
}
|
||||
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._select._optionMap.set(this.id, value);
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
this._setElementValue(value);
|
||||
if (isPresent(this._select)) this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {MapWrapper} from '../../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
const SELECT_MULTIPLE_VALUE_ACCESSOR = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (isString(value)) value = `'${value}'`;
|
||||
if (!isPrimitive(value)) value = 'Object';
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(':')[0];
|
||||
}
|
||||
|
||||
/** Mock interface for HTML Options */
|
||||
interface HTMLOption {
|
||||
value: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
/** Mock interface for HTMLCollection */
|
||||
abstract class HTMLCollection {
|
||||
length: number;
|
||||
abstract item(_: number): HTMLOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[multiple][ngControl],select[multiple][ngFormControl],select[multiple][ngModel]',
|
||||
host: {'(input)': 'onChange($event.target)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, NgSelectMultipleOption> = new Map<string, NgSelectMultipleOption>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor() {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
if (value == null) return;
|
||||
let values: Array<any> = <Array<any>>value;
|
||||
// convert values to ids
|
||||
let ids = values.map((v) => this._getOptionId(v));
|
||||
this._optionMap.forEach((opt, o) => { opt._setSelected(ids.indexOf(o.toString()) > -1); });
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (_: any) => {
|
||||
let selected: Array<any> = [];
|
||||
if (_.hasOwnProperty('selectedOptions')) {
|
||||
let options: HTMLCollection = _.selectedOptions;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let opt: any = options.item(i);
|
||||
let val: any = this._getOptionValue(opt.value);
|
||||
selected.push(val);
|
||||
}
|
||||
}
|
||||
// Degrade on IE
|
||||
else {
|
||||
let options: HTMLCollection = <HTMLCollection>_.options;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let opt: HTMLOption = options.item(i);
|
||||
if (opt.selected) {
|
||||
let val: any = this._getOptionValue(opt.value);
|
||||
selected.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn(selected);
|
||||
};
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(value: NgSelectMultipleOption): string {
|
||||
let id: string = (this._idCounter++).toString();
|
||||
this._optionMap.set(id, value);
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id)._value, value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let opt = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(opt) ? opt._value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <select multiple ngControl="city">
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectMultipleOption implements OnDestroy {
|
||||
id: string;
|
||||
/** @internal */
|
||||
_value: any;
|
||||
|
||||
constructor(
|
||||
private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectMultipleControlValueAccessor) {
|
||||
if (isPresent(this._select)) {
|
||||
this.id = this._select._registerOption(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._value = value;
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
if (isPresent(this._select)) {
|
||||
this._value = value;
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
} else {
|
||||
this._setElementValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setSelected(selected: boolean) {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'selected', selected);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];
|
@ -1,5 +0,0 @@
|
||||
export * from './location/platform_location';
|
||||
export * from './location/location_strategy';
|
||||
export * from './location/hash_location_strategy';
|
||||
export * from './location/path_location_strategy';
|
||||
export * from './location/location';
|
@ -1,139 +0,0 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||
import {NumberWrapper, RegExpWrapper, Type, isBlank, isNumber, isPresent} from '../facade/lang';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
var defaultLocale: string = 'en-US';
|
||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/g;
|
||||
|
||||
/**
|
||||
* Internal function to format numbers used by Decimal, Percent and Date pipes.
|
||||
*/
|
||||
function formatNumber(
|
||||
pipe: Type, value: number, style: NumberFormatStyle, digits: string, currency: string = null,
|
||||
currencyAsSymbol: boolean = false): string {
|
||||
if (isBlank(value)) return null;
|
||||
if (!isNumber(value)) {
|
||||
throw new InvalidPipeArgumentException(pipe, value);
|
||||
}
|
||||
var minInt = 1, minFraction = 0, maxFraction = 3;
|
||||
if (isPresent(digits)) {
|
||||
var parts = RegExpWrapper.firstMatch(_NUMBER_FORMAT_REGEXP, digits);
|
||||
if (isBlank(parts)) {
|
||||
throw new BaseException(`${digits} is not a valid digit info for number pipes`);
|
||||
}
|
||||
if (isPresent(parts[1])) { // min integer digits
|
||||
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
|
||||
}
|
||||
if (isPresent(parts[3])) { // min fraction digits
|
||||
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
|
||||
}
|
||||
if (isPresent(parts[5])) { // max fraction digits
|
||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||
}
|
||||
}
|
||||
return NumberFormatter.format(value, defaultLocale, style, {
|
||||
minimumIntegerDigits: minInt,
|
||||
minimumFractionDigits: minFraction,
|
||||
maximumFractionDigits: maxFraction,
|
||||
currency: currency,
|
||||
currencyAsSymbol: currencyAsSymbol
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
*
|
||||
* Formats a number as local text. i.e. group sizing and separator and other locale-specific
|
||||
* configurations are based on the active locale.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | number[:digitInfo]
|
||||
*
|
||||
* where `expression` is a number and `digitInfo` has the following format:
|
||||
*
|
||||
* {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
|
||||
*
|
||||
* - minIntegerDigits is the minimum number of integer digits to use. Defaults to 1.
|
||||
* - minFractionDigits is the minimum number of digits after fraction. Defaults to 0.
|
||||
* - maxFractionDigits is the maximum number of digits after fraction. Defaults to 3.
|
||||
*
|
||||
* For more information on the acceptable range for each of these numbers and other
|
||||
* details see your native internationalization library.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Pipe({name: 'number'})
|
||||
export class DecimalPipe implements PipeTransform {
|
||||
transform(value: any, digits: string = null): string {
|
||||
return formatNumber(DecimalPipe, value, NumberFormatStyle.Decimal, digits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
*
|
||||
* Formats a number as local percent.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | percent[:digitInfo]
|
||||
*
|
||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Pipe({name: 'percent'})
|
||||
export class PercentPipe implements PipeTransform {
|
||||
transform(value: any, digits: string = null): string {
|
||||
return formatNumber(PercentPipe, value, NumberFormatStyle.Percent, digits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: this pipe uses the Internationalization API.
|
||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use an
|
||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
||||
*
|
||||
*
|
||||
* Formats a number as local currency.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]
|
||||
*
|
||||
* where `currencyCode` is the ISO 4217 currency code, such as "USD" for the US dollar and
|
||||
* "EUR" for the euro. `symbolDisplay` is a boolean indicating whether to use the currency
|
||||
* symbol (e.g. $) or the currency code (e.g. USD) in the output. The default for this value
|
||||
* is `false`.
|
||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Pipe({name: 'currency'})
|
||||
export class CurrencyPipe implements PipeTransform {
|
||||
transform(
|
||||
value: any, currencyCode: string = 'USD', symbolDisplay: boolean = false,
|
||||
digits: string = null): string {
|
||||
return formatNumber(
|
||||
CurrencyPipe, value, NumberFormatStyle.Currency, digits, currencyCode, symbolDisplay);
|
||||
}
|
||||
}
|
@ -1,590 +0,0 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {ComponentFixture, TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {ListWrapper, StringMapWrapper, SetWrapper} from '../../src/facade/collection';
|
||||
import {Component, provide} from '@angular/core';
|
||||
import {NgFor, NgClass} from '@angular/common';
|
||||
|
||||
function detectChangesAndCheck(fixture: ComponentFixture<any>, classes: string) {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].nativeElement.className).toEqual(classes);
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS class list', () => {
|
||||
|
||||
it('should clean up when the directive is destroyed',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [['0']];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = [['1']];
|
||||
|
||||
detectChangesAndCheck(fixture, '1');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
describe('expressions evaluating to objects', () => {
|
||||
|
||||
it('should add classes specified in an object literal',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="{foo: true, bar: false}"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should add classes specified in an object literal without change in class names',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="{'foo-bar': true, 'fooBar': true}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo-bar fooBar');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes based on changes in object literal values',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="{foo: condition, bar: !condition}"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.condition = false;
|
||||
detectChangesAndCheck(fixture, 'bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes based on changes to the expression object',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'baz', true);
|
||||
detectChangesAndCheck(fixture, 'foo bar baz');
|
||||
|
||||
StringMapWrapper.delete(fixture.debugElement.componentInstance.objExpr, 'bar');
|
||||
detectChangesAndCheck(fixture, 'foo baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes based on reference changes to the expression object',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {foo: true, bar: true};
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {baz: true};
|
||||
detectChangesAndCheck(fixture, 'baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should remove active classes when expression evaluates to null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = null;
|
||||
detectChangesAndCheck(fixture, '');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {'foo': false, 'bar': true};
|
||||
detectChangesAndCheck(fixture, 'bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should allow multiple classes per expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {
|
||||
'bar baz': true,
|
||||
'bar1 baz1': true
|
||||
};
|
||||
detectChangesAndCheck(fixture, 'bar baz bar1 baz1');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {
|
||||
'bar baz': false,
|
||||
'bar1 baz1': true
|
||||
};
|
||||
detectChangesAndCheck(fixture, 'bar1 baz1');
|
||||
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should split by one or more spaces between classes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = {'foo bar baz': true};
|
||||
detectChangesAndCheck(fixture, 'foo bar baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('expressions evaluating to lists', () => {
|
||||
|
||||
it('should add classes specified in a list literal',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="['foo', 'bar', 'foo-bar', 'fooBar']"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes based on changes to the expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var arrExpr: string[] = fixture.debugElement.componentInstance.arrExpr;
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
arrExpr.push('bar');
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
arrExpr[1] = 'baz';
|
||||
detectChangesAndCheck(fixture, 'foo baz');
|
||||
|
||||
ListWrapper.remove(fixture.debugElement.componentInstance.arrExpr, 'baz');
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes when a reference changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr = ['bar'];
|
||||
detectChangesAndCheck(fixture, 'bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should take initial classes into account when a reference changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr = ['bar'];
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore empty or blank class names',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr = ['', ' '];
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trim blanks from class names',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div class="foo" [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr = [' bar '];
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should allow multiple classes per item in arrays',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="arrExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr =
|
||||
['foo bar baz', 'foo1 bar1 baz1'];
|
||||
detectChangesAndCheck(fixture, 'foo bar baz foo1 bar1 baz1');
|
||||
|
||||
fixture.debugElement.componentInstance.arrExpr = ['foo bar baz foobar'];
|
||||
detectChangesAndCheck(fixture, 'foo bar baz foobar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('expressions evaluating to sets', () => {
|
||||
|
||||
it('should add and remove classes if the set instance changed',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="setExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var setExpr = new Set<string>();
|
||||
setExpr.add('bar');
|
||||
fixture.debugElement.componentInstance.setExpr = setExpr;
|
||||
detectChangesAndCheck(fixture, 'bar');
|
||||
|
||||
setExpr = new Set<string>();
|
||||
setExpr.add('baz');
|
||||
fixture.debugElement.componentInstance.setExpr = setExpr;
|
||||
detectChangesAndCheck(fixture, 'baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
describe('expressions evaluating to string', () => {
|
||||
|
||||
it('should add classes specified in a string literal',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="'foo bar foo-bar fooBar'"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo bar foo-bar fooBar');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove classes based on changes to the expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="strExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.strExpr = 'foo bar';
|
||||
detectChangesAndCheck(fixture, 'foo bar');
|
||||
|
||||
|
||||
fixture.debugElement.componentInstance.strExpr = 'baz';
|
||||
detectChangesAndCheck(fixture, 'baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should remove active classes when switching from string to null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.strExpr = null;
|
||||
detectChangesAndCheck(fixture, '');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should take initial classes into account when switching from string to null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.strExpr = null;
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore empty and blank strings',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div class="foo" [ngClass]="strExpr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.strExpr = '';
|
||||
detectChangesAndCheck(fixture, 'foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('cooperation with other class-changing constructs', () => {
|
||||
|
||||
it('should co-operate with the class attribute',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div [ngClass]="objExpr" class="init foo"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, 'init foo bar');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
||||
detectChangesAndCheck(fixture, 'init bar');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = null;
|
||||
detectChangesAndCheck(fixture, 'init foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with the interpolated class attribute',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="objExpr" class="{{'init foo'}}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, `init foo bar`);
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
||||
detectChangesAndCheck(fixture, `init bar`);
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = null;
|
||||
detectChangesAndCheck(fixture, `init foo`);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with the class attribute and binding to it',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngClass]="objExpr" class="init" [class]="'foo'"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, `init foo bar`);
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
||||
detectChangesAndCheck(fixture, `init bar`);
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = null;
|
||||
detectChangesAndCheck(fixture, `init foo`);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with the class attribute and class.name binding',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div class="init foo" [ngClass]="objExpr" [class.baz]="condition"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'init foo baz');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, 'init foo baz bar');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'foo', false);
|
||||
detectChangesAndCheck(fixture, 'init baz bar');
|
||||
|
||||
fixture.debugElement.componentInstance.condition = false;
|
||||
detectChangesAndCheck(fixture, 'init bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with initial class and class attribute binding when binding changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div class="init" [ngClass]="objExpr" [class]="strExpr"></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
detectChangesAndCheck(fixture, 'init foo');
|
||||
|
||||
StringMapWrapper.set(
|
||||
fixture.debugElement.componentInstance.objExpr, 'bar', true);
|
||||
detectChangesAndCheck(fixture, 'init foo bar');
|
||||
|
||||
fixture.debugElement.componentInstance.strExpr = 'baz';
|
||||
detectChangesAndCheck(fixture, 'init bar baz foo');
|
||||
|
||||
fixture.debugElement.componentInstance.objExpr = null;
|
||||
detectChangesAndCheck(fixture, 'init baz');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgClass, NgFor], template: ''})
|
||||
class TestComponent {
|
||||
condition: boolean = true;
|
||||
items: any[];
|
||||
arrExpr: string[] = ['foo'];
|
||||
setExpr: Set<string> = new Set<string>();
|
||||
objExpr = {'foo': true, 'bar': false};
|
||||
strExpr = 'foo';
|
||||
|
||||
constructor() { this.setExpr.add('foo'); }
|
||||
}
|
@ -1,569 +0,0 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
|
||||
import {ListWrapper} from '../../src/facade/collection';
|
||||
import {IS_DART} from '../../src/facade/lang';
|
||||
import {Component, TemplateRef, ContentChild} from '@angular/core';
|
||||
import {NgFor} from '@angular/common';
|
||||
import {NgIf} from '@angular/common';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
|
||||
export function main() {
|
||||
describe('ngFor', () => {
|
||||
var TEMPLATE =
|
||||
'<div><copy-me template="ngFor let item of items">{{item.toString()}};</copy-me></div>';
|
||||
|
||||
it('should reflect initial elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect added elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
(<number[]>fixture.debugElement.componentInstance.items).push(3);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect removed elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 1);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect moved elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
|
||||
(<number[]>fixture.debugElement.componentInstance.items).push(1);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('2;1;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect a mix of all changes (additions/removals/moves)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5];
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.debugElement.componentInstance.items = [6, 2, 7, 0, 4, 8];
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('6;2;7;0;4;8;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should iterate over an array of objects',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
// INIT
|
||||
fixture.debugElement.componentInstance.items =
|
||||
[{'name': 'misko'}, {'name': 'shyam'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;');
|
||||
|
||||
// GROW
|
||||
(<any[]>fixture.debugElement.componentInstance.items).push({'name': 'adam'});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('misko;shyam;adam;');
|
||||
|
||||
// SHRINK
|
||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 2);
|
||||
ListWrapper.removeAt(fixture.debugElement.componentInstance.items, 0);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('shyam;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should gracefully handle nulls',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should gracefully handle ref changing to null and back',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
||||
|
||||
fixture.debugElement.componentInstance.items = null;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [1, 2, 3];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;3;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
if (!IS_DART) {
|
||||
it('should throw on non-iterable ref and suggest using an array',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
||||
try {
|
||||
fixture.detectChanges()
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
`Cannot find a differ supporting object 'whaaa' of type 'string'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
it('should throw on ref changing to string',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('1;2;');
|
||||
|
||||
fixture.debugElement.componentInstance.items = 'whaaa';
|
||||
expect(() => fixture.detectChanges()).toThrowError();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should works with duplicates',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, TEMPLATE)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var a = new Foo();
|
||||
fixture.debugElement.componentInstance.items = [a, a];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('foo;foo;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should repeat over nested arrays',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<div template="ngFor let item of items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div>|' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
|
||||
fixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;|c-1;|');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('e-1;|f-2;g-2;|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should repeat over nested arrays with no intermediate element',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div><template ngFor let-item [ngForOf]="items">' +
|
||||
'<div template="ngFor let subitem of item">' +
|
||||
'{{subitem}}-{{item.length}};' +
|
||||
'</div></template></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [['a', 'b'], ['c']];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('a-2;b-2;c-1;');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [['e'], ['f', 'g']];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('e-1;f-2;g-2;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should repeat over nested ngIf that are the last node in the ngFor temlate',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
|
||||
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var el = fixture.debugElement.nativeElement;
|
||||
var items = [1];
|
||||
fixture.debugElement.componentInstance.items = items;
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|');
|
||||
|
||||
items.push(1);
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|1|');
|
||||
|
||||
items.push(1);
|
||||
fixture.detectChanges();
|
||||
expect(el).toHaveText('0|even|1|2|even|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display indices correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let i=index">{{i.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('0123456789');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display first item correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsefalse');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display last item correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('falsefalsetrue');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display even items correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalsetrue');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('truefalse');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display odd items correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
'<div><copy-me template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [0, 1, 2, 3];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetruefalsetrue');
|
||||
|
||||
fixture.debugElement.componentInstance.items = [2, 1];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('falsetrue');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to use a custom template',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(
|
||||
TestComponent,
|
||||
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>')
|
||||
.overrideTemplate(
|
||||
ComponentUsingTestComponent,
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should use a default template if a custom one is null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`)
|
||||
.overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should use a custom template when both default and a custom one are present',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
|
||||
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`)
|
||||
.overrideTemplate(
|
||||
ComponentUsingTestComponent,
|
||||
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
|
||||
.createAsync(ComponentUsingTestComponent)
|
||||
.then((fixture) => {
|
||||
var testComponent = fixture.debugElement.children[0];
|
||||
testComponent.componentInstance.items = ['a', 'b', 'c'];
|
||||
fixture.detectChanges();
|
||||
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('track by', function() {
|
||||
it('should not replace tracked items',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
|
||||
<p>{{items[i]}}</p>
|
||||
</template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var buildItemList =
|
||||
() => {
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a'}];
|
||||
fixture.detectChanges();
|
||||
return fixture.debugElement.queryAll(By.css('p'))[0];
|
||||
}
|
||||
|
||||
var firstP = buildItemList();
|
||||
var finalP = buildItemList();
|
||||
expect(finalP.nativeElement).toBe(firstP.nativeElement);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should update implicit local variable on view',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'blue'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('blue');
|
||||
fixture.debugElement.componentInstance.items = [{'id': 'a', 'color': 'red'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('red');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
it('should move items around and keep them updated ',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items =
|
||||
[{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('blueyellow');
|
||||
fixture.debugElement.componentInstance.items =
|
||||
[{'id': 'b', 'color': 'orange'}, {'id': 'a', 'color': 'red'}];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('orangered');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle added and removed items properly when tracking by index',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.items = ['a', 'b', 'c', 'd'];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'g', 'h'];
|
||||
fixture.detectChanges();
|
||||
fixture.debugElement.componentInstance.items = ['e', 'f', 'h'];
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('efh');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class Foo {
|
||||
toString() { return 'foo'; }
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgFor, NgIf], template: ''})
|
||||
class TestComponent {
|
||||
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
|
||||
items: any;
|
||||
constructor() { this.items = [1, 2]; }
|
||||
trackById(index: number, item: any): string { return item['id']; }
|
||||
trackByIndex(index: number, item: any): number { return index; }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-cmp', directives: [TestComponent], template: ''})
|
||||
class ComponentUsingTestComponent {
|
||||
items: any;
|
||||
constructor() { this.items = [1, 2]; }
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
import {beforeEach, ddescribe, describe, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {expect} from '@angular/platform-browser/testing';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
import {IS_DART} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('ngIf directive', () => {
|
||||
it('should work in a template attribute',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should work in a template element',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html =
|
||||
'<div><template [ngIf]="booleanCondition"><copy-me>hello2</copy-me></template></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello2');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should toggle node when condition changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf booleanCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle nested if correctly',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html =
|
||||
'<div><template [ngIf]="booleanCondition"><copy-me *ngIf="nestedBooleanCondition">hello</copy-me></template></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.nestedBooleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.nestedBooleanCondition = true;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.booleanCondition = false;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update several nodes with if',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div>' +
|
||||
'<copy-me template="ngIf numberCondition + 1 >= 2">helloNumber</copy-me>' +
|
||||
'<copy-me template="ngIf stringCondition == \'foo\'">helloString</copy-me>' +
|
||||
'<copy-me template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</copy-me>' +
|
||||
'</div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(3);
|
||||
expect(getDOM().getText(fixture.debugElement.nativeElement))
|
||||
.toEqual('helloNumberhelloStringhelloFunction');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 0;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('helloString');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 1;
|
||||
fixture.debugElement.componentInstance.stringCondition = 'bar';
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('helloNumber');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
if (!IS_DART) {
|
||||
it('should not add the element twice if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(1);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not recreate the element if the condition goes from true to true (JS)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
getDOM().addClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo');
|
||||
|
||||
fixture.debugElement.componentInstance.numberCondition = 2;
|
||||
fixture.detectChanges();
|
||||
expect(
|
||||
getDOM().hasClass(
|
||||
getDOM().querySelector(fixture.debugElement.nativeElement, 'copy-me'),
|
||||
'foo'))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
if (IS_DART) {
|
||||
it('should not create the element if the condition is not a boolean (DART)',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var html = '<div><copy-me template="ngIf numberCondition">hello</copy-me></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, html)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
expect(() => fixture.detectChanges()).toThrowError();
|
||||
expect(getDOM()
|
||||
.querySelectorAll(fixture.debugElement.nativeElement, 'copy-me')
|
||||
.length)
|
||||
.toEqual(0);
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgIf], template: ''})
|
||||
class TestComponent {
|
||||
booleanCondition: boolean;
|
||||
nestedBooleanCondition: boolean;
|
||||
numberCondition: number;
|
||||
stringCondition: string;
|
||||
functionCondition: Function;
|
||||
constructor() {
|
||||
this.booleanCondition = true;
|
||||
this.nestedBooleanCondition = true;
|
||||
this.numberCondition = 1;
|
||||
this.stringCondition = 'foo';
|
||||
this.functionCondition = function(s: any, n: any): boolean { return s == 'foo' && n == 1; };
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
import {beforeEachProviders, beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
|
||||
import {Component, Injectable} from '@angular/core';
|
||||
import {NgPlural, NgPluralCase, NgLocalization} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
beforeEachProviders(() => [{provide: NgLocalization, useClass: TestLocalizationMap}]);
|
||||
|
||||
it('should display the template according to the exact value',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
|
||||
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 0;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 1;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be applicable to <ng-container> elements',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ng-container [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="=0">you have no messages.</template>' +
|
||||
'<template ngPluralCase="=1">you have one message.</template>' +
|
||||
'</ng-container></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 0;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have no messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 1;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have one message.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the template according to the category',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 2;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('you have a few messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 8;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have many messages.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should default to other when no matches are found',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="other"><li>default message.</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 100;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('default message.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should prioritize value matches over category matches',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngPlural]="switchValue">' +
|
||||
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
|
||||
'<template ngPluralCase="=2">you have two messages.</template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.switchValue = 2;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('you have two messages.');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 3;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('you have a few messages.');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class TestLocalizationMap extends NgLocalization {
|
||||
getPluralCategory(value: number): string {
|
||||
if (value > 1 && value < 4) {
|
||||
return 'few';
|
||||
} else if (value >= 4 && value < 10) {
|
||||
return 'many';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgPluralCase, NgPlural], template: ''})
|
||||
class TestComponent {
|
||||
switchValue: number = null;
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
import {beforeEach, beforeEachProviders, ddescribe, xdescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {NgStyle} from '@angular/common/src/directives/ng_style';
|
||||
|
||||
export function main() {
|
||||
describe('binding to CSS styles', () => {
|
||||
|
||||
it('should add styles specified in an object literal',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and change styles specified in an object expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
var expr: Map<string, any>;
|
||||
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
expr = fixture.debugElement.componentInstance.expr;
|
||||
(expr as any)['max-width'] = '30%';
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('30%');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should remove styles when deleting a key in an object expression',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with the style attribute',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div style="font-size: 12px" [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should co-operate with the style.[styleName]="expr" special-case in the compiler',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<div [style.font-size.px]="12" [ngStyle]="expr"></div>`;
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.expr = {'max-width': '40px'};
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('40px');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
StringMapWrapper.delete(
|
||||
fixture.debugElement.componentInstance.expr, 'max-width');
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'font-size'))
|
||||
.toEqual('12px');
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(getDOM().getStyle(
|
||||
fixture.debugElement.children[0].nativeElement, 'max-width'))
|
||||
.toEqual('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
})
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgStyle], template: ''})
|
||||
class TestComponent {
|
||||
expr: any;
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {Component} from '@angular/core';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
|
||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('switch', () => {
|
||||
describe('switch value changes', () => {
|
||||
it('should switch amongst when values',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when b');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// TODO(robwormald): deprecate and remove
|
||||
it('should switch amongst when values using switchWhen',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchWhen="a"><li>when a</li></template>' +
|
||||
'<template ngSwitchWhen="b"><li>when b</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when b');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should switch amongst when values with fallback to default',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<li template="ngSwitchCase \'a\'">when a</li>' +
|
||||
'<li template="ngSwitchDefault">when default</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when default');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when a');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when default');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support multiple whens with the same value',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
|
||||
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
|
||||
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default1;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default2;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('when default1;when default2;');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when a1;when a2;');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when b1;when b2;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('when values changes', () => {
|
||||
it('should switch amongst when values',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>' +
|
||||
'<ul [ngSwitch]="switchValue">' +
|
||||
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
|
||||
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
|
||||
'<template ngSwitchDefault><li>when default;</li></template>' +
|
||||
'</ul></div>';
|
||||
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.debugElement.componentInstance.when1 = 'a';
|
||||
fixture.debugElement.componentInstance.when2 = 'b';
|
||||
fixture.debugElement.componentInstance.switchValue = 'a';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'b';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when 2;');
|
||||
|
||||
fixture.debugElement.componentInstance.switchValue = 'c';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
|
||||
|
||||
fixture.debugElement.componentInstance.when1 = 'c';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when 1;');
|
||||
|
||||
fixture.debugElement.componentInstance.when1 = 'd';
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('when default;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Component(
|
||||
{selector: 'test-cmp', directives: [NgSwitch, NgSwitchCase, NgSwitchDefault], template: ''})
|
||||
class TestComponent {
|
||||
switchValue: any;
|
||||
when1: any;
|
||||
when2: any;
|
||||
|
||||
constructor() {
|
||||
this.switchValue = null;
|
||||
this.when1 = null;
|
||||
this.when2 = null;
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {Component, Directive, TemplateRef, ContentChildren, QueryList} from '@angular/core';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('insert', () => {
|
||||
it('should do nothing if templateRef is null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = `<template [ngTemplateOutlet]="null"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should insert content specified by TemplateRef',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should clear content if TemplateRef becomes null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentTplRef = null;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should swap content if TemplateRef changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.last;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display template if context is null',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('foo');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect initial context and changes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('bar');
|
||||
|
||||
fixture.componentInstance.context.foo = 'alter-bar';
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('alter-bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect user defined $implicit property in the context',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
|
||||
fixture.componentInstance.context = {
|
||||
$implicit: fixture.componentInstance.context
|
||||
};
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('bar');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect context re-binding',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template =
|
||||
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
var refs = fixture.debugElement.children[0].references['refs'];
|
||||
fixture.componentInstance.currentTplRef = refs.tplRefs.first;
|
||||
fixture.componentInstance.context = {shawshank: 'brooks'};
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('brooks');
|
||||
|
||||
fixture.componentInstance.context = {shawshank: 'was here'};
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('was here');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
|
||||
class CaptureTplRefs {
|
||||
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [NgTemplateOutlet, CaptureTplRefs], template: ''})
|
||||
class TestComponent {
|
||||
currentTplRef: TemplateRef<any>;
|
||||
context: any = {foo: 'bar'};
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit,} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {Component, Directive} from '@angular/core';
|
||||
import {ElementRef} from '@angular/core/src/linker/element_ref';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('non-bindable', () => {
|
||||
it('should not interpolate children',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div>{{text}}<span ngNonBindable>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('foo{{text}}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should ignore directives on child nodes',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div ngNonBindable><span id=child test-dec>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// We must use getDOM().querySelector instead of fixture.query here
|
||||
// since the elements inside are not compiled.
|
||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(getDOM().hasClass(span, 'compiled')).toBeFalsy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trigger directives on the same node',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
var template = '<div><span id=child ngNonBindable test-dec>{{text}}</span></div>';
|
||||
tcb.overrideTemplate(TestComponent, template)
|
||||
.createAsync(TestComponent)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
var span = getDOM().querySelector(fixture.debugElement.nativeElement, '#child');
|
||||
expect(getDOM().hasClass(span, 'compiled')).toBeTruthy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
})
|
||||
}
|
||||
|
||||
@Directive({selector: '[test-dec]'})
|
||||
class TestDirective {
|
||||
constructor(el: ElementRef) { getDOM().addClass(el.nativeElement, 'compiled'); }
|
||||
}
|
||||
|
||||
@Component({selector: 'test-cmp', directives: [TestDirective], template: ''})
|
||||
class TestComponent {
|
||||
text: string;
|
||||
constructor() { this.text = 'foo'; }
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
import {Control, FormBuilder} from '@angular/common/src/forms-deprecated';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
|
||||
export function main() {
|
||||
function syncValidator(_: any): any { return null; }
|
||||
function asyncValidator(_: any) { return PromiseWrapper.resolve(null); }
|
||||
|
||||
describe('Form Builder', () => {
|
||||
var b: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => { b = new FormBuilder(); });
|
||||
|
||||
it('should create controls from a value', () => {
|
||||
var g = b.group({'login': 'some value'});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
});
|
||||
|
||||
it('should create controls from an array', () => {
|
||||
var g = b.group(
|
||||
{'login': ['some value'], 'password': ['some value', syncValidator, asyncValidator]});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['password'].value).toEqual('some value');
|
||||
expect(g.controls['password'].validator).toEqual(syncValidator);
|
||||
expect(g.controls['password'].asyncValidator).toEqual(asyncValidator);
|
||||
});
|
||||
|
||||
it('should use controls', () => {
|
||||
var g = b.group({'login': b.control('some value', syncValidator, asyncValidator)});
|
||||
|
||||
expect(g.controls['login'].value).toEqual('some value');
|
||||
expect(g.controls['login'].validator).toBe(syncValidator);
|
||||
expect(g.controls['login'].asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create groups with optional controls', () => {
|
||||
var g = b.group({'login': 'some value'}, {'optionals': {'login': false}});
|
||||
|
||||
expect(g.contains('login')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should create groups with a custom validator', () => {
|
||||
var g = b.group(
|
||||
{'login': 'some value'}, {'validator': syncValidator, 'asyncValidator': asyncValidator});
|
||||
|
||||
expect(g.validator).toBe(syncValidator);
|
||||
expect(g.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
|
||||
it('should create control arrays', () => {
|
||||
var c = b.control('three');
|
||||
var a = b.array(
|
||||
['one', ['two', syncValidator], c, b.array(['four'])], syncValidator, asyncValidator);
|
||||
|
||||
expect(a.value).toEqual(['one', 'two', 'three', ['four']]);
|
||||
expect(a.validator).toBe(syncValidator);
|
||||
expect(a.asyncValidator).toBe(asyncValidator);
|
||||
});
|
||||
});
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,827 +0,0 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {fakeAsync, flushMicrotasks, Log, tick} from '@angular/core/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {ControlGroup, Control, ControlArray, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {IS_DART, isPresent} from '../../src/facade/lang';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
import {TimerWrapper, ObservableWrapper, EventEmitter} from '../../src/facade/async';
|
||||
|
||||
export function main() {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, timeouts = /*@ts2dart_const*/ {}) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var completer = PromiseWrapper.completer();
|
||||
var t = isPresent((timeouts as any /** TODO #9100 */)[c.value]) ?
|
||||
(timeouts as any /** TODO #9100 */)[c.value] :
|
||||
0;
|
||||
var res = c.value != expected ? {'async': true} : null;
|
||||
|
||||
if (t == 0) {
|
||||
completer.resolve(res);
|
||||
} else {
|
||||
TimerWrapper.setTimeout(() => { completer.resolve(res); }, t);
|
||||
}
|
||||
|
||||
return completer.promise;
|
||||
};
|
||||
}
|
||||
|
||||
function asyncValidatorReturningObservable(c: any /** TODO #9100 */) {
|
||||
var e = new EventEmitter();
|
||||
PromiseWrapper.scheduleMicrotask(() => ObservableWrapper.callEmit(e, {'async': true}));
|
||||
return e;
|
||||
}
|
||||
|
||||
describe('Form Model', () => {
|
||||
describe('Control', () => {
|
||||
it('should default the value to null', () => {
|
||||
var c = new Control();
|
||||
expect(c.value).toBe(null);
|
||||
});
|
||||
|
||||
describe('validator', () => {
|
||||
it('should run validator with the initial value', () => {
|
||||
var c = new Control('value', Validators.required);
|
||||
expect(c.valid).toEqual(true);
|
||||
});
|
||||
|
||||
it('should rerun the validator when the value changes', () => {
|
||||
var c = new Control('value', Validators.required);
|
||||
c.updateValue(null);
|
||||
expect(c.valid).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return errors', () => {
|
||||
var c = new Control(null, Validators.required);
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('asyncValidator', () => {
|
||||
it('should run validator with the initial value', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should support validators returning observables', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidatorReturningObservable);
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should rerun the validator when the value changes', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
|
||||
c.updateValue('expected');
|
||||
tick();
|
||||
|
||||
expect(c.valid).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should run the async validator only when the sync validator passes', fakeAsync(() => {
|
||||
var c = new Control('', Validators.required, asyncValidator('expected'));
|
||||
tick();
|
||||
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
|
||||
c.updateValue('some value');
|
||||
tick();
|
||||
|
||||
expect(c.errors).toEqual({'async': true});
|
||||
}));
|
||||
|
||||
it('should mark the control as pending while running the async validation',
|
||||
fakeAsync(() => {
|
||||
var c = new Control('', null, asyncValidator('expected'));
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
|
||||
tick();
|
||||
|
||||
expect(c.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should only use the latest async validation run', fakeAsync(() => {
|
||||
var c =
|
||||
new Control('', null, asyncValidator('expected', {'long': 200, 'expected': 100}));
|
||||
|
||||
c.updateValue('long');
|
||||
c.updateValue('expected');
|
||||
|
||||
tick(300);
|
||||
|
||||
expect(c.valid).toEqual(true);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('dirty', () => {
|
||||
it('should be false after creating a control', () => {
|
||||
var c = new Control('value');
|
||||
expect(c.dirty).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be true after changing the value of the control', () => {
|
||||
var c = new Control('value');
|
||||
c.markAsDirty();
|
||||
expect(c.dirty).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateValue', () => {
|
||||
var g: any /** TODO #9100 */, c: any /** TODO #9100 */;
|
||||
beforeEach(() => {
|
||||
c = new Control('oldValue');
|
||||
g = new ControlGroup({'one': c});
|
||||
});
|
||||
|
||||
it('should update the value of the control', () => {
|
||||
c.updateValue('newValue');
|
||||
expect(c.value).toEqual('newValue');
|
||||
});
|
||||
|
||||
it('should invoke ngOnChanges if it is present', () => {
|
||||
var ngOnChanges: any /** TODO #9100 */;
|
||||
c.registerOnChange((v: any /** TODO #9100 */) => ngOnChanges = ['invoked', v]);
|
||||
|
||||
c.updateValue('newValue');
|
||||
|
||||
expect(ngOnChanges).toEqual(['invoked', 'newValue']);
|
||||
});
|
||||
|
||||
it('should not invoke on change when explicitly specified', () => {
|
||||
var onChange: any /** TODO #9100 */ = null;
|
||||
c.registerOnChange((v: any /** TODO #9100 */) => onChange = ['invoked', v]);
|
||||
|
||||
c.updateValue('newValue', {emitModelToViewChange: false});
|
||||
|
||||
expect(onChange).toBeNull();
|
||||
});
|
||||
|
||||
it('should update the parent', () => {
|
||||
c.updateValue('newValue');
|
||||
expect(g.value).toEqual({'one': 'newValue'});
|
||||
});
|
||||
|
||||
it('should not update the parent when explicitly specified', () => {
|
||||
c.updateValue('newValue', {onlySelf: true});
|
||||
expect(g.value).toEqual({'one': 'oldValue'});
|
||||
});
|
||||
|
||||
it('should fire an event', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(
|
||||
c.valueChanges, (value) => { expect(value).toEqual('newValue'); });
|
||||
|
||||
c.updateValue('newValue');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should not fire an event when explicitly specified', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => { throw 'Should not happen'; });
|
||||
|
||||
c.updateValue('newValue', {emitEvent: false});
|
||||
|
||||
tick();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('valueChanges & statusChanges', () => {
|
||||
var c: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => { c = new Control('old', Validators.required); });
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => {
|
||||
expect(c.value).toEqual('new');
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
|
||||
it('should fire an event after the status has been updated to invalid', fakeAsync(() => {
|
||||
ObservableWrapper.subscribe(c.statusChanges, (status) => {
|
||||
expect(c.status).toEqual('INVALID');
|
||||
expect(status).toEqual('INVALID');
|
||||
});
|
||||
|
||||
c.updateValue('');
|
||||
tick();
|
||||
}));
|
||||
|
||||
it('should fire an event after the status has been updated to pending', fakeAsync(() => {
|
||||
var c = new Control('old', Validators.required, asyncValidator('expected'));
|
||||
|
||||
var log: any[] /** TODO #9100 */ = [];
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => log.push(`value: '${value}'`));
|
||||
ObservableWrapper.subscribe(
|
||||
c.statusChanges, (status) => log.push(`status: '${status}'`));
|
||||
|
||||
c.updateValue('');
|
||||
tick();
|
||||
|
||||
c.updateValue('nonEmpty');
|
||||
tick();
|
||||
|
||||
c.updateValue('expected');
|
||||
tick();
|
||||
|
||||
expect(log).toEqual([
|
||||
'' +
|
||||
'value: \'\'',
|
||||
'status: \'INVALID\'',
|
||||
'value: \'nonEmpty\'',
|
||||
'status: \'PENDING\'',
|
||||
'status: \'INVALID\'',
|
||||
'value: \'expected\'',
|
||||
'status: \'PENDING\'',
|
||||
'status: \'VALID\'',
|
||||
]);
|
||||
}));
|
||||
|
||||
// TODO: remove the if statement after making observable delivery sync
|
||||
if (!IS_DART) {
|
||||
it('should update set errors and status before emitting an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.valueChanges.subscribe((value: any /** TODO #9100 */) => {
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('');
|
||||
}));
|
||||
}
|
||||
|
||||
it('should return a cold observable',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
c.updateValue('will be ignored');
|
||||
ObservableWrapper.subscribe(c.valueChanges, (value) => {
|
||||
expect(value).toEqual('new');
|
||||
async.done();
|
||||
});
|
||||
c.updateValue('new');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('setErrors', () => {
|
||||
it('should set errors on a control', () => {
|
||||
var c = new Control('someValue');
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(c.valid).toEqual(false);
|
||||
expect(c.errors).toEqual({'someError': true});
|
||||
});
|
||||
|
||||
it('should reset the errors and validity when the value changes', () => {
|
||||
var c = new Control('someValue', Validators.required);
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
c.updateValue('');
|
||||
|
||||
expect(c.errors).toEqual({'required': true});
|
||||
});
|
||||
|
||||
it('should update the parent group\'s validity', () => {
|
||||
var c = new Control('someValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not reset parent\'s errors', () => {
|
||||
var c = new Control('someValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
g.setErrors({'someGroupError': true});
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
expect(g.errors).toEqual({'someGroupError': true});
|
||||
});
|
||||
|
||||
it('should reset errors when updating a value', () => {
|
||||
var c = new Control('oldValue');
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
g.setErrors({'someGroupError': true});
|
||||
c.setErrors({'someError': true});
|
||||
|
||||
c.updateValue('newValue');
|
||||
|
||||
expect(c.errors).toEqual(null);
|
||||
expect(g.errors).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ControlGroup', () => {
|
||||
describe('value', () => {
|
||||
it('should be the reduced value of the child controls', () => {
|
||||
var g = new ControlGroup({'one': new Control('111'), 'two': new Control('222')});
|
||||
expect(g.value).toEqual({'one': '111', 'two': '222'});
|
||||
});
|
||||
|
||||
it('should be empty when there are no child controls', () => {
|
||||
var g = new ControlGroup({});
|
||||
expect(g.value).toEqual({});
|
||||
});
|
||||
|
||||
it('should support nested groups', () => {
|
||||
var g = new ControlGroup(
|
||||
{'one': new Control('111'), 'nested': new ControlGroup({'two': new Control('222')})});
|
||||
expect(g.value).toEqual({'one': '111', 'nested': {'two': '222'}});
|
||||
|
||||
(<Control>(g.controls['nested'].find('two'))).updateValue('333');
|
||||
|
||||
expect(g.value).toEqual({'one': '111', 'nested': {'two': '333'}});
|
||||
});
|
||||
});
|
||||
|
||||
describe('adding and removing controls', () => {
|
||||
it('should update value and validity when control is added', () => {
|
||||
var g = new ControlGroup({'one': new Control('1')});
|
||||
expect(g.value).toEqual({'one': '1'});
|
||||
expect(g.valid).toBe(true);
|
||||
|
||||
g.addControl('two', new Control('2', Validators.minLength(10)));
|
||||
|
||||
expect(g.value).toEqual({'one': '1', 'two': '2'});
|
||||
expect(g.valid).toBe(false);
|
||||
});
|
||||
|
||||
it('should update value and validity when control is removed', () => {
|
||||
var g = new ControlGroup(
|
||||
{'one': new Control('1'), 'two': new Control('2', Validators.minLength(10))});
|
||||
expect(g.value).toEqual({'one': '1', 'two': '2'});
|
||||
expect(g.valid).toBe(false);
|
||||
|
||||
g.removeControl('two');
|
||||
|
||||
expect(g.value).toEqual({'one': '1'});
|
||||
expect(g.valid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should run the validator when the value changes', () => {
|
||||
var simpleValidator = (c: any /** TODO #9100 */) =>
|
||||
c.controls['one'].value != 'correct' ? {'broken': true} : null;
|
||||
|
||||
var c = new Control(null);
|
||||
var g = new ControlGroup({'one': c}, null, simpleValidator);
|
||||
|
||||
c.updateValue('correct');
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
expect(g.errors).toEqual(null);
|
||||
|
||||
c.updateValue('incorrect');
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
expect(g.errors).toEqual({'broken': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dirty', () => {
|
||||
var c: any /** TODO #9100 */, g: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
c = new Control('value');
|
||||
g = new ControlGroup({'one': c});
|
||||
});
|
||||
|
||||
it('should be false after creating a control', () => { expect(g.dirty).toEqual(false); });
|
||||
|
||||
it('should be false after changing the value of the control', () => {
|
||||
c.markAsDirty();
|
||||
|
||||
expect(g.dirty).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('optional components', () => {
|
||||
describe('contains', () => {
|
||||
var group: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
group = new ControlGroup(
|
||||
{
|
||||
'required': new Control('requiredValue'),
|
||||
'optional': new Control('optionalValue')
|
||||
},
|
||||
{'optional': false});
|
||||
});
|
||||
|
||||
// rename contains into has
|
||||
it('should return false when the component is not included',
|
||||
() => { expect(group.contains('optional')).toEqual(false); })
|
||||
|
||||
it('should return false when there is no component with the given name',
|
||||
() => { expect(group.contains('something else')).toEqual(false); });
|
||||
|
||||
it('should return true when the component is included', () => {
|
||||
expect(group.contains('required')).toEqual(true);
|
||||
|
||||
group.include('optional');
|
||||
|
||||
expect(group.contains('optional')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include an inactive component into the group value', () => {
|
||||
var group = new ControlGroup(
|
||||
{'required': new Control('requiredValue'), 'optional': new Control('optionalValue')},
|
||||
{'optional': false});
|
||||
|
||||
expect(group.value).toEqual({'required': 'requiredValue'});
|
||||
|
||||
group.include('optional');
|
||||
|
||||
expect(group.value).toEqual({'required': 'requiredValue', 'optional': 'optionalValue'});
|
||||
});
|
||||
|
||||
it('should not run Validators on an inactive component', () => {
|
||||
var group = new ControlGroup(
|
||||
{
|
||||
'required': new Control('requiredValue', Validators.required),
|
||||
'optional': new Control('', Validators.required)
|
||||
},
|
||||
{'optional': false});
|
||||
|
||||
expect(group.valid).toEqual(true);
|
||||
|
||||
group.include('optional');
|
||||
|
||||
expect(group.valid).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueChanges', () => {
|
||||
var g: any /** TODO #9100 */, c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
c1 = new Control('old1');
|
||||
c2 = new Control('old2');
|
||||
g = new ControlGroup({'one': c1, 'two': c2}, {'two': true});
|
||||
});
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(g.value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
expect(value).toEqual({'one': 'new1', 'two': 'old2'});
|
||||
async.done();
|
||||
});
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event after the control\'s observable fired an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
ObservableWrapper.subscribe(
|
||||
c1.valueChanges, (value) => { controlCallbackIsCalled = true; });
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is excluded',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(value).toEqual({'one': 'old1'});
|
||||
async.done();
|
||||
});
|
||||
|
||||
g.exclude('two');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is included',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
g.exclude('two');
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
expect(value).toEqual({'one': 'old1', 'two': 'old2'});
|
||||
async.done();
|
||||
});
|
||||
|
||||
g.include('two');
|
||||
}));
|
||||
|
||||
it('should fire an event every time a control is updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var loggedValues: any[] /** TODO #9100 */ = [];
|
||||
|
||||
ObservableWrapper.subscribe(g.valueChanges, (value) => {
|
||||
loggedValues.push(value);
|
||||
|
||||
if (loggedValues.length == 2) {
|
||||
expect(loggedValues).toEqual([
|
||||
{'one': 'new1', 'two': 'old2'}, {'one': 'new1', 'two': 'new2'}
|
||||
]);
|
||||
async.done();
|
||||
}
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
c2.updateValue('new2');
|
||||
}));
|
||||
|
||||
xit('should not fire an event when an excluded control is updated',
|
||||
inject(
|
||||
[AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
// hard to test without hacking zones
|
||||
}));
|
||||
});
|
||||
|
||||
describe('getError', () => {
|
||||
it('should return the error when it is present', () => {
|
||||
var c = new Control('', Validators.required);
|
||||
var g = new ControlGroup({'one': c});
|
||||
expect(c.getError('required')).toEqual(true);
|
||||
expect(g.getError('required', ['one'])).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return null otherwise', () => {
|
||||
var c = new Control('not empty', Validators.required);
|
||||
var g = new ControlGroup({'one': c});
|
||||
expect(c.getError('invalid')).toEqual(null);
|
||||
expect(g.getError('required', ['one'])).toEqual(null);
|
||||
expect(g.getError('required', ['invalid'])).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asyncValidator', () => {
|
||||
it('should run the async validator', fakeAsync(() => {
|
||||
var c = new Control('value');
|
||||
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should set the parent group\'s status to pending', fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
var g = new ControlGroup({'one': c});
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
|
||||
it('should run the parent group\'s async validator when children are pending',
|
||||
fakeAsync(() => {
|
||||
var c = new Control('value', null, asyncValidator('expected'));
|
||||
var g = new ControlGroup({'one': c}, null, null, asyncValidator('expected'));
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.find(['one']).errors).toEqual({'async': true});
|
||||
}));
|
||||
})
|
||||
});
|
||||
|
||||
describe('ControlArray', () => {
|
||||
describe('adding/removing', () => {
|
||||
var a: ControlArray;
|
||||
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */, c3: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
a = new ControlArray([]);
|
||||
c1 = new Control(1);
|
||||
c2 = new Control(2);
|
||||
c3 = new Control(3);
|
||||
});
|
||||
|
||||
it('should support pushing', () => {
|
||||
a.push(c1);
|
||||
expect(a.length).toEqual(1);
|
||||
expect(a.controls).toEqual([c1]);
|
||||
});
|
||||
|
||||
it('should support removing', () => {
|
||||
a.push(c1);
|
||||
a.push(c2);
|
||||
a.push(c3);
|
||||
|
||||
a.removeAt(1);
|
||||
|
||||
expect(a.controls).toEqual([c1, c3]);
|
||||
});
|
||||
|
||||
it('should support inserting', () => {
|
||||
a.push(c1);
|
||||
a.push(c3);
|
||||
|
||||
a.insert(1, c2);
|
||||
|
||||
expect(a.controls).toEqual([c1, c2, c3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('value', () => {
|
||||
it('should be the reduced value of the child controls', () => {
|
||||
var a = new ControlArray([new Control(1), new Control(2)]);
|
||||
expect(a.value).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('should be an empty array when there are no child controls', () => {
|
||||
var a = new ControlArray([]);
|
||||
expect(a.value).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should run the validator when the value changes', () => {
|
||||
var simpleValidator = (c: any /** TODO #9100 */) =>
|
||||
c.controls[0].value != 'correct' ? {'broken': true} : null;
|
||||
|
||||
var c = new Control(null);
|
||||
var g = new ControlArray([c], simpleValidator);
|
||||
|
||||
c.updateValue('correct');
|
||||
|
||||
expect(g.valid).toEqual(true);
|
||||
expect(g.errors).toEqual(null);
|
||||
|
||||
c.updateValue('incorrect');
|
||||
|
||||
expect(g.valid).toEqual(false);
|
||||
expect(g.errors).toEqual({'broken': true});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('dirty', () => {
|
||||
var c: Control;
|
||||
var a: ControlArray;
|
||||
|
||||
beforeEach(() => {
|
||||
c = new Control('value');
|
||||
a = new ControlArray([c]);
|
||||
});
|
||||
|
||||
it('should be false after creating a control', () => { expect(a.dirty).toEqual(false); });
|
||||
|
||||
it('should be false after changing the value of the control', () => {
|
||||
c.markAsDirty();
|
||||
|
||||
expect(a.dirty).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pending', () => {
|
||||
var c: Control;
|
||||
var a: ControlArray;
|
||||
|
||||
beforeEach(() => {
|
||||
c = new Control('value');
|
||||
a = new ControlArray([c]);
|
||||
});
|
||||
|
||||
it('should be false after creating a control', () => {
|
||||
expect(c.pending).toEqual(false);
|
||||
expect(a.pending).toEqual(false);
|
||||
});
|
||||
|
||||
it('should be true after changing the value of the control', () => {
|
||||
c.markAsPending();
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
expect(a.pending).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not update the parent when onlySelf = true', () => {
|
||||
c.markAsPending({onlySelf: true});
|
||||
|
||||
expect(c.pending).toEqual(true);
|
||||
expect(a.pending).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueChanges', () => {
|
||||
var a: ControlArray;
|
||||
var c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;
|
||||
|
||||
beforeEach(() => {
|
||||
c1 = new Control('old1');
|
||||
c2 = new Control('old2');
|
||||
a = new ControlArray([c1, c2]);
|
||||
});
|
||||
|
||||
it('should fire an event after the value has been updated',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(a.value).toEqual(['new1', 'old2']);
|
||||
expect(value).toEqual(['new1', 'old2']);
|
||||
async.done();
|
||||
});
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event after the control\'s observable fired an event',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
var controlCallbackIsCalled = false;
|
||||
|
||||
ObservableWrapper.subscribe(
|
||||
c1.valueChanges, (value) => { controlCallbackIsCalled = true; });
|
||||
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(controlCallbackIsCalled).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
|
||||
c1.updateValue('new1');
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is removed',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(value).toEqual(['old1']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
a.removeAt(1);
|
||||
}));
|
||||
|
||||
it('should fire an event when a control is added',
|
||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||
a.removeAt(1);
|
||||
|
||||
ObservableWrapper.subscribe(a.valueChanges, (value) => {
|
||||
expect(value).toEqual(['old1', 'old2']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
a.push(c2);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('find', () => {
|
||||
it('should return null when path is null', () => {
|
||||
var g = new ControlGroup({});
|
||||
expect(g.find(null)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should return null when path is empty', () => {
|
||||
var g = new ControlGroup({});
|
||||
expect(g.find([])).toEqual(null);
|
||||
});
|
||||
|
||||
it('should return null when path is invalid', () => {
|
||||
var g = new ControlGroup({});
|
||||
expect(g.find(['one', 'two'])).toEqual(null);
|
||||
});
|
||||
|
||||
it('should return a child of a control group', () => {
|
||||
var g = new ControlGroup(
|
||||
{'one': new Control('111'), 'nested': new ControlGroup({'two': new Control('222')})});
|
||||
|
||||
expect(g.find(['nested', 'two']).value).toEqual('222');
|
||||
expect(g.find(['one']).value).toEqual('111');
|
||||
expect(g.find('nested/two').value).toEqual('222');
|
||||
expect(g.find('one').value).toEqual('111');
|
||||
});
|
||||
|
||||
it('should return an element of an array', () => {
|
||||
var g = new ControlGroup({'array': new ControlArray([new Control('111')])});
|
||||
|
||||
expect(g.find(['array', 0]).value).toEqual('111');
|
||||
});
|
||||
});
|
||||
|
||||
describe('asyncValidator', () => {
|
||||
it('should run the async validator', fakeAsync(() => {
|
||||
var c = new Control('value');
|
||||
var g = new ControlArray([c], null, asyncValidator('expected'));
|
||||
|
||||
expect(g.pending).toEqual(true);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(g.errors).toEqual({'async': true});
|
||||
expect(g.pending).toEqual(false);
|
||||
}));
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup, Validators} from '@angular/common/src/forms-deprecated';
|
||||
import {Log, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {EventEmitter, ObservableWrapper, TimerWrapper} from '../../src/facade/async';
|
||||
import {PromiseWrapper} from '../../src/facade/promise';
|
||||
|
||||
export function main() {
|
||||
function validator(key: string, error: any) {
|
||||
return function(c: AbstractControl) {
|
||||
var r = {};
|
||||
(r as any /** TODO #9100 */)[key] = error;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Validators', () => {
|
||||
describe('required', () => {
|
||||
it('should error on an empty string',
|
||||
() => { expect(Validators.required(new Control(''))).toEqual({'required': true}); });
|
||||
|
||||
it('should error on null',
|
||||
() => { expect(Validators.required(new Control(null))).toEqual({'required': true}); });
|
||||
|
||||
it('should not error on a non-empty string',
|
||||
() => { expect(Validators.required(new Control('not empty'))).toEqual(null); });
|
||||
|
||||
it('should accept zero as valid',
|
||||
() => { expect(Validators.required(new Control(0))).toEqual(null); });
|
||||
});
|
||||
|
||||
describe('minLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.minLength(2)(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.minLength(2)(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.minLength(2)(new Control('aa'))).toEqual(null); });
|
||||
|
||||
it('should error on short strings', () => {
|
||||
expect(Validators.minLength(2)(new Control('a'))).toEqual({
|
||||
'minlength': {'requiredLength': 2, 'actualLength': 1}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.maxLength(2)(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.maxLength(2)(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.maxLength(2)(new Control('aa'))).toEqual(null); });
|
||||
|
||||
it('should error on long strings', () => {
|
||||
expect(Validators.maxLength(2)(new Control('aaa'))).toEqual({
|
||||
'maxlength': {'requiredLength': 2, 'actualLength': 3}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pattern', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(''))).toEqual(null); });
|
||||
|
||||
it('should not error on null',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control(null))).toEqual(null); });
|
||||
|
||||
it('should not error on valid strings',
|
||||
() => { expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaAA'))).toEqual(null); });
|
||||
|
||||
it('should error on failure to match string', () => {
|
||||
expect(Validators.pattern('[a-zA-Z ]*')(new Control('aaa0'))).toEqual({
|
||||
'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('compose', () => {
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.compose(null)).toBe(null); });
|
||||
|
||||
it('should collect errors from all the validators', () => {
|
||||
var c = Validators.compose([validator('a', true), validator('b', true)]);
|
||||
expect(c(new Control(''))).toEqual({'a': true, 'b': true});
|
||||
});
|
||||
|
||||
it('should run validators left to right', () => {
|
||||
var c = Validators.compose([validator('a', 1), validator('a', 2)]);
|
||||
expect(c(new Control(''))).toEqual({'a': 2});
|
||||
});
|
||||
|
||||
it('should return null when no errors', () => {
|
||||
var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]);
|
||||
expect(c(new Control(''))).toEqual(null);
|
||||
});
|
||||
|
||||
it('should ignore nulls', () => {
|
||||
var c = Validators.compose([null, Validators.required]);
|
||||
expect(c(new Control(''))).toEqual({'required': true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('composeAsync', () => {
|
||||
function asyncValidator(expected: any /** TODO #9100 */, response: any /** TODO #9100 */) {
|
||||
return (c: any /** TODO #9100 */) => {
|
||||
var emitter = new EventEmitter();
|
||||
var res = c.value != expected ? response : null;
|
||||
|
||||
PromiseWrapper.scheduleMicrotask(() => {
|
||||
ObservableWrapper.callEmit(emitter, res);
|
||||
// this is required because of a bug in ObservableWrapper
|
||||
// where callComplete can fire before callEmit
|
||||
// remove this one the bug is fixed
|
||||
TimerWrapper.setTimeout(() => { ObservableWrapper.callComplete(emitter); }, 0);
|
||||
});
|
||||
return emitter;
|
||||
};
|
||||
}
|
||||
|
||||
it('should return null when given null',
|
||||
() => { expect(Validators.composeAsync(null)).toEqual(null); });
|
||||
|
||||
it('should collect errors from all the validators', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([
|
||||
asyncValidator('expected', {'one': true}), asyncValidator('expected', {'two': true})
|
||||
]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true, 'two': true});
|
||||
}));
|
||||
|
||||
it('should return null when no errors', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([asyncValidator('expected', {'one': true})]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('expected'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual(null);
|
||||
}));
|
||||
|
||||
it('should ignore nulls', fakeAsync(() => {
|
||||
var c = Validators.composeAsync([asyncValidator('expected', {'one': true}), null]);
|
||||
|
||||
var value: any /** TODO #9100 */ = null;
|
||||
(<Promise<any>>c(new Control('invalid'))).then(v => value = v);
|
||||
|
||||
tick(1);
|
||||
|
||||
expect(value).toEqual({'one': true});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
import {DatePipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
|
||||
import {DateWrapper} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('DatePipe', () => {
|
||||
var date: Date;
|
||||
var pipe: DatePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
date = DateWrapper.create(2015, 6, 15, 21, 3, 1);
|
||||
pipe = new DatePipe();
|
||||
});
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(DatePipe).pure).toEqual(true); });
|
||||
|
||||
// TODO(mlaval): enable tests when Intl API is no longer used, see
|
||||
// https://github.com/angular/angular/issues/3333
|
||||
// Have to restrict to Chrome as IE uses a different formatting
|
||||
if (browserDetection.supportsIntlApi && browserDetection.isChromeDesktop) {
|
||||
describe('supports', () => {
|
||||
it('should support date', () => { expect(() => pipe.transform(date)).not.toThrow(); });
|
||||
it('should support int', () => { expect(() => pipe.transform(123456789)).not.toThrow(); });
|
||||
it('should support numeric strings',
|
||||
() => { expect(() => pipe.transform('123456789')).not.toThrow(); });
|
||||
|
||||
it('should support ISO string',
|
||||
() => { expect(() => pipe.transform('2015-06-15T21:43:11Z')).not.toThrow(); });
|
||||
|
||||
it('should not support other objects', () => {
|
||||
expect(() => pipe.transform({})).toThrow();
|
||||
expect(() => pipe.transform('')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should format each component correctly', () => {
|
||||
expect(pipe.transform(date, 'y')).toEqual('2015');
|
||||
expect(pipe.transform(date, 'yy')).toEqual('15');
|
||||
expect(pipe.transform(date, 'M')).toEqual('6');
|
||||
expect(pipe.transform(date, 'MM')).toEqual('06');
|
||||
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
|
||||
expect(pipe.transform(date, 'MMMM')).toEqual('June');
|
||||
expect(pipe.transform(date, 'd')).toEqual('15');
|
||||
expect(pipe.transform(date, 'E')).toEqual('Mon');
|
||||
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
|
||||
expect(pipe.transform(date, 'H')).toEqual('21');
|
||||
expect(pipe.transform(date, 'j')).toEqual('9 PM');
|
||||
expect(pipe.transform(date, 'm')).toEqual('3');
|
||||
expect(pipe.transform(date, 's')).toEqual('1');
|
||||
expect(pipe.transform(date, 'mm')).toEqual('03');
|
||||
expect(pipe.transform(date, 'ss')).toEqual('01');
|
||||
});
|
||||
|
||||
it('should format common multi component patterns', () => {
|
||||
expect(pipe.transform(date, 'E, M/d/y')).toEqual('Mon, 6/15/2015');
|
||||
expect(pipe.transform(date, 'E, M/d')).toEqual('Mon, 6/15');
|
||||
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'yMEd')).toEqual('20156Mon15');
|
||||
expect(pipe.transform(date, 'MEd')).toEqual('6Mon15');
|
||||
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
|
||||
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'jms')).toEqual('9:03:01 PM');
|
||||
expect(pipe.transform(date, 'ms')).toEqual('31');
|
||||
expect(pipe.transform(date, 'jm')).toEqual('9:03 PM');
|
||||
});
|
||||
|
||||
it('should format with pattern aliases', () => {
|
||||
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 PM');
|
||||
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 PM');
|
||||
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
|
||||
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
|
||||
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
|
||||
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
|
||||
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
|
||||
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
|
||||
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 PM');
|
||||
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 PM');
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import {I18nPluralPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nPluralPipe', () => {
|
||||
var pipe: I18nPluralPipe;
|
||||
var mapping = {'=0': 'No messages.', '=1': 'One message.', 'other': 'There are some messages.'};
|
||||
var interpolatedMapping = {
|
||||
'=0': 'No messages.',
|
||||
'=1': 'One message.',
|
||||
'other': 'There are # messages, that is #.'
|
||||
};
|
||||
|
||||
beforeEach(() => { pipe = new I18nPluralPipe(); });
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(I18nPluralPipe).pure).toEqual(true); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return 0 text if value is 0', () => {
|
||||
var val = pipe.transform(0, mapping);
|
||||
expect(val).toEqual('No messages.');
|
||||
});
|
||||
|
||||
it('should return 1 text if value is 1', () => {
|
||||
var val = pipe.transform(1, mapping);
|
||||
expect(val).toEqual('One message.');
|
||||
});
|
||||
|
||||
it('should return other text if value is anything other than 0 or 1', () => {
|
||||
var val = pipe.transform(6, mapping);
|
||||
expect(val).toEqual('There are some messages.');
|
||||
});
|
||||
|
||||
it('should interpolate the value into the text where indicated', () => {
|
||||
var val = pipe.transform(6, interpolatedMapping);
|
||||
expect(val).toEqual('There are 6 messages, that is 6.');
|
||||
});
|
||||
|
||||
it('should use \'other\' if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), interpolatedMapping);
|
||||
expect(val).toEqual('There are messages, that is .');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
() => { expect(() => pipe.transform(0, <any>'hey')).toThrowError(); });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import {I18nSelectPipe} from '@angular/common';
|
||||
import {PipeResolver} from '@angular/compiler/src/pipe_resolver';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('I18nSelectPipe', () => {
|
||||
var pipe: I18nSelectPipe;
|
||||
var mapping = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||
|
||||
beforeEach(() => { pipe = new I18nSelectPipe(); });
|
||||
|
||||
it('should be marked as pure',
|
||||
() => { expect(new PipeResolver().resolve(I18nSelectPipe).pure).toEqual(true); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return male text if value is male', () => {
|
||||
var val = pipe.transform('male', mapping);
|
||||
expect(val).toEqual('Invite him.');
|
||||
});
|
||||
|
||||
it('should return female text if value is female', () => {
|
||||
var val = pipe.transform('female', mapping);
|
||||
expect(val).toEqual('Invite her.');
|
||||
});
|
||||
|
||||
it('should return other text if value is anything other than male or female', () => {
|
||||
var val = pipe.transform('Anything else', mapping);
|
||||
expect(val).toEqual('Invite them.');
|
||||
});
|
||||
|
||||
it('should use \'other\' if value is undefined', () => {
|
||||
var val = pipe.transform(void(0), mapping);
|
||||
expect(val).toEqual('Invite them.');
|
||||
});
|
||||
|
||||
it('should not support bad arguments',
|
||||
() => { expect(() => pipe.transform('male', <any>'hey')).toThrowError(); });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
import {Json, StringWrapper} from '../../src/facade/lang';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {JsonPipe} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('JsonPipe', () => {
|
||||
var regNewLine = '\n';
|
||||
var inceptionObj: any;
|
||||
var inceptionObjString: string;
|
||||
var pipe: JsonPipe;
|
||||
|
||||
function normalize(obj: string): string { return StringWrapper.replace(obj, regNewLine, ''); }
|
||||
|
||||
beforeEach(() => {
|
||||
inceptionObj = {dream: {dream: {dream: 'Limbo'}}};
|
||||
inceptionObjString = '{\n' +
|
||||
' "dream": {\n' +
|
||||
' "dream": {\n' +
|
||||
' "dream": "Limbo"\n' +
|
||||
' }\n' +
|
||||
' }\n' +
|
||||
'}';
|
||||
|
||||
|
||||
pipe = new JsonPipe();
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return JSON-formatted string',
|
||||
() => { expect(pipe.transform(inceptionObj)).toEqual(inceptionObjString); });
|
||||
|
||||
it('should return JSON-formatted string even when normalized', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(inceptionObjString);
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
|
||||
it('should return JSON-formatted string similar to Json.stringify', () => {
|
||||
var dream1 = normalize(pipe.transform(inceptionObj));
|
||||
var dream2 = normalize(Json.stringify(inceptionObj));
|
||||
expect(dream1).toEqual(dream2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('integration', () => {
|
||||
it('should work with mutable objects',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.createAsync(TestComp).then((fixture) => {
|
||||
let mutable: number[] = [1];
|
||||
fixture.debugElement.componentInstance.data = mutable;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1\n]');
|
||||
|
||||
mutable.push(2);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('[\n 1,\n 2\n]');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-comp', template: '{{data | json}}', pipes: [JsonPipe]})
|
||||
class TestComp {
|
||||
data: any;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import {LowerCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('LowerCasePipe', () => {
|
||||
var upper: string;
|
||||
var lower: string;
|
||||
var pipe: LowerCasePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
lower = 'something';
|
||||
upper = 'SOMETHING';
|
||||
pipe = new LowerCasePipe();
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return lowercase', () => {
|
||||
var val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
});
|
||||
|
||||
it('should lowercase when there is a new value', () => {
|
||||
var val = pipe.transform(upper);
|
||||
expect(val).toEqual(lower);
|
||||
var val2 = pipe.transform('WAT');
|
||||
expect(val2).toEqual('wat');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach,} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
|
||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('Number pipes', () => {
|
||||
// TODO(mlaval): enable tests when Intl API is no longer used, see
|
||||
// https://github.com/angular/angular/issues/3333
|
||||
// Have to restrict to Chrome as IE uses a different formatting
|
||||
if (browserDetection.supportsIntlApi && browserDetection.isChromeDesktop) {
|
||||
describe('DecimalPipe', () => {
|
||||
var pipe: DecimalPipe;
|
||||
|
||||
beforeEach(() => { pipe = new DecimalPipe(); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return correct value for numbers', () => {
|
||||
expect(pipe.transform(12345)).toEqual('12,345');
|
||||
expect(pipe.transform(123, '.2')).toEqual('123.00');
|
||||
expect(pipe.transform(1, '3.')).toEqual('001');
|
||||
expect(pipe.transform(1.1, '3.4-5')).toEqual('001.1000');
|
||||
expect(pipe.transform(1.123456, '3.4-5')).toEqual('001.12346');
|
||||
expect(pipe.transform(1.1234)).toEqual('1.123');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
|
||||
});
|
||||
});
|
||||
|
||||
describe('PercentPipe', () => {
|
||||
var pipe: PercentPipe;
|
||||
|
||||
beforeEach(() => { pipe = new PercentPipe(); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return correct value for numbers', () => {
|
||||
expect(pipe.transform(1.23)).toEqual('123%');
|
||||
expect(pipe.transform(1.2, '.2')).toEqual('120.00%');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
|
||||
});
|
||||
});
|
||||
|
||||
describe('CurrencyPipe', () => {
|
||||
var pipe: CurrencyPipe;
|
||||
|
||||
beforeEach(() => { pipe = new CurrencyPipe(); });
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return correct value for numbers', () => {
|
||||
expect(pipe.transform(123)).toEqual('USD123');
|
||||
expect(pipe.transform(12, 'EUR', false, '.2')).toEqual('EUR12.00');
|
||||
expect(pipe.transform(5.123, 'USD', false, '.0-2')).toEqual('USD5.12');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(new Object())).toThrowError(); });
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {ReplacePipe} from '@angular/common';
|
||||
import {RegExpWrapper, StringJoiner} from '../../src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('ReplacePipe', () => {
|
||||
var someNumber: number;
|
||||
var str: string;
|
||||
var pipe: ReplacePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
someNumber = 42;
|
||||
str = 'Douglas Adams';
|
||||
pipe = new ReplacePipe();
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
|
||||
it('should not support input other than strings and numbers', () => {
|
||||
expect(() => pipe.transform({}, 'Douglas', 'Hugh')).toThrow();
|
||||
expect(() => pipe.transform([1, 2, 3], 'Douglas', 'Hugh')).toThrow();
|
||||
});
|
||||
|
||||
it('should not support patterns other than strings and regular expressions', () => {
|
||||
expect(() => pipe.transform(str, <any>{}, 'Hugh')).toThrow();
|
||||
expect(() => pipe.transform(str, <any>null, 'Hugh')).toThrow();
|
||||
expect(() => pipe.transform(str, <any>123, 'Hugh')).toThrow();
|
||||
});
|
||||
|
||||
it('should not support replacements other than strings and functions', () => {
|
||||
expect(() => pipe.transform(str, 'Douglas', <any>{})).toThrow();
|
||||
expect(() => pipe.transform(str, 'Douglas', <any>null)).toThrow();
|
||||
expect(() => pipe.transform(str, 'Douglas', <any>123)).toThrow();
|
||||
});
|
||||
|
||||
it('should return a new string with the pattern replaced', () => {
|
||||
var result1 = pipe.transform(str, 'Douglas', 'Hugh');
|
||||
|
||||
var result2 = pipe.transform(str, RegExpWrapper.create('a'), '_');
|
||||
|
||||
var result3 = pipe.transform(str, RegExpWrapper.create('a', 'i'), '_');
|
||||
|
||||
var f = ((x: any) => { return 'Adams!'; });
|
||||
|
||||
var result4 = pipe.transform(str, 'Adams', f);
|
||||
|
||||
var result5 = pipe.transform(someNumber, '2', '4');
|
||||
|
||||
expect(result1).toEqual('Hugh Adams');
|
||||
expect(result2).toEqual('Dougl_s Ad_ms');
|
||||
expect(result3).toEqual('Dougl_s _d_ms');
|
||||
expect(result4).toEqual('Douglas Adams!');
|
||||
expect(result5).toEqual('44');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal';
|
||||
import {} from '@angular/core/testing/testing_internal';
|
||||
import {browserDetection} from '@angular/platform-browser/testing';
|
||||
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
|
||||
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {Component} from '@angular/core';
|
||||
import {SlicePipe} from '@angular/common';
|
||||
|
||||
export function main() {
|
||||
describe('SlicePipe', () => {
|
||||
var list: number[];
|
||||
var str: string;
|
||||
var pipe: SlicePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
list = [1, 2, 3, 4, 5];
|
||||
str = 'tuvwxyz';
|
||||
pipe = new SlicePipe();
|
||||
});
|
||||
|
||||
describe('supports', () => {
|
||||
it('should support strings', () => { expect(() => pipe.transform(str, 0)).not.toThrow(); });
|
||||
it('should support lists', () => { expect(() => pipe.transform(list, 0)).not.toThrow(); });
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform({}, 0)).toThrow(); });
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
|
||||
it('should return null if the value is null',
|
||||
() => { expect(pipe.transform(null, 1)).toBe(null); });
|
||||
|
||||
it('should return all items after START index when START is positive and END is omitted',
|
||||
() => {
|
||||
expect(pipe.transform(list, 3)).toEqual([4, 5]);
|
||||
expect(pipe.transform(str, 3)).toEqual('wxyz');
|
||||
});
|
||||
|
||||
it('should return last START items when START is negative and END is omitted', () => {
|
||||
expect(pipe.transform(list, -3)).toEqual([3, 4, 5]);
|
||||
expect(pipe.transform(str, -3)).toEqual('xyz');
|
||||
});
|
||||
|
||||
it('should return all items between START and END index when START and END are positive',
|
||||
() => {
|
||||
expect(pipe.transform(list, 1, 3)).toEqual([2, 3]);
|
||||
expect(pipe.transform(str, 1, 3)).toEqual('uv');
|
||||
});
|
||||
|
||||
it('should return all items between START and END from the end when START and END are negative',
|
||||
() => {
|
||||
expect(pipe.transform(list, -4, -2)).toEqual([2, 3]);
|
||||
expect(pipe.transform(str, -4, -2)).toEqual('wx');
|
||||
});
|
||||
|
||||
it('should return an empty value if START is greater than END', () => {
|
||||
expect(pipe.transform(list, 4, 2)).toEqual([]);
|
||||
expect(pipe.transform(str, 4, 2)).toEqual('');
|
||||
});
|
||||
|
||||
it('should return an empty value if START greater than input length', () => {
|
||||
expect(pipe.transform(list, 99)).toEqual([]);
|
||||
expect(pipe.transform(str, 99)).toEqual('');
|
||||
});
|
||||
|
||||
// Makes Edge to disconnect when running the full unit test campaign
|
||||
// TODO: remove when issue is solved: https://github.com/angular/angular/issues/4756
|
||||
if (!browserDetection.isEdge) {
|
||||
it('should return entire input if START is negative and greater than input length', () => {
|
||||
expect(pipe.transform(list, -99)).toEqual([1, 2, 3, 4, 5]);
|
||||
expect(pipe.transform(str, -99)).toEqual('tuvwxyz');
|
||||
});
|
||||
|
||||
it('should not modify the input list', () => {
|
||||
expect(pipe.transform(list, 2)).toEqual([3, 4, 5]);
|
||||
expect(list).toEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
describe('integration', () => {
|
||||
it('should work with mutable arrays',
|
||||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||
tcb.createAsync(TestComp).then((fixture) => {
|
||||
let mutable: number[] = [1, 2];
|
||||
fixture.debugElement.componentInstance.data = mutable;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('2');
|
||||
|
||||
mutable.push(3);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('2,3');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'test-comp', template: '{{(data | slice:1).join(",") }}', pipes: [SlicePipe]})
|
||||
class TestComp {
|
||||
data: any;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import {UpperCasePipe} from '@angular/common';
|
||||
import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('UpperCasePipe', () => {
|
||||
var upper: string;
|
||||
var lower: string;
|
||||
var pipe: UpperCasePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
lower = 'something';
|
||||
upper = 'SOMETHING';
|
||||
pipe = new UpperCasePipe();
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
|
||||
it('should return uppercase', () => {
|
||||
var val = pipe.transform(lower);
|
||||
expect(val).toEqual(upper);
|
||||
});
|
||||
|
||||
it('should uppercase when there is a new value', () => {
|
||||
var val = pipe.transform(lower);
|
||||
expect(val).toEqual(upper);
|
||||
var val2 = pipe.transform('wat');
|
||||
expect(val2).toEqual('WAT');
|
||||
});
|
||||
|
||||
it('should not support other objects',
|
||||
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref';
|
||||
import {SpyObject, proxy} from '@angular/core/testing/testing_internal';
|
||||
|
||||
export class SpyChangeDetectorRef extends SpyObject {
|
||||
constructor() {
|
||||
super(ChangeDetectorRef);
|
||||
this.spy('markForCheck');
|
||||
}
|
||||
}
|
||||
|
||||
export class SpyNgControl extends SpyObject {}
|
||||
|
||||
export class SpyValueAccessor extends SpyObject { writeValue: any; }
|
@ -1,2 +0,0 @@
|
||||
export {SpyLocation} from './testing/location_mock';
|
||||
export {MockLocationStrategy} from './testing/mock_location_strategy';
|
@ -1,120 +0,0 @@
|
||||
import {EventEmitter, Injectable} from '@angular/core';
|
||||
|
||||
import {Location} from '../index';
|
||||
import {ObservableWrapper} from '../src/facade/async';
|
||||
import {LocationStrategy} from '../src/location/location_strategy';
|
||||
|
||||
|
||||
/**
|
||||
* A spy for {@link Location} that allows tests to fire simulated location events.
|
||||
*/
|
||||
@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();
|
||||
/** @internal */
|
||||
_baseHref: string = '';
|
||||
/** @internal */
|
||||
_platformStrategy: LocationStrategy = null;
|
||||
|
||||
setInitialPath(url: string) { this._history[this._historyIndex].path = url; }
|
||||
|
||||
setBaseHref(url: string) { this._baseHref = url; }
|
||||
|
||||
path(): string { return this._history[this._historyIndex].path; }
|
||||
|
||||
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||
var givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||
var currPath =
|
||||
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
||||
|
||||
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||
}
|
||||
|
||||
simulateUrlPop(pathname: string) {
|
||||
ObservableWrapper.callEmit(this._subject, {'url': pathname, 'pop': true});
|
||||
}
|
||||
|
||||
simulateHashChange(pathname: string) {
|
||||
// Because we don't prevent the native event, the browser will independently update the path
|
||||
this.setInitialPath(pathname);
|
||||
this.urlChanges.push('hash: ' + pathname);
|
||||
ObservableWrapper.callEmit(this._subject, {'url': pathname, 'pop': true, 'type': 'hashchange'});
|
||||
}
|
||||
|
||||
prepareExternalUrl(url: string): string {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this._baseHref + url;
|
||||
}
|
||||
|
||||
go(path: string, query: string = '') {
|
||||
path = this.prepareExternalUrl(path);
|
||||
|
||||
if (this._historyIndex > 0) {
|
||||
this._history.splice(this._historyIndex + 1);
|
||||
}
|
||||
this._history.push(new LocationState(path, query));
|
||||
this._historyIndex = this._history.length - 1;
|
||||
|
||||
var locationState = this._history[this._historyIndex - 1];
|
||||
if (locationState.path == path && locationState.query == query) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.urlChanges.push(url);
|
||||
}
|
||||
|
||||
replaceState(path: string, query: string = '') {
|
||||
path = this.prepareExternalUrl(path);
|
||||
|
||||
var history = this._history[this._historyIndex];
|
||||
if (history.path == path && history.query == query) {
|
||||
return;
|
||||
}
|
||||
|
||||
history.path = path;
|
||||
history.query = query;
|
||||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.urlChanges.push('replace: ' + url);
|
||||
}
|
||||
|
||||
forward() {
|
||||
if (this._historyIndex < (this._history.length - 1)) {
|
||||
this._historyIndex++;
|
||||
ObservableWrapper.callEmit(this._subject, {'url': this.path(), 'pop': true});
|
||||
}
|
||||
}
|
||||
|
||||
back() {
|
||||
if (this._historyIndex > 0) {
|
||||
this._historyIndex--;
|
||||
ObservableWrapper.callEmit(this._subject, {'url': this.path(), 'pop': true});
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(
|
||||
onNext: (value: any) => void, onThrow: (error: any) => void = null,
|
||||
onReturn: () => void = null): Object {
|
||||
return ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||
}
|
||||
|
||||
normalize(url: string): string { return null; }
|
||||
}
|
||||
|
||||
class LocationState {
|
||||
path: string;
|
||||
query: string;
|
||||
constructor(path: string, query: string) {
|
||||
this.path = path;
|
||||
this.query = query;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../../dist/packages-dist/common/esm",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core"]
|
||||
},
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"target": "es2015"
|
||||
},
|
||||
"files": [
|
||||
"index.ts",
|
||||
"testing.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
]
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../../dist/packages-dist/common/",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core/"]
|
||||
},
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"target": "es5"
|
||||
},
|
||||
"files": [
|
||||
"index.ts",
|
||||
"testing.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
]
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
# Angular Template Compiler
|
||||
|
||||
Angular applications are built with templates, which may be `.html` or `.css` files,
|
||||
or may be inline `template` attributes on Decorators like `@Component`.
|
||||
|
||||
These templates are compiled into executable JS at application runtime (except in `interpretation` mode).
|
||||
This compilation can occur on the client, but it results in slower bootstrap time, and also
|
||||
requires that the compiler be included in the code downloaded to the client.
|
||||
|
||||
You can produce smaller, faster applications by running Angular's compiler as a build step,
|
||||
and then downloading only the executable JS to the client.
|
||||
|
||||
## Install and use
|
||||
|
||||
```
|
||||
# First install angular, see https://github.com/angular/angular/blob/master/CHANGELOG.md#200-rc0-2016-05-02
|
||||
$ npm install @angular/compiler-cli typescript@next @angular/platform-server @angular/compiler
|
||||
# Optional sanity check, make sure TypeScript can compile.
|
||||
$ ./node_modules/.bin/tsc -p path/to/project
|
||||
# ngc is a drop-in replacement for tsc.
|
||||
$ ./node_modules/.bin/ngc -p path/to/project
|
||||
```
|
||||
|
||||
In order to write a `bootstrap` that imports the generated code, you should first write your
|
||||
top-level component, and run `ngc` once to produce a generated `.ngfactory.ts` file.
|
||||
Then you can add an import statement in the `bootstrap` allowing you to bootstrap off the
|
||||
generated code:
|
||||
|
||||
```typescript
|
||||
import {ComponentResolver, ReflectiveInjector, coreBootstrap} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS, browserPlatform} from '@angular/platform-browser';
|
||||
|
||||
import {MyComponentNgFactory} from './mycomponent.ngfactory';
|
||||
|
||||
const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, browserPlatform().injector);
|
||||
coreBootstrap(MyComponentNgFactory, appInjector);
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The `tsconfig.json` file may contain an additional configuration block:
|
||||
```
|
||||
"angularCompilerOptions": {
|
||||
"genDir": ".",
|
||||
"debug": true
|
||||
}
|
||||
```
|
||||
|
||||
### `genDir`
|
||||
|
||||
the `genDir` option controls the path (relative to `tsconfig.json`) where the generated file tree
|
||||
will be written. If `genDir` is not set, then the code will be generated in the source tree, next
|
||||
to your original sources. More options may be added as we implement more features.
|
||||
|
||||
We recommend you avoid checking generated files into version control. This permits a state where
|
||||
the generated files in the repository were created from sources that were never checked in,
|
||||
making it impossible to reproduce the current state. Also, your changes will effectively appear
|
||||
twice in code reviews, with the generated version inscrutible by the reviewer.
|
||||
|
||||
In TypeScript 1.8, the generated sources will have to be written alongside your originals,
|
||||
so set `genDir` to the same location as your files (typicially the same as `rootDir`).
|
||||
Add `**/*.ngfactory.ts` to your `.gitignore` or other mechanism for your version control system.
|
||||
|
||||
In TypeScript 1.9 and above, you can add a generated folder into your application,
|
||||
such as `codegen`. Using the `rootDirs` option, you can allow relative imports like
|
||||
`import {} from './foo.ngfactory'` even though the `src` and `codegen` trees are distinct.
|
||||
Add `**/codegen` to your `.gitignore` or similar.
|
||||
|
||||
Note that in the second option, TypeScript will emit the code into two parallel directories
|
||||
as well. This is by design, see https://github.com/Microsoft/TypeScript/issues/8245.
|
||||
This makes the configuration of your runtime module loader more complex, so we don't recommend
|
||||
this option yet.
|
||||
|
||||
### `debug`
|
||||
|
||||
Set the `debug` option to true to generate debug information in the generate files.
|
||||
Default to `false`.
|
||||
|
||||
See the example in the `test/` directory for a working example.
|
||||
|
||||
## Compiler CLI
|
||||
|
||||
This program mimics the TypeScript tsc command line. It accepts a `-p` flag which points to a
|
||||
`tsconfig.json` file, or a directory containing one.
|
||||
|
||||
This CLI is intended for demos, prototyping, or for users with simple build systems
|
||||
that run bare `tsc`.
|
||||
|
||||
Users with a build system should expect an Angular 2 template plugin. Such a plugin would be
|
||||
based on the `index.ts` in this directory, but should share the TypeScript compiler instance
|
||||
with the one already used in the plugin for TypeScript typechecking and emit.
|
||||
|
||||
## Design
|
||||
At a high level, this program
|
||||
- collects static metadata about the sources using the `tsc-wrapped` package in angular2
|
||||
- uses the `OfflineCompiler` from `angular2/src/compiler/compiler` to codegen additional `.ts` files
|
||||
- these `.ts` files are written to the `genDir` path, then compiled together with the application.
|
||||
|
||||
## For developers
|
||||
```
|
||||
# Build angular2 and the compiler
|
||||
./build.sh
|
||||
# Copy over the package so we can test the compiler tests
|
||||
$ cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped
|
||||
# Run the test once
|
||||
# (First edit the LINKABLE_PKGS to use npm link instead of npm install)
|
||||
$ ./scripts/ci-lite/offline_compiler_test.sh
|
||||
# Keep a package fresh in watch mode
|
||||
./node_modules/.bin/tsc -p modules/@angular/compiler/tsconfig-es5.json -w
|
||||
# Iterate on the test
|
||||
cd /tmp/wherever/e2e_test.1464388257/
|
||||
./node_modules/.bin/ngc
|
||||
./node_modules/.bin/jasmine test/*_spec.js
|
||||
```
|
@ -1,5 +0,0 @@
|
||||
export {CodeGenerator} from './src/codegen';
|
||||
export {NodeReflectorHostContext, ReflectorHost, ReflectorHostContext} from './src/reflector_host';
|
||||
export {StaticReflector, StaticReflectorHost, StaticSymbol} from './src/static_reflector';
|
||||
|
||||
export * from '@angular/tsc-wrapped';
|
@ -1 +0,0 @@
|
||||
<div></div>
|
@ -1,30 +0,0 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: '<div></div>',
|
||||
})
|
||||
export class MyComp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'next-comp',
|
||||
templateUrl: './multiple_components.html',
|
||||
})
|
||||
export class NextComp {
|
||||
}
|
||||
|
||||
// Verify that exceptions from DirectiveResolver don't propagate
|
||||
export function NotADirective(c: any): void {}
|
||||
@NotADirective
|
||||
export class HasCustomDecorator {
|
||||
}
|
||||
|
||||
// Verify that custom decorators have metadata collected, eg Ionic
|
||||
export function Page(c: any): (f: Function) => void {
|
||||
return NotADirective;
|
||||
}
|
||||
|
||||
@Page({template: 'Ionic template'})
|
||||
export class AnIonicPage {
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'animate-cmp',
|
||||
animations: [trigger(
|
||||
'openClose',
|
||||
[
|
||||
state('*', style({height: AUTO_STYLE, color: 'black', borderColor: 'black'})),
|
||||
state('closed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})),
|
||||
state('open', style({height: AUTO_STYLE, borderColor: 'green', color: 'green'})),
|
||||
transition('* => *', animate(500))
|
||||
])],
|
||||
template: `
|
||||
<button (click)="setAsOpen()">Open</button>
|
||||
<button (click)="setAsClosed()">Closed</button>
|
||||
<button (click)="setAsSomethingElse()">Something Else</button>
|
||||
<hr />
|
||||
<div @openClose="stateExpression">
|
||||
Look at this box
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class AnimateCmp {
|
||||
stateExpression: string;
|
||||
constructor() { this.setAsClosed(); }
|
||||
setAsSomethingElse() { this.stateExpression = 'something'; }
|
||||
setAsOpen() { this.stateExpression = 'open'; }
|
||||
setAsClosed() { this.stateExpression = 'closed'; }
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
@import './shared.css';
|
||||
|
||||
.green { color: green }
|
@ -1,4 +0,0 @@
|
||||
<div [attr.array]="[0]" [attr.map]="{a:1}" title="translate me" i18n-title="meaning|desc">{{ctxProp}}</div>
|
||||
<form><input type="button" [(ngModel)]="ctxProp"/></form>
|
||||
<my-comp *ngIf="ctxBool"></my-comp>
|
||||
<div *ngFor="let x of ctxArr" [attr.value]="x"></div>
|
@ -1,18 +0,0 @@
|
||||
import {FORM_DIRECTIVES, NgFor, NgIf} from '@angular/common';
|
||||
import {Component, Inject} from '@angular/core';
|
||||
|
||||
import {MyComp} from './a/multiple_components';
|
||||
|
||||
@Component({
|
||||
selector: 'basic',
|
||||
templateUrl: './basic.html',
|
||||
styles: ['.red { color: red }'],
|
||||
styleUrls: ['./basic.css'],
|
||||
directives: [MyComp, FORM_DIRECTIVES, NgIf, NgFor]
|
||||
})
|
||||
export class Basic {
|
||||
ctxProp: string;
|
||||
ctxBool: boolean;
|
||||
ctxArr: any[] = [];
|
||||
constructor() { this.ctxProp = 'initialValue'; }
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import {ReflectiveInjector, coreBootstrap} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS, browserPlatform} from '@angular/platform-browser';
|
||||
|
||||
import {Basic} from './basic';
|
||||
import {BasicNgFactory} from './basic.ngfactory';
|
||||
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, browserPlatform().injector);
|
||||
coreBootstrap(BasicNgFactory, appInjector);
|
@ -1,2 +0,0 @@
|
||||
// Verify we don't try to extract metadata for .d.ts files
|
||||
export declare var a: string;
|
@ -1,31 +0,0 @@
|
||||
import * as common from '@angular/common';
|
||||
import {Component, Inject, OpaqueToken} from '@angular/core';
|
||||
|
||||
import {wrapInArray} from './funcs';
|
||||
|
||||
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
|
||||
|
||||
@Component({
|
||||
selector: 'comp-providers',
|
||||
template: '',
|
||||
providers: [
|
||||
{provide: 'strToken', useValue: 'strValue'},
|
||||
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
|
||||
{provide: 'reference', useValue: common.NgIf},
|
||||
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
|
||||
]
|
||||
})
|
||||
export class CompWithProviders {
|
||||
constructor(@Inject('strToken') public ctxProp: string) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cmp-reference',
|
||||
template: `
|
||||
<input #a>{{a.value}}
|
||||
<div *ngIf="true">{{a.value}}</div>
|
||||
`,
|
||||
directives: [wrapInArray(common.NgIf)]
|
||||
})
|
||||
export class CompWithReferences {
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export function wrapInArray(value: any): any[] {
|
||||
return [value];
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-with-proj', template: '<ng-content></ng-content>'})
|
||||
export class CompWithProjection {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'main',
|
||||
template: '<comp-with-proj><span greeting="Hello world!"></span></comp-with-proj>',
|
||||
directives: [CompWithProjection]
|
||||
})
|
||||
export class MainComp {
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import {Component, QueryList, ViewChild, ViewChildren} from '@angular/core';
|
||||
|
||||
@Component({selector: 'comp-for-child-query', template: 'child'})
|
||||
export class CompForChildQuery {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'comp-with-child-query',
|
||||
template: '<comp-for-child-query></comp-for-child-query>',
|
||||
directives: [CompForChildQuery]
|
||||
})
|
||||
export class CompWithChildQuery {
|
||||
@ViewChild(CompForChildQuery) child: CompForChildQuery;
|
||||
@ViewChildren(CompForChildQuery) children: QueryList<CompForChildQuery>;
|
||||
}
|
@ -1 +0,0 @@
|
||||
.blue { color: blue }
|
@ -1,76 +0,0 @@
|
||||
require('reflect-metadata');
|
||||
require('zone.js/dist/zone-node.js');
|
||||
require('zone.js/dist/long-stack-trace-zone.js');
|
||||
|
||||
import {AnimateCmpNgFactory} from '../src/animate.ngfactory';
|
||||
import {ReflectiveInjector, DebugElement, getDebugNode, lockRunMode} from '@angular/core';
|
||||
import {serverPlatform} from '@angular/platform-server';
|
||||
import {BROWSER_APP_PROVIDERS} from '@angular/platform-browser';
|
||||
|
||||
|
||||
// Need to lock the mode explicitely as this test is not using Angular's testing framework.
|
||||
lockRunMode();
|
||||
|
||||
describe('template codegen output', () => {
|
||||
function findTargetElement(elm: DebugElement): DebugElement {
|
||||
// the open-close-container is a child of the main container
|
||||
// if the template changes then please update the location below
|
||||
return elm.children[4];
|
||||
}
|
||||
|
||||
it('should apply the animate states to the element', (done) => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var comp = AnimateCmpNgFactory.create(appInjector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
|
||||
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
|
||||
comp.instance.setAsOpen();
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(targetDebugElement.styles['height']).toEqual(null);
|
||||
expect(targetDebugElement.styles['borderColor']).toEqual('green');
|
||||
expect(targetDebugElement.styles['color']).toEqual('green');
|
||||
|
||||
comp.instance.setAsClosed();
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(targetDebugElement.styles['height']).toEqual('0px');
|
||||
expect(targetDebugElement.styles['borderColor']).toEqual('maroon');
|
||||
expect(targetDebugElement.styles['color']).toEqual('maroon');
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should apply the default animate state to the element', (done) => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var comp = AnimateCmpNgFactory.create(appInjector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
|
||||
var targetDebugElement = findTargetElement(<DebugElement>debugElement);
|
||||
|
||||
comp.instance.setAsSomethingElse();
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(targetDebugElement.styles['height']).toEqual(null);
|
||||
expect(targetDebugElement.styles['borderColor']).toEqual('black');
|
||||
expect(targetDebugElement.styles['color']).toEqual('black');
|
||||
|
||||
comp.instance.setAsClosed();
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(targetDebugElement.styles['height']).not.toEqual(null);
|
||||
expect(targetDebugElement.styles['borderColor']).not.toEqual('grey');
|
||||
expect(targetDebugElement.styles['color']).not.toEqual('grey');
|
||||
done();
|
||||
}, 0);
|
||||
}, 0);
|
||||
});
|
||||
});
|
@ -1,76 +0,0 @@
|
||||
// Only needed to satisfy the check in core/src/util/decorators.ts
|
||||
// TODO(alexeagle): maybe remove that check?
|
||||
require('reflect-metadata');
|
||||
|
||||
require('zone.js/dist/zone-node.js');
|
||||
require('zone.js/dist/long-stack-trace-zone.js');
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {BasicNgFactory} from '../src/basic.ngfactory';
|
||||
import {MyComp} from '../src/a/multiple_components';
|
||||
import {ReflectiveInjector, DebugElement, getDebugNode, lockRunMode} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS} from '@angular/platform-browser';
|
||||
import {serverPlatform} from '@angular/platform-server';
|
||||
|
||||
// Need to lock the mode explicitely as this test is not using Angular's testing framework.
|
||||
lockRunMode();
|
||||
|
||||
describe('template codegen output', () => {
|
||||
const outDir = 'src';
|
||||
|
||||
it('should lower Decorators without reflect-metadata', () => {
|
||||
const jsOutput = path.join(outDir, 'basic.js');
|
||||
expect(fs.existsSync(jsOutput)).toBeTruthy();
|
||||
expect(fs.readFileSync(jsOutput, {encoding: 'utf-8'})).not.toContain('Reflect.decorate');
|
||||
});
|
||||
|
||||
it('should produce metadata.json outputs', () => {
|
||||
const metadataOutput = path.join(outDir, 'basic.metadata.json');
|
||||
expect(fs.existsSync(metadataOutput)).toBeTruthy();
|
||||
const output = fs.readFileSync(metadataOutput, {encoding: 'utf-8'});
|
||||
expect(output).toContain('"decorators":');
|
||||
expect(output).toContain('"module":"@angular/core","name":"Component"');
|
||||
});
|
||||
|
||||
it('should write .d.ts files', () => {
|
||||
const dtsOutput = path.join(outDir, 'basic.d.ts');
|
||||
expect(fs.existsSync(dtsOutput)).toBeTruthy();
|
||||
expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic');
|
||||
});
|
||||
|
||||
it('should be able to create the basic component', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var comp = BasicNgFactory.create(appInjector);
|
||||
expect(comp.instance).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support ngIf', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var comp = BasicNgFactory.create(appInjector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
expect(debugElement.children.length).toBe(2);
|
||||
|
||||
comp.instance.ctxBool = true;
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
expect(debugElement.children.length).toBe(3);
|
||||
expect(debugElement.children[2].injector.get(MyComp)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support ngFor', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var comp = BasicNgFactory.create(appInjector);
|
||||
var debugElement = <DebugElement>getDebugNode(comp.location.nativeElement);
|
||||
expect(debugElement.children.length).toBe(2);
|
||||
|
||||
// test NgFor
|
||||
comp.instance.ctxArr = [1, 2];
|
||||
comp.changeDetectorRef.detectChanges();
|
||||
expect(debugElement.children.length).toBe(4);
|
||||
expect(debugElement.children[2].attributes['value']).toBe('1');
|
||||
expect(debugElement.children[3].attributes['value']).toBe('2');
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
// Only needed to satisfy the check in core/src/util/decorators.ts
|
||||
// TODO(alexeagle): maybe remove that check?
|
||||
require('reflect-metadata');
|
||||
require('zone.js/dist/zone-node.js');
|
||||
require('zone.js/dist/long-stack-trace-zone.js');
|
||||
let serializer = require('@angular/compiler/src/i18n/xmb_serializer.js');
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
describe('template i18n extraction output', () => {
|
||||
const outDir = '';
|
||||
|
||||
it('should extract i18n messages', () => {
|
||||
const xmbOutput = path.join(outDir, 'messages.xmb');
|
||||
expect(fs.existsSync(xmbOutput)).toBeTruthy();
|
||||
const xmb = fs.readFileSync(xmbOutput, {encoding: 'utf-8'});
|
||||
const res = serializer.deserializeXmb(xmb);
|
||||
const keys = Object.keys(res.messages);
|
||||
expect(keys.length).toEqual(1);
|
||||
expect(res.errors.length).toEqual(0);
|
||||
expect(res.messages[keys[0]][0].value).toEqual('translate me');
|
||||
});
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
import {DebugElement, ReflectiveInjector, getDebugNode, lockRunMode} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS, By} from '@angular/platform-browser';
|
||||
import {serverPlatform} from '@angular/platform-server';
|
||||
|
||||
import {CompWithProjection} from '../src/projection';
|
||||
import {MainCompNgFactory} from '../src/projection.ngfactory';
|
||||
|
||||
// Need to lock the mode explicitely as this test is not using Angular's testing framework.
|
||||
lockRunMode();
|
||||
|
||||
describe('content projection', () => {
|
||||
it('should support basic content projection', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var mainComp = MainCompNgFactory.create(appInjector);
|
||||
|
||||
var debugElement = <DebugElement>getDebugNode(mainComp.location.nativeElement);
|
||||
var compWithProjection = debugElement.query(By.directive(CompWithProjection));
|
||||
expect(compWithProjection.children.length).toBe(1);
|
||||
expect(compWithProjection.children[0].attributes['greeting']).toEqual('Hello world!');
|
||||
});
|
||||
});
|
@ -1,34 +0,0 @@
|
||||
import {DebugElement, QueryList, ReflectiveInjector, getDebugNode, lockRunMode} from '@angular/core';
|
||||
import {BROWSER_APP_PROVIDERS, By} from '@angular/platform-browser';
|
||||
import {serverPlatform} from '@angular/platform-server';
|
||||
|
||||
import {CompForChildQuery, CompWithChildQuery} from '../src/queries';
|
||||
import {CompWithChildQueryNgFactory} from '../src/queries.ngfactory';
|
||||
|
||||
describe('child queries', () => {
|
||||
it('should support compiling child queries', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var childQueryComp = CompWithChildQueryNgFactory.create(appInjector);
|
||||
|
||||
var debugElement = <DebugElement>getDebugNode(childQueryComp.location.nativeElement);
|
||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
expect(childQueryComp.instance.child).toBeDefined();
|
||||
expect(childQueryComp.instance.child instanceof CompForChildQuery).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
it('should support compiling children queries', () => {
|
||||
const appInjector =
|
||||
ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, serverPlatform().injector);
|
||||
var childQueryComp = CompWithChildQueryNgFactory.create(appInjector);
|
||||
|
||||
var debugElement = <DebugElement>getDebugNode(childQueryComp.location.nativeElement);
|
||||
var compWithChildren = debugElement.query(By.directive(CompWithChildQuery));
|
||||
|
||||
childQueryComp.changeDetectorRef.detectChanges();
|
||||
|
||||
expect(childQueryComp.instance.children).toBeDefined();
|
||||
expect(childQueryComp.instance.children instanceof QueryList).toBe(true);
|
||||
});
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
{
|
||||
"angularCompilerOptions": {
|
||||
// For TypeScript 1.8, we have to lay out generated files
|
||||
// in the same source directory with your code.
|
||||
"genDir": ".",
|
||||
"debug": true
|
||||
},
|
||||
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"rootDir": "",
|
||||
"declaration": true,
|
||||
"lib": ["es6", "dom"],
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
{
|
||||
"name": "@angular/compiler-cli",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "Execute angular2 template compiler in nodejs.",
|
||||
"main": "index.js",
|
||||
"typings": "index.d.ts",
|
||||
"bin": {
|
||||
"ngc": "./src/main.js",
|
||||
"ng-xi18n": "./src/extract_i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/tsc-wrapped": "^0.1.0",
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"parse5": "1.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^1.9.0-dev",
|
||||
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
||||
"@angular/platform-server": "0.0.0-PLACEHOLDER",
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"compiler"
|
||||
],
|
||||
"contributors": [
|
||||
"Tobias Bosch <tbosch@google.com> (https://angular.io/)",
|
||||
"Alex Eagle <alexeagle@google.com> (https://angular.io/)"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular/issues"
|
||||
},
|
||||
"homepage": "https://github.com/angular/angular/tree/master/tools/compiler-cli"
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
/**
|
||||
* Transform template html and css into executable code.
|
||||
* Intended to be used in a build step.
|
||||
*/
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation, lockRunMode} from '@angular/core';
|
||||
import {AngularCompilerOptions} from '@angular/tsc-wrapped';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CompileMetadataResolver, DirectiveNormalizer, DomElementSchemaRegistry, HtmlParser, Lexer, Parser, StyleCompiler, TemplateParser, TypeScriptEmitter, ViewCompiler} from './compiler_private';
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
const PREAMBLE = `/**
|
||||
* This file is generated by the Angular 2 template compiler.
|
||||
* Do not edit.
|
||||
*/
|
||||
/* tslint:disable */
|
||||
|
||||
`;
|
||||
|
||||
export class CodeGenerator {
|
||||
constructor(
|
||||
private options: AngularCompilerOptions, private program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private resolver: CompileMetadataResolver, private compiler: compiler.OfflineCompiler,
|
||||
private reflectorHost: ReflectorHost) {
|
||||
lockRunMode();
|
||||
}
|
||||
|
||||
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
const directiveType = metadata.type.runtime;
|
||||
const directives = this.resolver.getViewDirectivesMetadata(directiveType);
|
||||
return Promise.all(directives.map(d => this.compiler.normalizeDirectiveMetadata(d)))
|
||||
.then(normalizedDirectives => {
|
||||
const pipes = this.resolver.getViewPipesMetadata(directiveType);
|
||||
return new compiler.NormalizedComponentWithViewDirectives(
|
||||
metadata, normalizedDirectives, pipes);
|
||||
});
|
||||
};
|
||||
return Promise.all(metadatas.map(normalize))
|
||||
.then(
|
||||
normalizedCompWithDirectives =>
|
||||
this.compiler.compileTemplates(normalizedCompWithDirectives));
|
||||
}
|
||||
|
||||
private readComponents(absSourcePath: string) {
|
||||
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||
if (!moduleMetadata) {
|
||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||
return result;
|
||||
}
|
||||
const metadata = moduleMetadata['metadata'];
|
||||
const symbols = metadata && Object.keys(metadata);
|
||||
if (!symbols || !symbols.length) {
|
||||
return result;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
if (metadata[symbol] && metadata[symbol].__symbolic == 'error') {
|
||||
// Ignore symbols that are only included to record error information.
|
||||
continue;
|
||||
}
|
||||
const staticType = this.reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||
let directive: compiler.CompileDirectiveMetadata;
|
||||
directive = this.resolver.maybeGetDirectiveMetadata(<any>staticType);
|
||||
|
||||
if (!directive || !directive.isComponent) {
|
||||
continue;
|
||||
}
|
||||
result.push(this.compiler.normalizeDirectiveMetadata(directive));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Write codegen in a directory structure matching the sources.
|
||||
private calculateEmitPath(filePath: string) {
|
||||
let root = this.options.basePath;
|
||||
for (let eachRootDir of this.options.rootDirs || []) {
|
||||
if (this.options.trace) {
|
||||
console.log(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
||||
}
|
||||
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
||||
root = eachRootDir;
|
||||
}
|
||||
}
|
||||
|
||||
return path.join(this.options.genDir, path.relative(root, filePath));
|
||||
}
|
||||
|
||||
// TODO(tbosch): add a cache for shared css files
|
||||
// TODO(tbosch): detect cycles!
|
||||
private generateStylesheet(filepath: string, shim: boolean): Promise<any> {
|
||||
return this.compiler.loadAndCompileStylesheet(filepath, shim, '.ts')
|
||||
.then((sourceWithImports) => {
|
||||
const emitPath = this.calculateEmitPath(sourceWithImports.source.moduleUrl);
|
||||
// TODO(alexeagle): should include the sourceFile to the WriteFileCallback
|
||||
this.host.writeFile(emitPath, PREAMBLE + sourceWithImports.source.source, false);
|
||||
return Promise.all(
|
||||
sourceWithImports.importedUrls.map(url => this.generateStylesheet(url, shim)));
|
||||
});
|
||||
}
|
||||
|
||||
codegen(): Promise<any> {
|
||||
let stylesheetPromises: Promise<any>[] = [];
|
||||
const generateOneFile = (absSourcePath: string) =>
|
||||
Promise.all(this.readComponents(absSourcePath))
|
||||
.then((metadatas: compiler.CompileDirectiveMetadata[]) => {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
return;
|
||||
}
|
||||
metadatas.forEach((metadata) => {
|
||||
let stylesheetPaths = metadata && metadata.template && metadata.template.styleUrls;
|
||||
if (stylesheetPaths) {
|
||||
stylesheetPaths.forEach((path) => {
|
||||
stylesheetPromises.push(this.generateStylesheet(
|
||||
path, metadata.template.encapsulation === ViewEncapsulation.Emulated));
|
||||
});
|
||||
}
|
||||
});
|
||||
return this.generateSource(metadatas);
|
||||
})
|
||||
.then(generated => {
|
||||
if (generated) {
|
||||
const sourceFile = this.program.getSourceFile(absSourcePath);
|
||||
const emitPath = this.calculateEmitPath(generated.moduleUrl);
|
||||
this.host.writeFile(
|
||||
emitPath, PREAMBLE + generated.source, false, () => {}, [sourceFile]);
|
||||
}
|
||||
})
|
||||
.catch((e) => { console.error(e.stack); });
|
||||
var compPromises = this.program.getSourceFiles()
|
||||
.map(sf => sf.fileName)
|
||||
.filter(f => !GENERATED_FILES.test(f))
|
||||
.map(generateOneFile);
|
||||
return Promise.all(stylesheetPromises.concat(compPromises));
|
||||
}
|
||||
|
||||
static create(
|
||||
options: AngularCompilerOptions, program: ts.Program, compilerHost: ts.CompilerHost,
|
||||
reflectorHostContext?: ReflectorHostContext): CodeGenerator {
|
||||
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser = new HtmlParser();
|
||||
const config = new compiler.CompilerConfig({
|
||||
genDebugInfo: options.debug === true,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false,
|
||||
platformDirectives: [],
|
||||
platformPipes: []
|
||||
});
|
||||
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
|
||||
const parser = new Parser(new Lexer());
|
||||
const tmplParser = new TemplateParser(
|
||||
parser, new DomElementSchemaRegistry(), htmlParser,
|
||||
/*console*/ null, []);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||
new TypeScriptEmitter(reflectorHost), xhr);
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||
|
||||
return new CodeGenerator(
|
||||
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost);
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
import {__compiler_private__ as _c} from '@angular/compiler';
|
||||
|
||||
export type AssetUrl = _c.AssetUrl;
|
||||
export var AssetUrl: typeof _c.AssetUrl = _c.AssetUrl;
|
||||
|
||||
export type ImportGenerator = _c.ImportGenerator;
|
||||
export var ImportGenerator: typeof _c.ImportGenerator = _c.ImportGenerator;
|
||||
|
||||
export type CompileMetadataResolver = _c.CompileMetadataResolver;
|
||||
export var CompileMetadataResolver: typeof _c.CompileMetadataResolver = _c.CompileMetadataResolver;
|
||||
|
||||
export type HtmlParser = _c.HtmlParser;
|
||||
export var HtmlParser: typeof _c.HtmlParser = _c.HtmlParser;
|
||||
|
||||
export type I18nHtmlParser = _c.I18nHtmlParser;
|
||||
export var I18nHtmlParser: typeof _c.I18nHtmlParser = _c.I18nHtmlParser;
|
||||
|
||||
export type MessageExtractor = _c.MessageExtractor;
|
||||
export var MessageExtractor: typeof _c.MessageExtractor = _c.MessageExtractor;
|
||||
|
||||
export type ExtractionResult = _c.ExtractionResult;
|
||||
export var ExtractionResult: typeof _c.ExtractionResult = _c.ExtractionResult;
|
||||
|
||||
export type Message = _c.Message;
|
||||
export var Message: typeof _c.Message = _c.Message;
|
||||
|
||||
export var removeDuplicates: typeof _c.removeDuplicates = _c.removeDuplicates;
|
||||
export var serializeXmb: typeof _c.serializeXmb = _c.serializeXmb;
|
||||
export var deserializeXmb: typeof _c.deserializeXmb = _c.deserializeXmb;
|
||||
|
||||
export type ParseError = _c.ParseError;
|
||||
export var ParseError: typeof _c.ParseError = _c.ParseError;
|
||||
|
||||
export type DirectiveNormalizer = _c.DirectiveNormalizer;
|
||||
export var DirectiveNormalizer: typeof _c.DirectiveNormalizer = _c.DirectiveNormalizer;
|
||||
|
||||
export type Lexer = _c.Lexer;
|
||||
export var Lexer: typeof _c.Lexer = _c.Lexer;
|
||||
|
||||
export type Parser = _c.Parser;
|
||||
export var Parser: typeof _c.Parser = _c.Parser;
|
||||
|
||||
export type TemplateParser = _c.TemplateParser;
|
||||
export var TemplateParser: typeof _c.TemplateParser = _c.TemplateParser;
|
||||
|
||||
export type DomElementSchemaRegistry = _c.DomElementSchemaRegistry;
|
||||
export var DomElementSchemaRegistry: typeof _c.DomElementSchemaRegistry =
|
||||
_c.DomElementSchemaRegistry;
|
||||
|
||||
export type StyleCompiler = _c.StyleCompiler;
|
||||
export var StyleCompiler: typeof _c.StyleCompiler = _c.StyleCompiler;
|
||||
|
||||
export type ViewCompiler = _c.ViewCompiler;
|
||||
export var ViewCompiler: typeof _c.ViewCompiler = _c.ViewCompiler;
|
||||
|
||||
export type TypeScriptEmitter = _c.TypeScriptEmitter;
|
||||
export var TypeScriptEmitter: typeof _c.TypeScriptEmitter = _c.TypeScriptEmitter;
|
@ -1,9 +0,0 @@
|
||||
import {__core_private__ as r, __core_private_types__ as t} from '@angular/core';
|
||||
|
||||
export type ReflectorReader = t.ReflectorReader;
|
||||
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
|
||||
|
||||
export type ReflectionCapabilities = t.ReflectionCapabilities;
|
||||
export var ReflectionCapabilities: typeof t.ReflectionCapabilities = r.ReflectionCapabilities;
|
||||
|
||||
export var reflector: typeof t.reflector = r.reflector;
|
@ -1,181 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Extract i18n messages from source code
|
||||
*/
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as tsc from '@angular/tsc-wrapped';
|
||||
import * as path from 'path';
|
||||
import * as compiler from '@angular/compiler';
|
||||
import {ViewEncapsulation, lockRunMode} from '@angular/core';
|
||||
|
||||
import {StaticReflector} from './static_reflector';
|
||||
import {CompileMetadataResolver, HtmlParser, DirectiveNormalizer, Lexer, Parser, TemplateParser, DomElementSchemaRegistry, StyleCompiler, ViewCompiler, TypeScriptEmitter, MessageExtractor, removeDuplicates, ExtractionResult, Message, ParseError, serializeXmb,} from './compiler_private';
|
||||
|
||||
import {ReflectorHost} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
|
||||
function extract(
|
||||
ngOptions: tsc.AngularCompilerOptions, program: ts.Program, host: ts.CompilerHost) {
|
||||
return Extractor.create(ngOptions, program, host).extract();
|
||||
}
|
||||
|
||||
const _dirPaths = new Map<compiler.CompileDirectiveMetadata, string>();
|
||||
|
||||
const _GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
|
||||
class Extractor {
|
||||
constructor(
|
||||
private _options: tsc.AngularCompilerOptions, private _program: ts.Program,
|
||||
public host: ts.CompilerHost, private staticReflector: StaticReflector,
|
||||
private _resolver: CompileMetadataResolver, private _compiler: compiler.OfflineCompiler,
|
||||
private _reflectorHost: ReflectorHost, private _extractor: MessageExtractor) {
|
||||
lockRunMode();
|
||||
}
|
||||
|
||||
private _extractCmpMessages(metadatas: compiler.CompileDirectiveMetadata[]):
|
||||
Promise<ExtractionResult> {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
const directiveType = metadata.type.runtime;
|
||||
const directives = this._resolver.getViewDirectivesMetadata(directiveType);
|
||||
return Promise.all(directives.map(d => this._compiler.normalizeDirectiveMetadata(d)))
|
||||
.then(normalizedDirectives => {
|
||||
const pipes = this._resolver.getViewPipesMetadata(directiveType);
|
||||
return new compiler.NormalizedComponentWithViewDirectives(
|
||||
metadata, normalizedDirectives, pipes);
|
||||
});
|
||||
};
|
||||
|
||||
return Promise.all(metadatas.map(normalize))
|
||||
.then((cmps: compiler.NormalizedComponentWithViewDirectives[]) => {
|
||||
let messages: Message[] = [];
|
||||
let errors: ParseError[] = [];
|
||||
cmps.forEach(cmp => {
|
||||
let url = _dirPaths.get(cmp.component);
|
||||
let result = this._extractor.extract(cmp.component.template.template, url);
|
||||
errors = errors.concat(result.errors);
|
||||
messages = messages.concat(result.messages);
|
||||
});
|
||||
|
||||
// Extraction Result might contain duplicate messages at this point
|
||||
return new ExtractionResult(messages, errors);
|
||||
});
|
||||
}
|
||||
|
||||
private _readComponents(absSourcePath: string): Promise<compiler.CompileDirectiveMetadata>[] {
|
||||
const result: Promise<compiler.CompileDirectiveMetadata>[] = [];
|
||||
const metadata = this.staticReflector.getModuleMetadata(absSourcePath);
|
||||
if (!metadata) {
|
||||
console.log(`WARNING: no metadata found for ${absSourcePath}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
const symbols = Object.keys(metadata['metadata']);
|
||||
if (!symbols || !symbols.length) {
|
||||
return result;
|
||||
}
|
||||
for (const symbol of symbols) {
|
||||
const staticType = this._reflectorHost.findDeclaration(absSourcePath, symbol, absSourcePath);
|
||||
let directive: compiler.CompileDirectiveMetadata;
|
||||
directive = this._resolver.maybeGetDirectiveMetadata(<any>staticType);
|
||||
|
||||
if (directive && directive.isComponent) {
|
||||
let promise = this._compiler.normalizeDirectiveMetadata(directive);
|
||||
promise.then(md => _dirPaths.set(md, absSourcePath));
|
||||
result.push(promise);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
extract(): Promise<any> {
|
||||
_dirPaths.clear();
|
||||
|
||||
const promises = this._program.getSourceFiles()
|
||||
.map(sf => sf.fileName)
|
||||
.filter(f => !_GENERATED_FILES.test(f))
|
||||
.map(
|
||||
(absSourcePath: string): Promise<any> =>
|
||||
Promise.all(this._readComponents(absSourcePath))
|
||||
.then(metadatas => this._extractCmpMessages(metadatas))
|
||||
.catch(e => console.error(e.stack)));
|
||||
|
||||
let messages: Message[] = [];
|
||||
let errors: ParseError[] = [];
|
||||
|
||||
return Promise.all(promises).then(extractionResults => {
|
||||
extractionResults.filter(result => !!result).forEach(result => {
|
||||
messages = messages.concat(result.messages);
|
||||
errors = errors.concat(result.errors);
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(errors.map(e => e.toString()).join('\n'));
|
||||
}
|
||||
|
||||
messages = removeDuplicates(messages);
|
||||
|
||||
let genPath = path.join(this._options.genDir, 'messages.xmb');
|
||||
let msgBundle = serializeXmb(messages);
|
||||
|
||||
this.host.writeFile(genPath, msgBundle, false);
|
||||
});
|
||||
}
|
||||
|
||||
static create(
|
||||
options: tsc.AngularCompilerOptions, program: ts.Program,
|
||||
compilerHost: ts.CompilerHost): Extractor {
|
||||
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const reflectorHost = new ReflectorHost(program, compilerHost, options);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser = new HtmlParser();
|
||||
const config = new compiler.CompilerConfig({
|
||||
genDebugInfo: true,
|
||||
defaultEncapsulation: ViewEncapsulation.Emulated,
|
||||
logBindingUpdate: false,
|
||||
useJit: false,
|
||||
platformDirectives: [],
|
||||
platformPipes: []
|
||||
});
|
||||
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
|
||||
const parser = new Parser(new Lexer());
|
||||
const tmplParser = new TemplateParser(
|
||||
parser, new DomElementSchemaRegistry(), htmlParser,
|
||||
/*console*/ null, []);
|
||||
const offlineCompiler = new compiler.OfflineCompiler(
|
||||
normalizer, tmplParser, new StyleCompiler(urlResolver), new ViewCompiler(config),
|
||||
new TypeScriptEmitter(reflectorHost), xhr);
|
||||
const resolver = new CompileMetadataResolver(
|
||||
new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector),
|
||||
new compiler.ViewResolver(staticReflector), config, staticReflector);
|
||||
|
||||
// TODO(vicb): handle implicit
|
||||
const extractor = new MessageExtractor(htmlParser, parser, [], {});
|
||||
|
||||
return new Extractor(
|
||||
options, program, compilerHost, staticReflector, resolver, offlineCompiler, reflectorHost,
|
||||
extractor);
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
tsc.main(args.p || args.project || '.', args.basePath, extract)
|
||||
.then(exitCode => process.exit(exitCode))
|
||||
.catch(e => {
|
||||
console.error(e.stack);
|
||||
console.error('Compilation failed');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Must be imported first, because angular2 decorators throws on load.
|
||||
import 'reflect-metadata';
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as tsc from '@angular/tsc-wrapped';
|
||||
|
||||
import {CodeGenerator} from './codegen';
|
||||
|
||||
function codegen(
|
||||
ngOptions: tsc.AngularCompilerOptions, program: ts.Program, host: ts.CompilerHost) {
|
||||
return CodeGenerator.create(ngOptions, program, host).codegen();
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2));
|
||||
tsc.main(args.p || args.project || '.', args.basePath, codegen)
|
||||
.then(exitCode => process.exit(exitCode))
|
||||
.catch(e => {
|
||||
console.error(e.stack);
|
||||
console.error('Compilation failed');
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
import {AngularCompilerOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AssetUrl, ImportGenerator} from './compiler_private';
|
||||
import {StaticReflectorHost, StaticSymbol} from './static_reflector';
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
export interface ReflectorHostContext {
|
||||
exists(fileName: string): boolean;
|
||||
read(fileName: string): string;
|
||||
write(fileName: string, data: string): void;
|
||||
}
|
||||
|
||||
export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
private metadataCollector = new MetadataCollector();
|
||||
private context: ReflectorHostContext;
|
||||
constructor(
|
||||
private program: ts.Program, private compilerHost: ts.CompilerHost,
|
||||
private options: AngularCompilerOptions, context?: ReflectorHostContext) {
|
||||
this.context = context || new NodeReflectorHostContext();
|
||||
}
|
||||
|
||||
angularImportLocations() {
|
||||
return {
|
||||
coreDecorators: '@angular/core/src/metadata',
|
||||
diDecorators: '@angular/core/src/di/decorators',
|
||||
diMetadata: '@angular/core/src/di/metadata',
|
||||
diOpaqueToken: '@angular/core/src/di/opaque_token',
|
||||
animationMetadata: '@angular/core/src/animation/metadata',
|
||||
provider: '@angular/core/src/di/provider'
|
||||
};
|
||||
}
|
||||
private resolve(m: string, containingFile: string) {
|
||||
const resolved =
|
||||
ts.resolveModuleName(m, containingFile, this.options, this.compilerHost).resolvedModule;
|
||||
return resolved ? resolved.resolvedFileName : null;
|
||||
};
|
||||
|
||||
private normalizeAssetUrl(url: string): string {
|
||||
let assetUrl = AssetUrl.parse(url);
|
||||
return assetUrl ? `${assetUrl.packageName}/${assetUrl.modulePath}` : null;
|
||||
}
|
||||
|
||||
private resolveAssetUrl(url: string, containingFile: string): string {
|
||||
let assetUrl = this.normalizeAssetUrl(url);
|
||||
if (assetUrl) {
|
||||
return this.resolve(assetUrl, containingFile);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want a moduleId that will appear in import statements in the generated code.
|
||||
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
||||
* Relativize the paths by checking candidate prefixes of the absolute path, to see if
|
||||
* they are resolvable by the moduleResolution strategy from the CompilerHost.
|
||||
*/
|
||||
getImportPath(containingFile: string, importedFile: string) {
|
||||
importedFile = this.resolveAssetUrl(importedFile, containingFile);
|
||||
containingFile = this.resolveAssetUrl(containingFile, '');
|
||||
|
||||
// TODO(tbosch): if a file does not yet exist (because we compile it later),
|
||||
// we still need to create it so that the `resolve` method works!
|
||||
if (!this.compilerHost.fileExists(importedFile)) {
|
||||
if (this.options.trace) {
|
||||
console.log(`Generating empty file ${importedFile} to allow resolution of import`);
|
||||
}
|
||||
this.compilerHost.writeFile(importedFile, '', false);
|
||||
this.context.write(importedFile, '');
|
||||
}
|
||||
|
||||
const importModuleName = importedFile.replace(EXT, '');
|
||||
const parts = importModuleName.split(path.sep).filter(p => !!p);
|
||||
|
||||
for (let index = parts.length - 1; index >= 0; index--) {
|
||||
let candidate = parts.slice(index, parts.length).join(path.sep);
|
||||
if (this.resolve('.' + path.sep + candidate, containingFile) === importedFile) {
|
||||
return `./${candidate}`;
|
||||
}
|
||||
if (this.resolve(candidate, containingFile) === importedFile) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// Try a relative import
|
||||
let candidate = path.relative(path.dirname(containingFile), importModuleName);
|
||||
if (this.resolve(candidate, containingFile) === importedFile) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to find any resolvable import for ${importedFile} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
findDeclaration(
|
||||
module: string, symbolName: string, containingFile: string,
|
||||
containingModule?: string): StaticSymbol {
|
||||
if (!containingFile || !containingFile.length) {
|
||||
if (module.indexOf('.') === 0) {
|
||||
throw new Error('Resolution of relative paths requires a containing file.');
|
||||
}
|
||||
// Any containing file gives the same result for absolute imports
|
||||
containingFile = path.join(this.options.basePath, 'index.ts');
|
||||
}
|
||||
|
||||
try {
|
||||
let assetUrl = this.normalizeAssetUrl(module);
|
||||
if (assetUrl) {
|
||||
module = assetUrl;
|
||||
}
|
||||
const filePath = this.resolve(module, containingFile);
|
||||
|
||||
if (!filePath) {
|
||||
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
|
||||
}
|
||||
|
||||
const tc = this.program.getTypeChecker();
|
||||
const sf = this.program.getSourceFile(filePath);
|
||||
if (!sf || !(<any>sf).symbol) {
|
||||
// The source file was not needed in the compile but we do need the values from
|
||||
// the corresponding .ts files stored in the .metadata.json file. Just assume the
|
||||
// symbol and file we resolved to be correct as we don't need this to be the
|
||||
// cannonical reference as this reference could have only been generated by a
|
||||
// .metadata.json file resolving values.
|
||||
return this.getStaticSymbol(filePath, symbolName);
|
||||
}
|
||||
|
||||
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
|
||||
if (!symbol) {
|
||||
throw new Error(`can't find symbol ${symbolName} exported from module ${filePath}`);
|
||||
}
|
||||
if (symbol &&
|
||||
symbol.flags & ts.SymbolFlags.Alias) { // This is an alias, follow what it aliases
|
||||
symbol = tc.getAliasedSymbol(symbol);
|
||||
}
|
||||
const declaration = symbol.getDeclarations()[0];
|
||||
const declarationFile = declaration.getSourceFile().fileName;
|
||||
|
||||
return this.getStaticSymbol(declarationFile, symbol.getName());
|
||||
} catch (e) {
|
||||
console.error(`can't resolve module ${module} from ${containingFile}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private typeCache = new Map<string, StaticSymbol>();
|
||||
|
||||
/**
|
||||
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
||||
* All types passed to the StaticResolver should be pseudo-types returned by this method.
|
||||
*
|
||||
* @param declarationFile the absolute path of the file where the symbol is declared
|
||||
* @param name the name of the type.
|
||||
*/
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol {
|
||||
let key = `"${declarationFile}".${name}`;
|
||||
let result = this.typeCache.get(key);
|
||||
if (!result) {
|
||||
result = new StaticSymbol(declarationFile, name);
|
||||
this.typeCache.set(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(alexeagle): take a statictype
|
||||
getMetadataFor(filePath: string): ModuleMetadata {
|
||||
if (!this.context.exists(filePath)) {
|
||||
throw new Error(`No such file '${filePath}'`);
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||
if (this.context.exists(metadataPath)) {
|
||||
return this.readMetadata(metadataPath);
|
||||
}
|
||||
}
|
||||
|
||||
let sf = this.program.getSourceFile(filePath);
|
||||
if (!sf) {
|
||||
throw new Error(`Source file ${filePath} not present in program.`);
|
||||
}
|
||||
const metadata = this.metadataCollector.getMetadata(sf);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
readMetadata(filePath: string) {
|
||||
try {
|
||||
const result = JSON.parse(this.context.read(filePath));
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${filePath}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeReflectorHostContext implements ReflectorHostContext {
|
||||
exists(fileName: string): boolean { return fs.existsSync(fileName); }
|
||||
|
||||
read(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
||||
|
||||
write(fileName: string, data: string): void { fs.writeFileSync(fileName, data, 'utf8'); }
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import {ReflectionCapabilities, reflector} from './core_private';
|
||||
import {StaticReflector} from './static_reflector';
|
||||
|
||||
export class StaticAndDynamicReflectionCapabilities {
|
||||
static install(staticDelegate: StaticReflector) {
|
||||
reflector.updateCapabilities(new StaticAndDynamicReflectionCapabilities(staticDelegate));
|
||||
}
|
||||
|
||||
private dynamicDelegate = new ReflectionCapabilities();
|
||||
|
||||
constructor(private staticDelegate: StaticReflector) {}
|
||||
|
||||
isReflectionEnabled(): boolean { return true; }
|
||||
factory(type: any): Function { return this.dynamicDelegate.factory(type); }
|
||||
interfaces(type: any): any[] { return this.dynamicDelegate.interfaces(type); }
|
||||
hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean {
|
||||
return isStaticType(type) ?
|
||||
this.staticDelegate.hasLifecycleHook(type, lcInterface, lcProperty) :
|
||||
this.dynamicDelegate.hasLifecycleHook(type, lcInterface, lcProperty);
|
||||
}
|
||||
parameters(type: any): any[][] {
|
||||
return isStaticType(type) ? this.staticDelegate.parameters(type) :
|
||||
this.dynamicDelegate.parameters(type);
|
||||
}
|
||||
annotations(type: any): any[] {
|
||||
return isStaticType(type) ? this.staticDelegate.annotations(type) :
|
||||
this.dynamicDelegate.annotations(type);
|
||||
}
|
||||
propMetadata(typeOrFunc: any): {[key: string]: any[]} {
|
||||
return isStaticType(typeOrFunc) ? this.staticDelegate.propMetadata(typeOrFunc) :
|
||||
this.dynamicDelegate.propMetadata(typeOrFunc);
|
||||
}
|
||||
getter(name: string) { return this.dynamicDelegate.getter(name); }
|
||||
setter(name: string) { return this.dynamicDelegate.setter(name); }
|
||||
method(name: string) { return this.dynamicDelegate.method(name); }
|
||||
importUri(type: any): string { return this.staticDelegate.importUri(type); }
|
||||
}
|
||||
|
||||
function isStaticType(type: any): boolean {
|
||||
return typeof type === 'object' && type.name && type.filePath;
|
||||
}
|
@ -1,604 +0,0 @@
|
||||
import {AttributeMetadata, ComponentMetadata, ContentChildMetadata, ContentChildrenMetadata, DirectiveMetadata, HostBindingMetadata, HostListenerMetadata, HostMetadata, InjectMetadata, InjectableMetadata, InputMetadata, OptionalMetadata, OutputMetadata, PipeMetadata, Provider, QueryMetadata, SelfMetadata, SkipSelfMetadata, ViewChildMetadata, ViewChildrenMetadata, ViewQueryMetadata, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
|
||||
import {ReflectorReader} from './core_private';
|
||||
|
||||
const SUPPORTED_SCHEMA_VERSION = 1;
|
||||
|
||||
/**
|
||||
* The host of the static resolver is expected to be able to provide module metadata in the form of
|
||||
* ModuleMetadata. Angular 2 CLI will produce this metadata for a module whenever a .d.ts files is
|
||||
* produced and the module has exported variables or classes with decorators. Module metadata can
|
||||
* also be produced directly from TypeScript sources by using MetadataCollector in tools/metadata.
|
||||
*/
|
||||
export interface StaticReflectorHost {
|
||||
/**
|
||||
* Return a ModuleMetadata for the given module.
|
||||
*
|
||||
* @param modulePath is a string identifier for a module as an absolute path.
|
||||
* @returns the metadata for the given module.
|
||||
*/
|
||||
getMetadataFor(modulePath: string): {[key: string]: any};
|
||||
|
||||
/**
|
||||
* Resolve a symbol from an import statement form, to the file where it is declared.
|
||||
* @param module the location imported from
|
||||
* @param containingFile for relative imports, the path of the file containing the import
|
||||
*/
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol;
|
||||
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol;
|
||||
|
||||
angularImportLocations(): {
|
||||
coreDecorators: string,
|
||||
diDecorators: string,
|
||||
diMetadata: string,
|
||||
diOpaqueToken: string,
|
||||
animationMetadata: string,
|
||||
provider: string
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A token representing the a reference to a static type.
|
||||
*
|
||||
* This token is unique for a filePath and name and can be used as a hash table key.
|
||||
*/
|
||||
export class StaticSymbol {
|
||||
constructor(public filePath: string, public name: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A static reflector implements enough of the Reflector API that is necessary to compile
|
||||
* templates statically.
|
||||
*/
|
||||
export class StaticReflector implements ReflectorReader {
|
||||
private annotationCache = new Map<StaticSymbol, any[]>();
|
||||
private propertyCache = new Map<StaticSymbol, {[key: string]: any}>();
|
||||
private parameterCache = new Map<StaticSymbol, any[]>();
|
||||
private metadataCache = new Map<string, {[key: string]: any}>();
|
||||
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
|
||||
private opaqueToken: StaticSymbol;
|
||||
|
||||
constructor(private host: StaticReflectorHost) { this.initializeConversionMap(); }
|
||||
|
||||
importUri(typeOrFunc: StaticSymbol): string {
|
||||
var staticSymbol = this.host.findDeclaration(typeOrFunc.filePath, typeOrFunc.name, '');
|
||||
return staticSymbol ? staticSymbol.filePath : null;
|
||||
}
|
||||
|
||||
public annotations(type: StaticSymbol): any[] {
|
||||
let annotations = this.annotationCache.get(type);
|
||||
if (!annotations) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
if (classMetadata['decorators']) {
|
||||
annotations = this.simplify(type, classMetadata['decorators']);
|
||||
} else {
|
||||
annotations = [];
|
||||
}
|
||||
this.annotationCache.set(type, annotations.filter(ann => !!ann));
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public propMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let propMetadata = this.propertyCache.get(type);
|
||||
if (!propMetadata) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : {};
|
||||
propMetadata = mapStringMap(members, (propData, propName) => {
|
||||
let prop = (<any[]>propData).find(a => a['__symbolic'] == 'property');
|
||||
if (prop && prop['decorators']) {
|
||||
return this.simplify(type, prop['decorators']);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
this.propertyCache.set(type, propMetadata);
|
||||
}
|
||||
return propMetadata;
|
||||
}
|
||||
|
||||
public parameters(type: StaticSymbol): any[] {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
try {
|
||||
let parameters = this.parameterCache.get(type);
|
||||
if (!parameters) {
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : null;
|
||||
let ctorData = members ? members['__ctor__'] : null;
|
||||
if (ctorData) {
|
||||
let ctor = (<any[]>ctorData).find(a => a['__symbolic'] == 'constructor');
|
||||
let parameterTypes = <any[]>this.simplify(type, ctor['parameters'] || []);
|
||||
let parameterDecorators = <any[]>this.simplify(type, ctor['parameterDecorators'] || []);
|
||||
|
||||
parameters = [];
|
||||
parameterTypes.forEach((paramType, index) => {
|
||||
let nestedResult: any[] = [];
|
||||
if (paramType) {
|
||||
nestedResult.push(paramType);
|
||||
}
|
||||
let decorators = parameterDecorators ? parameterDecorators[index] : null;
|
||||
if (decorators) {
|
||||
nestedResult.push(...decorators);
|
||||
}
|
||||
parameters.push(nestedResult);
|
||||
});
|
||||
}
|
||||
if (!parameters) {
|
||||
parameters = [];
|
||||
}
|
||||
this.parameterCache.set(type, parameters);
|
||||
}
|
||||
return parameters;
|
||||
} catch (e) {
|
||||
console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean {
|
||||
if (!(type instanceof StaticSymbol)) {
|
||||
throw new Error(
|
||||
`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`);
|
||||
}
|
||||
let classMetadata = this.getTypeMetadata(type);
|
||||
let members = classMetadata ? classMetadata['members'] : null;
|
||||
let member: any[] = members ? members[lcProperty] : null;
|
||||
return member ? member.some(a => a['__symbolic'] == 'method') : false;
|
||||
}
|
||||
|
||||
private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void {
|
||||
this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => {
|
||||
var metadata = Object.create(ctor.prototype);
|
||||
ctor.apply(metadata, args);
|
||||
return metadata;
|
||||
});
|
||||
}
|
||||
|
||||
private registerFunction(type: StaticSymbol, fn: any): void {
|
||||
this.conversionMap.set(
|
||||
type, (context: StaticSymbol, args: any[]) => { return fn.apply(undefined, args); });
|
||||
}
|
||||
|
||||
private initializeConversionMap(): void {
|
||||
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
|
||||
this.host.angularImportLocations();
|
||||
this.opaqueToken = this.host.findDeclaration(diOpaqueToken, 'OpaqueToken');
|
||||
this.registerDecoratorOrConstructor(this.host.findDeclaration(provider, 'Provider'), Provider);
|
||||
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Host'), HostMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Injectable'), InjectableMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Self'), SelfMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'SkipSelf'), SkipSelfMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Inject'), InjectMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diDecorators, 'Optional'), OptionalMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Attribute'), AttributeMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Query'), QueryMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewQuery'), ViewQueryMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChild'), ContentChildMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ContentChildren'), ContentChildrenMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChild'), ViewChildMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'ViewChildren'), ViewChildrenMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Input'), InputMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Output'), OutputMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Pipe'), PipeMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostBinding'), HostBindingMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'HostListener'), HostListenerMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Directive'), DirectiveMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(coreDecorators, 'Component'), ComponentMetadata);
|
||||
|
||||
// Note: Some metadata classes can be used directly with Provider.deps.
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'HostMetadata'), HostMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'SelfMetadata'), SelfMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'SkipSelfMetadata'), SkipSelfMetadata);
|
||||
this.registerDecoratorOrConstructor(
|
||||
this.host.findDeclaration(diMetadata, 'OptionalMetadata'), OptionalMetadata);
|
||||
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence);
|
||||
this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
public simplify(context: StaticSymbol, value: any): any {
|
||||
let _this = this;
|
||||
let scope = BindingScope.empty;
|
||||
let calling = new Map<StaticSymbol, boolean>();
|
||||
|
||||
function simplifyInContext(context: StaticSymbol, value: any, depth: number): any {
|
||||
function resolveReference(expression: any): StaticSymbol {
|
||||
let staticSymbol: StaticSymbol;
|
||||
if (expression['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
expression['module'], expression['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, expression['name']);
|
||||
}
|
||||
return staticSymbol;
|
||||
}
|
||||
|
||||
function isOpaqueToken(value: any): boolean {
|
||||
if (value && value.__symbolic === 'new' && value.expression) {
|
||||
let target = value.expression;
|
||||
if (target.__symbolic == 'reference') {
|
||||
return sameSymbol(resolveReference(target), _this.opaqueToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function simplifyCall(expression: any) {
|
||||
let context: {[name: string]: string}|undefined = undefined;
|
||||
if (expression['__symbolic'] == 'call') {
|
||||
let target = expression['expression'];
|
||||
if (target && target.__symbolic === 'reference') {
|
||||
context = {name: target.name};
|
||||
}
|
||||
let targetFunction = simplify(target);
|
||||
if (targetFunction['__symbolic'] == 'function') {
|
||||
if (calling.get(targetFunction)) {
|
||||
throw new Error('Recursion not supported');
|
||||
}
|
||||
calling.set(targetFunction, true);
|
||||
let value = targetFunction['value'];
|
||||
if (value) {
|
||||
// Determine the arguments
|
||||
let args = (expression['arguments'] || []).map((arg: any) => simplify(arg));
|
||||
let parameters: string[] = targetFunction['parameters'];
|
||||
let functionScope = BindingScope.build();
|
||||
for (let i = 0; i < parameters.length; i++) {
|
||||
functionScope.define(parameters[i], args[i]);
|
||||
}
|
||||
let oldScope = scope;
|
||||
let result: any;
|
||||
try {
|
||||
scope = functionScope.done();
|
||||
result = simplify(value);
|
||||
} finally {
|
||||
scope = oldScope;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
calling.delete(targetFunction);
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0) {
|
||||
// If depth is 0 we are evaluating the top level expression that is describing element
|
||||
// decorator. In this case, it is a decorator we don't understand, such as a custom
|
||||
// non-angular decorator, and we should just ignore it.
|
||||
return {__symbolic: 'ignore'};
|
||||
}
|
||||
return simplify({__symbolic: 'error', message: 'Function call not supported', context});
|
||||
}
|
||||
|
||||
function simplify(expression: any): any {
|
||||
if (isPrimitive(expression)) {
|
||||
return expression;
|
||||
}
|
||||
if (expression instanceof Array) {
|
||||
let result: any[] = [];
|
||||
for (let item of (<any>expression)) {
|
||||
// Check for a spread expression
|
||||
if (item && item.__symbolic === 'spread') {
|
||||
let spreadArray = simplify(item.expression);
|
||||
if (Array.isArray(spreadArray)) {
|
||||
for (let spreadItem of spreadArray) {
|
||||
result.push(spreadItem);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let value = simplify(item);
|
||||
if (shouldIgnore(value)) {
|
||||
continue;
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expression) {
|
||||
if (expression['__symbolic']) {
|
||||
let staticSymbol: StaticSymbol;
|
||||
switch (expression['__symbolic']) {
|
||||
case 'binop':
|
||||
let left = simplify(expression['left']);
|
||||
if (shouldIgnore(left)) return left;
|
||||
let right = simplify(expression['right']);
|
||||
if (shouldIgnore(right)) return right;
|
||||
switch (expression['operator']) {
|
||||
case '&&':
|
||||
return left && right;
|
||||
case '||':
|
||||
return left || right;
|
||||
case '|':
|
||||
return left | right;
|
||||
case '^':
|
||||
return left ^ right;
|
||||
case '&':
|
||||
return left & right;
|
||||
case '==':
|
||||
return left == right;
|
||||
case '!=':
|
||||
return left != right;
|
||||
case '===':
|
||||
return left === right;
|
||||
case '!==':
|
||||
return left !== right;
|
||||
case '<':
|
||||
return left < right;
|
||||
case '>':
|
||||
return left > right;
|
||||
case '<=':
|
||||
return left <= right;
|
||||
case '>=':
|
||||
return left >= right;
|
||||
case '<<':
|
||||
return left << right;
|
||||
case '>>':
|
||||
return left >> right;
|
||||
case '+':
|
||||
return left + right;
|
||||
case '-':
|
||||
return left - right;
|
||||
case '*':
|
||||
return left * right;
|
||||
case '/':
|
||||
return left / right;
|
||||
case '%':
|
||||
return left % right;
|
||||
}
|
||||
return null;
|
||||
case 'pre':
|
||||
let operand = simplify(expression['operand']);
|
||||
if (shouldIgnore(operand)) return operand;
|
||||
switch (expression['operator']) {
|
||||
case '+':
|
||||
return operand;
|
||||
case '-':
|
||||
return -operand;
|
||||
case '!':
|
||||
return !operand;
|
||||
case '~':
|
||||
return ~operand;
|
||||
}
|
||||
return null;
|
||||
case 'index':
|
||||
let indexTarget = simplify(expression['expression']);
|
||||
let index = simplify(expression['index']);
|
||||
if (indexTarget && isPrimitive(index)) return indexTarget[index];
|
||||
return null;
|
||||
case 'select':
|
||||
let selectTarget = simplify(expression['expression']);
|
||||
let member = simplify(expression['member']);
|
||||
if (selectTarget && isPrimitive(member)) return selectTarget[member];
|
||||
return null;
|
||||
case 'reference':
|
||||
if (!expression.module) {
|
||||
let name: string = expression['name'];
|
||||
let localValue = scope.resolve(name);
|
||||
if (localValue != BindingScope.missing) {
|
||||
return localValue;
|
||||
}
|
||||
}
|
||||
staticSymbol = resolveReference(expression);
|
||||
let result: any = staticSymbol;
|
||||
let moduleMetadata = _this.getModuleMetadata(staticSymbol.filePath);
|
||||
let declarationValue =
|
||||
moduleMetadata ? moduleMetadata['metadata'][staticSymbol.name] : null;
|
||||
if (declarationValue) {
|
||||
if (isOpaqueToken(declarationValue)) {
|
||||
// If the referenced symbol is initalized by a new OpaqueToken we can keep the
|
||||
// reference to the symbol.
|
||||
return staticSymbol;
|
||||
}
|
||||
result = simplifyInContext(staticSymbol, declarationValue, depth + 1);
|
||||
}
|
||||
return result;
|
||||
case 'class':
|
||||
return context;
|
||||
case 'function':
|
||||
return expression;
|
||||
case 'new':
|
||||
case 'call':
|
||||
// Determine if the function is a built-in conversion
|
||||
let target = expression['expression'];
|
||||
if (target['module']) {
|
||||
staticSymbol = _this.host.findDeclaration(
|
||||
target['module'], target['name'], context.filePath);
|
||||
} else {
|
||||
staticSymbol = _this.host.getStaticSymbol(context.filePath, target['name']);
|
||||
}
|
||||
let converter = _this.conversionMap.get(staticSymbol);
|
||||
if (converter) {
|
||||
let args: any[] = expression['arguments'];
|
||||
if (!args) {
|
||||
args = [];
|
||||
}
|
||||
return converter(
|
||||
context, args.map(arg => simplifyInContext(context, arg, depth + 1)));
|
||||
}
|
||||
|
||||
// Determine if the function is one we can simplify.
|
||||
return simplifyCall(expression);
|
||||
|
||||
case 'error':
|
||||
let message = produceErrorMessage(expression);
|
||||
if (expression['line']) {
|
||||
message =
|
||||
`${message} (position ${expression['line']}:${expression['character']} in the original .ts file)`;
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return mapStringMap(expression, (value, name) => simplify(value));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return simplify(value);
|
||||
} catch (e) {
|
||||
throw new Error(`${e.message}, resolving symbol ${context.name} in ${context.filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
let result = simplifyInContext(context, value, 0);
|
||||
if (shouldIgnore(result)) {
|
||||
return undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param module an absolute path to a module file.
|
||||
*/
|
||||
public getModuleMetadata(module: string): {[key: string]: any} {
|
||||
let moduleMetadata = this.metadataCache.get(module);
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata = this.host.getMetadataFor(module);
|
||||
if (Array.isArray(moduleMetadata)) {
|
||||
moduleMetadata = (<Array<any>>moduleMetadata)
|
||||
.find(element => element.version === SUPPORTED_SCHEMA_VERSION) ||
|
||||
moduleMetadata[0];
|
||||
}
|
||||
if (!moduleMetadata) {
|
||||
moduleMetadata =
|
||||
{__symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {}};
|
||||
}
|
||||
if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
||||
throw new Error(
|
||||
`Metadata version mismatch for module ${module}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`);
|
||||
}
|
||||
this.metadataCache.set(module, moduleMetadata);
|
||||
}
|
||||
return moduleMetadata;
|
||||
}
|
||||
|
||||
private getTypeMetadata(type: StaticSymbol): {[key: string]: any} {
|
||||
let moduleMetadata = this.getModuleMetadata(type.filePath);
|
||||
let result = moduleMetadata['metadata'][type.name];
|
||||
if (!result) {
|
||||
result = {__symbolic: 'class'};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function expandedMessage(error: any): string {
|
||||
switch (error.message) {
|
||||
case 'Reference to non-exported class':
|
||||
if (error.context && error.context.className) {
|
||||
return `Reference to a non-exported class ${error.context.className}`;
|
||||
}
|
||||
break;
|
||||
case 'Variable not initialized':
|
||||
return 'Only initialized variables and constants can be referenced';
|
||||
case 'Destructuring not supported':
|
||||
return 'Referencing an exported destructured variable or constant is not supported';
|
||||
case 'Could not resolve type':
|
||||
if (error.context && error.context.typeName) {
|
||||
return `Could not resolve type ${error.context.typeName}`;
|
||||
}
|
||||
break;
|
||||
case 'Function call not supported':
|
||||
let prefix =
|
||||
error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
|
||||
return prefix +
|
||||
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
||||
}
|
||||
return error.message;
|
||||
}
|
||||
|
||||
function produceErrorMessage(error: any): string {
|
||||
return `Error encountered resolving symbol values statically. ${expandedMessage(error)}`;
|
||||
}
|
||||
|
||||
function mapStringMap(input: {[key: string]: any}, transform: (value: any, key: string) => any):
|
||||
{[key: string]: any} {
|
||||
if (!input) return {};
|
||||
var result: {[key: string]: any} = {};
|
||||
Object.keys(input).forEach((key) => {
|
||||
let value = transform(input[key], key);
|
||||
if (!shouldIgnore(value)) {
|
||||
result[key] = value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function isPrimitive(o: any): boolean {
|
||||
return o === null || (typeof o !== 'function' && typeof o !== 'object');
|
||||
}
|
||||
|
||||
interface BindingScopeBuilder {
|
||||
define(name: string, value: any): BindingScopeBuilder;
|
||||
done(): BindingScope;
|
||||
}
|
||||
|
||||
abstract class BindingScope {
|
||||
abstract resolve(name: string): any;
|
||||
public static missing = {};
|
||||
public static empty: BindingScope = {resolve: name => BindingScope.missing};
|
||||
|
||||
public static build(): BindingScopeBuilder {
|
||||
let current = new Map<string, any>();
|
||||
let parent: BindingScope = undefined;
|
||||
return {
|
||||
define: function(name, value) {
|
||||
current.set(name, value);
|
||||
return this;
|
||||
},
|
||||
done: function() {
|
||||
return current.size > 0 ? new PopulatedScope(current) : BindingScope.empty;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class PopulatedScope extends BindingScope {
|
||||
constructor(private bindings: Map<string, any>) { super(); }
|
||||
|
||||
resolve(name: string): any {
|
||||
return this.bindings.has(name) ? this.bindings.get(name) : BindingScope.missing;
|
||||
}
|
||||
}
|
||||
|
||||
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
|
||||
return a === b || (a.name == b.name && a.filePath == b.filePath);
|
||||
}
|
||||
|
||||
function shouldIgnore(value: any): boolean {
|
||||
return value && value.__symbolic == 'ignore';
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReflectorHost, ReflectorHostContext} from '../src/reflector_host';
|
||||
|
||||
export type Entry = string | Directory;
|
||||
|
||||
export interface Directory { [name: string]: Entry; }
|
||||
|
||||
export class MockContext implements ReflectorHostContext {
|
||||
constructor(public currentDirectory: string, private files: Entry) {}
|
||||
|
||||
exists(fileName: string): boolean { return this.getEntry(fileName) !== undefined; }
|
||||
|
||||
read(fileName: string): string|undefined {
|
||||
let data = this.getEntry(fileName);
|
||||
if (typeof data === 'string') {
|
||||
return data;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
write(fileName: string, data: string): void {
|
||||
let parts = fileName.split('/');
|
||||
let name = parts.pop();
|
||||
let entry = this.getEntry(parts);
|
||||
if (entry && typeof entry !== 'string') {
|
||||
entry[name] = data;
|
||||
}
|
||||
}
|
||||
|
||||
getEntry(fileName: string|string[]): Entry|undefined {
|
||||
let parts = typeof fileName === 'string' ? fileName.split('/') : fileName;
|
||||
if (parts[0]) {
|
||||
parts = this.currentDirectory.split('/').concat(parts);
|
||||
}
|
||||
parts.shift();
|
||||
parts = normalize(parts);
|
||||
let current = this.files;
|
||||
while (parts.length) {
|
||||
let part = parts.shift();
|
||||
if (typeof current === 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let next = (<Directory>current)[part];
|
||||
if (next === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(parts: string[]): string[] {
|
||||
let result: string[] = [];
|
||||
while (parts.length) {
|
||||
let part = parts.shift();
|
||||
switch (part) {
|
||||
case '.':
|
||||
break;
|
||||
case '..':
|
||||
result.pop();
|
||||
break;
|
||||
default:
|
||||
result.push(part);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class MockCompilerHost implements ts.CompilerHost {
|
||||
constructor(private context: MockContext) {}
|
||||
|
||||
fileExists(fileName: string): boolean { return this.context.exists(fileName); }
|
||||
|
||||
readFile(fileName: string): string { return this.context.read(fileName); }
|
||||
|
||||
directoryExists(directoryName: string): boolean { return this.context.exists(directoryName); }
|
||||
|
||||
getSourceFile(
|
||||
fileName: string, languageVersion: ts.ScriptTarget,
|
||||
onError?: (message: string) => void): ts.SourceFile {
|
||||
let sourceText = this.context.read(fileName);
|
||||
if (sourceText) {
|
||||
return ts.createSourceFile(fileName, sourceText, languageVersion);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return ts.getDefaultLibFileName(options);
|
||||
}
|
||||
|
||||
writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.write(fileName, text); }
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.context.currentDirectory;
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string { return fileName; }
|
||||
|
||||
useCaseSensitiveFileNames(): boolean { return false; }
|
||||
|
||||
getNewLine(): string { return '\n'; }
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ReflectorHost} from '../src/reflector_host';
|
||||
|
||||
import {Directory, Entry, MockCompilerHost, MockContext} from './mocks';
|
||||
|
||||
describe('reflector_host', () => {
|
||||
var context: MockContext;
|
||||
var host: ts.CompilerHost;
|
||||
var program: ts.Program;
|
||||
var reflectorHost: ReflectorHost;
|
||||
|
||||
beforeEach(() => {
|
||||
context = new MockContext('/tmp/src', clone(FILES));
|
||||
host = new MockCompilerHost(context)
|
||||
program = ts.createProgram(
|
||||
['main.ts'], {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
},
|
||||
host);
|
||||
// Force a typecheck
|
||||
let errors = program.getSemanticDiagnostics();
|
||||
if (errors && errors.length) {
|
||||
throw new Error('Expected no errors');
|
||||
}
|
||||
reflectorHost = new ReflectorHost(
|
||||
program, host, {
|
||||
genDir: '/tmp/dist',
|
||||
basePath: '/tmp/src',
|
||||
skipMetadataEmit: false,
|
||||
skipTemplateCodegen: false,
|
||||
trace: false
|
||||
},
|
||||
context);
|
||||
});
|
||||
|
||||
it('should provide the import locations for angular', () => {
|
||||
let {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} =
|
||||
reflectorHost.angularImportLocations();
|
||||
expect(coreDecorators).toEqual('@angular/core/src/metadata');
|
||||
expect(diDecorators).toEqual('@angular/core/src/di/decorators');
|
||||
expect(diMetadata).toEqual('@angular/core/src/di/metadata');
|
||||
expect(animationMetadata).toEqual('@angular/core/src/animation/metadata');
|
||||
expect(provider).toEqual('@angular/core/src/di/provider');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from main @angular/core', () => {
|
||||
expect(reflectorHost.getImportPath('main.ts', 'node_modules/@angular/core.d.ts'))
|
||||
.toEqual('@angular/core');
|
||||
});
|
||||
|
||||
it('should be ble to produce an import from main to a sub-directory', () => {
|
||||
expect(reflectorHost.getImportPath('main.ts', 'lib/utils.ts')).toEqual('./lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a peer file', () => {
|
||||
expect(reflectorHost.getImportPath('lib/utils.ts', 'lib/collections.ts'))
|
||||
.toEqual('./collections');
|
||||
});
|
||||
|
||||
it('should be able to produce an import from to a sibling directory', () => {
|
||||
expect(reflectorHost.getImportPath('lib2/utils2.ts', 'lib/utils.ts')).toEqual('../lib/utils');
|
||||
});
|
||||
|
||||
it('should be able to produce a symbol for an exported symbol', () => {
|
||||
expect(reflectorHost.findDeclaration('@angular/router-deprecated', 'foo', 'main.ts'))
|
||||
.toBeDefined();
|
||||
});
|
||||
|
||||
it('should be able to produce a symbol for values space only reference', () => {
|
||||
expect(
|
||||
reflectorHost.findDeclaration('@angular/router-deprecated/src/providers', 'foo', 'main.ts'))
|
||||
.toBeDefined();
|
||||
});
|
||||
|
||||
it('should be produce the same symbol if asked twice', () => {
|
||||
let foo1 = reflectorHost.getStaticSymbol('main.ts', 'foo');
|
||||
let foo2 = reflectorHost.getStaticSymbol('main.ts', 'foo');
|
||||
expect(foo1).toBe(foo2);
|
||||
});
|
||||
|
||||
it('should be able to read a metadata file',
|
||||
() => {
|
||||
expect(reflectorHost.getMetadataFor('node_modules/@angular/core.d.ts'))
|
||||
.toEqual({__symbolic: 'module', version: 1, metadata: {foo: {__symbolic: 'class'}}})});
|
||||
});
|
||||
|
||||
const dummyModule = 'export let foo: any[];'
|
||||
const FILES: Entry = {
|
||||
'tmp': {
|
||||
'src': {
|
||||
'main.ts': `
|
||||
import * as c from '@angular/core';
|
||||
import * as r from '@angular/router-deprecated';
|
||||
import * as u from './lib/utils';
|
||||
import * as cs from './lib/collections';
|
||||
import * as u2 from './lib2/utils2';
|
||||
`,
|
||||
'lib': {
|
||||
'utils.ts': dummyModule,
|
||||
'collections.ts': dummyModule,
|
||||
},
|
||||
'lib2': {'utils2.ts': dummyModule},
|
||||
'node_modules': {
|
||||
'@angular': {
|
||||
'core.d.ts': dummyModule,
|
||||
'core.metadata.json':
|
||||
`{"__symbolic":"module", "version": 1, "metadata": {"foo": {"__symbolic": "class"}}}`,
|
||||
'router-deprecated':
|
||||
{'index.d.ts': dummyModule, 'src': {'providers.d.ts': dummyModule}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clone(entry: Entry): Entry {
|
||||
if (typeof entry === 'string') {
|
||||
return entry;
|
||||
} else {
|
||||
let result: Directory = {};
|
||||
for (let name in entry) {
|
||||
result[name] = clone(entry[name]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,906 +0,0 @@
|
||||
import {StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler-cli/src/static_reflector';
|
||||
import {animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
|
||||
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
|
||||
import {ListWrapper} from '@angular/facade/src/collection';
|
||||
import {isBlank} from '@angular/facade/src/lang';
|
||||
import {MetadataCollector} from '@angular/tsc-wrapped';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
|
||||
// This matches .ts files but not .d.ts files.
|
||||
const TS_EXT = /(^.|(?!\.d)..)\.ts$/;
|
||||
|
||||
describe('StaticReflector', () => {
|
||||
let noContext = new StaticSymbol('', '');
|
||||
let host: StaticReflectorHost;
|
||||
let reflector: StaticReflector;
|
||||
|
||||
beforeEach(() => {
|
||||
host = new MockReflectorHost();
|
||||
reflector = new StaticReflector(host);
|
||||
});
|
||||
|
||||
function simplify(context: StaticSymbol, value: any) {
|
||||
return reflector.simplify(context, value);
|
||||
}
|
||||
|
||||
it('should get annotations for NgFor', () => {
|
||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let annotations = reflector.annotations(NgFor);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('[ngFor][ngForOf]');
|
||||
expect(annotation.inputs).toEqual(['ngForTrackBy', 'ngForOf', 'ngForTemplate']);
|
||||
});
|
||||
|
||||
it('should get constructor for NgFor', () => {
|
||||
let NgFor = host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor');
|
||||
let ViewContainerRef =
|
||||
host.findDeclaration('angular2/src/core/linker/view_container_ref', 'ViewContainerRef');
|
||||
let TemplateRef = host.findDeclaration('angular2/src/core/linker/template_ref', 'TemplateRef');
|
||||
let IterableDiffers = host.findDeclaration(
|
||||
'angular2/src/core/change_detection/differs/iterable_differs', 'IterableDiffers');
|
||||
let ChangeDetectorRef = host.findDeclaration(
|
||||
'angular2/src/core/change_detection/change_detector_ref', 'ChangeDetectorRef');
|
||||
|
||||
let parameters = reflector.parameters(NgFor);
|
||||
expect(parameters).toEqual([
|
||||
[ViewContainerRef], [TemplateRef], [IterableDiffers], [ChangeDetectorRef]
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get annotations for HeroDetailComponent', () => {
|
||||
let HeroDetailComponent =
|
||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||
let annotations = reflector.annotations(HeroDetailComponent);
|
||||
expect(annotations.length).toEqual(1);
|
||||
let annotation = annotations[0];
|
||||
expect(annotation.selector).toEqual('my-hero-detail');
|
||||
expect(annotation.directives).toEqual([[host.findDeclaration(
|
||||
'angular2/src/common/directives/ng_for', 'NgFor')]]);
|
||||
expect(annotation.animations).toEqual([trigger('myAnimation', [
|
||||
state('state1', style({'background': 'white'})),
|
||||
transition(
|
||||
'* => *',
|
||||
sequence([group([animate(
|
||||
'1s 0.5s',
|
||||
keyframes([style({'background': 'blue'}), style({'background': 'red'})]))])]))
|
||||
])]);
|
||||
});
|
||||
|
||||
it('should throw and exception for unsupported metadata versions', () => {
|
||||
let e = host.findDeclaration('src/version-error', 'e');
|
||||
expect(() => reflector.annotations(e))
|
||||
.toThrow(new Error(
|
||||
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1'));
|
||||
});
|
||||
|
||||
it('should get and empty annotation list for an unknown class', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let annotations = reflector.annotations(UnknownClass);
|
||||
expect(annotations).toEqual([]);
|
||||
});
|
||||
|
||||
it('should get propMetadata for HeroDetailComponent', () => {
|
||||
let HeroDetailComponent =
|
||||
host.findDeclaration('src/app/hero-detail.component', 'HeroDetailComponent');
|
||||
let props = reflector.propMetadata(HeroDetailComponent);
|
||||
expect(props['hero']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get an empty object from propMetadata for an unknown class', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let properties = reflector.propMetadata(UnknownClass);
|
||||
expect(properties).toEqual({});
|
||||
});
|
||||
|
||||
it('should get empty parameters list for an unknown class ', () => {
|
||||
let UnknownClass = host.findDeclaration('src/app/app.component', 'UnknownClass');
|
||||
let parameters = reflector.parameters(UnknownClass);
|
||||
expect(parameters).toEqual([]);
|
||||
});
|
||||
|
||||
it('should provide context for errors reported by the collector', () => {
|
||||
let SomeClass = host.findDeclaration('src/error-reporting', 'SomeClass');
|
||||
expect(() => reflector.annotations(SomeClass))
|
||||
.toThrow(new Error(
|
||||
'Error encountered resolving symbol values statically. A reasonable error message (position 12:33 in the original .ts file), resolving symbol ErrorSym in /tmp/src/error-references.d.ts, resolving symbol Link2 in /tmp/src/error-references.d.ts, resolving symbol Link1 in /tmp/src/error-references.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts'));
|
||||
});
|
||||
|
||||
it('should simplify primitive into itself', () => {
|
||||
expect(simplify(noContext, 1)).toBe(1);
|
||||
expect(simplify(noContext, true)).toBe(true);
|
||||
expect(simplify(noContext, 'some value')).toBe('some value');
|
||||
});
|
||||
|
||||
it('should simplify an array into a copy of the array', () => {
|
||||
expect(simplify(noContext, [1, 2, 3])).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should simplify an object to a copy of the object', () => {
|
||||
let expr = {a: 1, b: 2, c: 3};
|
||||
expect(simplify(noContext, expr)).toEqual(expr);
|
||||
});
|
||||
|
||||
it('should simplify &&', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&&', left: true, right: true})))
|
||||
.toBe(true);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&&', left: true, right: false})))
|
||||
.toBe(false);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&&', left: false, right: true})))
|
||||
.toBe(false);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&&', left: false, right: false})))
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify ||', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '||', left: true, right: true})))
|
||||
.toBe(true);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '||', left: true, right: false})))
|
||||
.toBe(true);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '||', left: false, right: true})))
|
||||
.toBe(true);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '||', left: false, right: false})))
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
it('should simplify &', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&', left: 0x22, right: 0x0F})))
|
||||
.toBe(0x22 & 0x0F);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '&', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 & 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify |', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F})))
|
||||
.toBe(0x22 | 0x0F);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ^', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '|', left: 0x22, right: 0x0F})))
|
||||
.toBe(0x22 | 0x0F);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '|', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 | 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ==', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '==', left: 0x22, right: 0x22})))
|
||||
.toBe(0x22 == 0x22);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '==', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 == 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !=', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '!=', left: 0x22, right: 0x22})))
|
||||
.toBe(0x22 != 0x22);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '!=', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 != 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify ===', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '===', left: 0x22, right: 0x22})))
|
||||
.toBe(0x22 === 0x22);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '===', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 === 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify !==', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '!==', left: 0x22, right: 0x22})))
|
||||
.toBe(0x22 !== 0x22);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '!==', left: 0x22, right: 0xF0})))
|
||||
.toBe(0x22 !== 0xF0);
|
||||
});
|
||||
|
||||
it('should simplify >', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>', left: 1, right: 1})))
|
||||
.toBe(1 > 1);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>', left: 1, right: 0})))
|
||||
.toBe(1 > 0);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>', left: 0, right: 1})))
|
||||
.toBe(0 > 1);
|
||||
});
|
||||
|
||||
it('should simplify >=', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>=', left: 1, right: 1})))
|
||||
.toBe(1 >= 1);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>=', left: 1, right: 0})))
|
||||
.toBe(1 >= 0);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>=', left: 0, right: 1})))
|
||||
.toBe(0 >= 1);
|
||||
});
|
||||
|
||||
it('should simplify <=', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<=', left: 1, right: 1})))
|
||||
.toBe(1 <= 1);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<=', left: 1, right: 0})))
|
||||
.toBe(1 <= 0);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<=', left: 0, right: 1})))
|
||||
.toBe(0 <= 1);
|
||||
});
|
||||
|
||||
it('should simplify <', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<', left: 1, right: 1})))
|
||||
.toBe(1 < 1);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<', left: 1, right: 0})))
|
||||
.toBe(1 < 0);
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<', left: 0, right: 1})))
|
||||
.toBe(0 < 1);
|
||||
});
|
||||
|
||||
it('should simplify <<', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '<<', left: 0x55, right: 2})))
|
||||
.toBe(0x55 << 2);
|
||||
});
|
||||
|
||||
it('should simplify >>', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '>>', left: 0x55, right: 2})))
|
||||
.toBe(0x55 >> 2);
|
||||
});
|
||||
|
||||
it('should simplify +', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '+', left: 0x55, right: 2})))
|
||||
.toBe(0x55 + 2);
|
||||
});
|
||||
|
||||
it('should simplify -', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '-', left: 0x55, right: 2})))
|
||||
.toBe(0x55 - 2);
|
||||
});
|
||||
|
||||
it('should simplify *', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '*', left: 0x55, right: 2})))
|
||||
.toBe(0x55 * 2);
|
||||
});
|
||||
|
||||
it('should simplify /', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '/', left: 0x55, right: 2})))
|
||||
.toBe(0x55 / 2);
|
||||
});
|
||||
|
||||
it('should simplify %', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'binop', operator: '%', left: 0x55, right: 2})))
|
||||
.toBe(0x55 % 2);
|
||||
});
|
||||
|
||||
it('should simplify prefix -', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'pre', operator: '-', operand: 2}))).toBe(-2);
|
||||
});
|
||||
|
||||
it('should simplify prefix ~', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'pre', operator: '~', operand: 2}))).toBe(~2);
|
||||
});
|
||||
|
||||
it('should simplify prefix !', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'pre', operator: '!', operand: true}))).toBe(!true);
|
||||
expect(simplify(noContext, ({__symbolic: 'pre', operator: '!', operand: false}))).toBe(!false);
|
||||
});
|
||||
|
||||
it('should simplify an array index', () => {
|
||||
expect(simplify(noContext, ({__symbolic: 'index', expression: [1, 2, 3], index: 2}))).toBe(3);
|
||||
});
|
||||
|
||||
it('should simplify an object index', () => {
|
||||
let expr = {__symbolic: 'select', expression: {a: 1, b: 2, c: 3}, member: 'b'};
|
||||
expect(simplify(noContext, expr)).toBe(2);
|
||||
});
|
||||
|
||||
it('should simplify a module reference', () => {
|
||||
expect(simplify(
|
||||
new StaticSymbol('/src/cases', ''),
|
||||
({__symbolic: 'reference', module: './extern', name: 's'})))
|
||||
.toEqual('s');
|
||||
});
|
||||
|
||||
it('should simplify a non existing reference as a static symbol', () => {
|
||||
expect(simplify(
|
||||
new StaticSymbol('/src/cases', ''),
|
||||
({__symbolic: 'reference', module: './extern', name: 'nonExisting'})))
|
||||
.toEqual(host.getStaticSymbol('/src/extern.d.ts', 'nonExisting'));
|
||||
});
|
||||
|
||||
it('should simplify values initialized with a function call', () => {
|
||||
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), {
|
||||
__symbolic: 'reference',
|
||||
name: 'one'
|
||||
})).toEqual(['some-value']);
|
||||
expect(simplify(new StaticSymbol('/tmp/src/function-reference.ts', ''), {
|
||||
__symbolic: 'reference',
|
||||
name: 'two'
|
||||
})).toEqual(2);
|
||||
});
|
||||
|
||||
it('should error on direct recursive calls', () => {
|
||||
expect(
|
||||
() => simplify(
|
||||
new StaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||
{__symbolic: 'reference', name: 'recursion'}))
|
||||
.toThrow(new Error(
|
||||
'Recursion not supported, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
||||
});
|
||||
|
||||
it('should error on indirect recursive calls', () => {
|
||||
expect(
|
||||
() => simplify(
|
||||
new StaticSymbol('/tmp/src/function-reference.ts', ''),
|
||||
{__symbolic: 'reference', name: 'indirectRecursion'}))
|
||||
.toThrow(new Error(
|
||||
'Recursion not supported, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
|
||||
});
|
||||
|
||||
it('should simplify a spread expression', () => {
|
||||
expect(simplify(new StaticSymbol('/tmp/src/spread.ts', ''), {
|
||||
__symbolic: 'reference',
|
||||
name: 'spread'
|
||||
})).toEqual([0, 1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
it('should be able to get metadata from a ts file', () => {
|
||||
let metadata = reflector.getModuleMetadata('/tmp/src/custom-decorator-reference.ts');
|
||||
expect(metadata).toEqual({
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
Foo: {
|
||||
__symbolic: 'class',
|
||||
decorators: [{
|
||||
__symbolic: 'call',
|
||||
expression:
|
||||
{__symbolic: 'reference', module: './custom-decorator', name: 'CustomDecorator'}
|
||||
}],
|
||||
members: {
|
||||
foo: [{
|
||||
__symbolic: 'property',
|
||||
decorators: [{
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './custom-decorator',
|
||||
name: 'CustomDecorator'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get metadata for a class containing a custom decorator', () => {
|
||||
let props = reflector.propMetadata(
|
||||
host.getStaticSymbol('/tmp/src/custom-decorator-reference.ts', 'Foo'));
|
||||
expect(props).toEqual({foo: []});
|
||||
});
|
||||
|
||||
it('should report an error for invalid function calls', () => {
|
||||
expect(
|
||||
() =>
|
||||
reflector.annotations(host.getStaticSymbol('/tmp/src/invalid-calls.ts', 'MyComponent')))
|
||||
.toThrow(new Error(
|
||||
`Error encountered resolving symbol values statically. Calling function 'someFunction', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol MyComponent in /tmp/src/invalid-calls.ts, resolving symbol MyComponent in /tmp/src/invalid-calls.ts`));
|
||||
});
|
||||
});
|
||||
|
||||
class MockReflectorHost implements StaticReflectorHost {
|
||||
private staticTypeCache = new Map<string, StaticSymbol>();
|
||||
private collector = new MetadataCollector();
|
||||
|
||||
constructor() {}
|
||||
|
||||
angularImportLocations() {
|
||||
return {
|
||||
coreDecorators: 'angular2/src/core/metadata',
|
||||
diDecorators: 'angular2/src/core/di/decorators',
|
||||
diMetadata: 'angular2/src/core/di/metadata',
|
||||
diOpaqueToken: 'angular2/src/core/di/opaque_token',
|
||||
animationMetadata: 'angular2/src/core/animation/metadata',
|
||||
provider: 'angular2/src/core/di/provider'
|
||||
};
|
||||
}
|
||||
getStaticSymbol(declarationFile: string, name: string): StaticSymbol {
|
||||
var cacheKey = `${declarationFile}:${name}`;
|
||||
var result = this.staticTypeCache.get(cacheKey);
|
||||
if (isBlank(result)) {
|
||||
result = new StaticSymbol(declarationFile, name);
|
||||
this.staticTypeCache.set(cacheKey, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// In tests, assume that symbols are not re-exported
|
||||
findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol {
|
||||
function splitPath(path: string): string[] { return path.split(/\/|\\/g); }
|
||||
|
||||
function resolvePath(pathParts: string[]): string {
|
||||
let result: string[] = [];
|
||||
ListWrapper.forEachWithIndex(pathParts, (part, index) => {
|
||||
switch (part) {
|
||||
case '':
|
||||
case '.':
|
||||
if (index > 0) return;
|
||||
break;
|
||||
case '..':
|
||||
if (index > 0 && result.length != 0) result.pop();
|
||||
return;
|
||||
}
|
||||
result.push(part);
|
||||
});
|
||||
return result.join('/');
|
||||
}
|
||||
|
||||
function pathTo(from: string, to: string): string {
|
||||
let result = to;
|
||||
if (to.startsWith('.')) {
|
||||
let fromParts = splitPath(from);
|
||||
fromParts.pop(); // remove the file name.
|
||||
let toParts = splitPath(to);
|
||||
result = resolvePath(fromParts.concat(toParts));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (modulePath.indexOf('.') === 0) {
|
||||
return this.getStaticSymbol(pathTo(containingFile, modulePath) + '.d.ts', symbolName);
|
||||
}
|
||||
return this.getStaticSymbol('/tmp/' + modulePath + '.d.ts', symbolName);
|
||||
}
|
||||
|
||||
getMetadataFor(moduleId: string): any {
|
||||
let data: {[key: string]: any} = {
|
||||
'/tmp/angular2/src/common/forms-deprecated/directives.d.ts': [{
|
||||
'__symbolic': 'module',
|
||||
'version': 1,
|
||||
'metadata': {
|
||||
'FORM_DIRECTIVES': [
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'name': 'NgFor',
|
||||
'module': 'angular2/src/common/directives/ng_for'
|
||||
}
|
||||
]
|
||||
}
|
||||
}],
|
||||
'/tmp/angular2/src/common/directives/ng_for.d.ts': {
|
||||
'__symbolic': 'module',
|
||||
'version': 1,
|
||||
'metadata': {
|
||||
'NgFor': {
|
||||
'__symbolic': 'class',
|
||||
'decorators': [
|
||||
{
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'Directive',
|
||||
'module': '../../core/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
{
|
||||
'selector': '[ngFor][ngForOf]',
|
||||
'inputs': ['ngForTrackBy', 'ngForOf', 'ngForTemplate']
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'members': {
|
||||
'__ctor__': [
|
||||
{
|
||||
'__symbolic': 'constructor',
|
||||
'parameters': [
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'module': '../../core/linker/view_container_ref',
|
||||
'name': 'ViewContainerRef'
|
||||
},
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'module': '../../core/linker/template_ref',
|
||||
'name': 'TemplateRef'
|
||||
},
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'module': '../../core/change_detection/differs/iterable_differs',
|
||||
'name': 'IterableDiffers'
|
||||
},
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'module': '../../core/change_detection/change_detector_ref',
|
||||
'name': 'ChangeDetectorRef'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/angular2/src/core/linker/view_container_ref.d.ts':
|
||||
{version: 1, 'metadata': {'ViewContainerRef': {'__symbolic': 'class'}}},
|
||||
'/tmp/angular2/src/core/linker/template_ref.d.ts':
|
||||
{version: 1, 'module': './template_ref', 'metadata': {'TemplateRef': {'__symbolic': 'class'}}},
|
||||
'/tmp/angular2/src/core/change_detection/differs/iterable_differs.d.ts':
|
||||
{version: 1, 'metadata': {'IterableDiffers': {'__symbolic': 'class'}}},
|
||||
'/tmp/angular2/src/core/change_detection/change_detector_ref.d.ts':
|
||||
{version: 1, 'metadata': {'ChangeDetectorRef': {'__symbolic': 'class'}}},
|
||||
'/tmp/src/app/hero-detail.component.d.ts': {
|
||||
'__symbolic': 'module',
|
||||
'version': 1,
|
||||
'metadata': {
|
||||
'HeroDetailComponent': {
|
||||
'__symbolic': 'class',
|
||||
'decorators': [
|
||||
{
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'Component',
|
||||
'module': 'angular2/src/core/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
{
|
||||
'selector': 'my-hero-detail',
|
||||
'template':
|
||||
'\n <div *ngIf="hero">\n <h2>{{hero.name}} details!</h2>\n <div><label>id: </label>{{hero.id}}</div>\n <div>\n <label>name: </label>\n <input [(ngModel)]="hero.name" placeholder="name"/>\n </div>\n </div>\n',
|
||||
'directives': [
|
||||
{
|
||||
'__symbolic': 'reference',
|
||||
'name': 'FORM_DIRECTIVES',
|
||||
'module': 'angular2/src/common/forms-deprecated/directives'
|
||||
}
|
||||
],
|
||||
'animations': [{
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'trigger',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
'myAnimation',
|
||||
[{ '__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'state',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
'state1',
|
||||
{ '__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'style',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
{ 'background':'white' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}, {
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'transition',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments': [
|
||||
'* => *',
|
||||
{
|
||||
'__symbolic':'call',
|
||||
'expression':{
|
||||
'__symbolic':'reference',
|
||||
'name':'sequence',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[[{ '__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'group',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[[{
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'animate',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[
|
||||
'1s 0.5s',
|
||||
{ '__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'keyframes',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[[{ '__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'style',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[ { 'background': 'blue'} ]
|
||||
}, {
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic':'reference',
|
||||
'name':'style',
|
||||
'module': 'angular2/src/core/animation/metadata'
|
||||
},
|
||||
'arguments':[ { 'background': 'red'} ]
|
||||
}]]
|
||||
}
|
||||
]
|
||||
}]]
|
||||
}]]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
'members': {
|
||||
'hero': [
|
||||
{
|
||||
'__symbolic': 'property',
|
||||
'decorators': [
|
||||
{
|
||||
'__symbolic': 'call',
|
||||
'expression': {
|
||||
'__symbolic': 'reference',
|
||||
'name': 'Input',
|
||||
'module': 'angular2/src/core/metadata'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/src/extern.d.ts': {'__symbolic': 'module', 'version': 1, metadata: {s: 's'}},
|
||||
'/tmp/src/version-error.d.ts': {'__symbolic': 'module', 'version': 100, metadata: {e: 's'}},
|
||||
'/tmp/src/error-reporting.d.ts': {
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
SomeClass: {
|
||||
__symbolic: 'class',
|
||||
decorators: [
|
||||
{
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
name: 'Component',
|
||||
module: 'angular2/src/core/metadata'
|
||||
},
|
||||
arguments: [
|
||||
{
|
||||
directives: [
|
||||
{
|
||||
__symbolic: 'reference',
|
||||
module: 'src/error-references',
|
||||
name: 'Link1',
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/src/error-references.d.ts': {
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
Link1: {
|
||||
__symbolic: 'reference',
|
||||
module: 'src/error-references',
|
||||
name: 'Link2'
|
||||
},
|
||||
Link2: {
|
||||
__symbolic: 'reference',
|
||||
module: 'src/error-references',
|
||||
name: 'ErrorSym'
|
||||
},
|
||||
ErrorSym: {
|
||||
__symbolic: 'error',
|
||||
message: 'A reasonable error message',
|
||||
line: 12,
|
||||
character: 33
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/src/function-declaration.d.ts': {
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
one: {
|
||||
__symbolic: 'function',
|
||||
parameters: ['a'],
|
||||
value: [
|
||||
{__symbolic: 'reference', name: 'a'}
|
||||
]
|
||||
},
|
||||
add: {
|
||||
__symbolic: 'function',
|
||||
parameters: ['a','b'],
|
||||
value: {
|
||||
__symbolic: 'binop',
|
||||
operator: '+',
|
||||
left: {__symbolic: 'reference', name: 'a'},
|
||||
right: {__symbolic: 'reference', name: 'b'}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/src/function-reference.ts': {
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
one: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-declaration',
|
||||
name: 'one'
|
||||
},
|
||||
arguments: ['some-value']
|
||||
},
|
||||
two: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-declaration',
|
||||
name: 'add'
|
||||
},
|
||||
arguments: [1, 1]
|
||||
},
|
||||
recursion: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-recursive',
|
||||
name: 'recursive'
|
||||
},
|
||||
arguments: [1]
|
||||
},
|
||||
indirectRecursion: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-recursive',
|
||||
name: 'indirectRecursion1'
|
||||
},
|
||||
arguments: [1]
|
||||
}
|
||||
}
|
||||
},
|
||||
'/tmp/src/function-recursive.d.ts': {
|
||||
__symbolic: 'modules',
|
||||
version: 1,
|
||||
metadata: {
|
||||
recursive: {
|
||||
__symbolic: 'function',
|
||||
parameters: ['a'],
|
||||
value: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-recursive',
|
||||
name: 'recursive',
|
||||
},
|
||||
arguments: [
|
||||
{
|
||||
__symbolic: 'reference',
|
||||
name: 'a'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
indirectRecursion1: {
|
||||
__symbolic: 'function',
|
||||
parameters: ['a'],
|
||||
value: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-recursive',
|
||||
name: 'indirectRecursion2',
|
||||
},
|
||||
arguments: [
|
||||
{
|
||||
__symbolic: 'reference',
|
||||
name: 'a'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
indirectRecursion2: {
|
||||
__symbolic: 'function',
|
||||
parameters: ['a'],
|
||||
value: {
|
||||
__symbolic: 'call',
|
||||
expression: {
|
||||
__symbolic: 'reference',
|
||||
module: './function-recursive',
|
||||
name: 'indirectRecursion1',
|
||||
},
|
||||
arguments: [
|
||||
{
|
||||
__symbolic: 'reference',
|
||||
name: 'a'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
'/tmp/src/spread.ts': {
|
||||
__symbolic: 'module',
|
||||
version: 1,
|
||||
metadata: {
|
||||
spread: [0, {__symbolic: 'spread', expression: [1, 2, 3, 4]}, 5]
|
||||
}
|
||||
},
|
||||
'/tmp/src/custom-decorator.ts': `
|
||||
export function CustomDecorator(): any {
|
||||
return () => {};
|
||||
}
|
||||
`,
|
||||
'/tmp/src/custom-decorator-reference.ts': `
|
||||
import {CustomDecorator} from './custom-decorator';
|
||||
|
||||
@CustomDecorator()
|
||||
export class Foo {
|
||||
@CustomDecorator() get foo(): string { return ''; }
|
||||
}
|
||||
`,
|
||||
'/tmp/src/invalid-calll-definitions.ts': `
|
||||
export function someFunction(a: any) {
|
||||
if (Array.isArray(a)) {
|
||||
return a;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
`,
|
||||
'/tmp/src/invalid-calls.ts': `
|
||||
import {someFunction} from './nvalid-calll-definitions.ts';
|
||||
import {Component} from 'angular2/src/core/metadata';
|
||||
import {NgIf} from 'angular2/common';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
directives: [someFunction([NgIf])]
|
||||
})
|
||||
export class MyComponent {}
|
||||
|
||||
@someFunction()
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
directives: [NgIf]
|
||||
})
|
||||
export class MyOtherComponent { }
|
||||
`
|
||||
};
|
||||
|
||||
|
||||
if (data[moduleId] && moduleId.match(TS_EXT)) {
|
||||
let text = data[moduleId];
|
||||
if (typeof text === 'string') {
|
||||
let sf = ts.createSourceFile(moduleId, data[moduleId], ts.ScriptTarget.ES5);
|
||||
let diagnostics: ts.Diagnostic[] = (<any>sf).parseDiagnostics;
|
||||
if (diagnostics && diagnostics.length) {
|
||||
throw Error(`Error encountered during parse of file ${moduleId}`);
|
||||
}
|
||||
return this.collector.getMetadata(sf);
|
||||
}
|
||||
}
|
||||
return data[moduleId];
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||
"@angular/platform-server": ["../../../dist/packages-dist/platform-server"],
|
||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||
"@angular/tsc-wrapped": ["../../../dist/tools/@angular/tsc-wrapped"]
|
||||
},
|
||||
"experimentalDecorators": true,
|
||||
"rootDir": ".",
|
||||
"sourceRoot": ".",
|
||||
"outDir": "../../../dist/packages-dist/compiler-cli",
|
||||
"declaration": true
|
||||
},
|
||||
"exclude": ["integrationtest"],
|
||||
"files": [
|
||||
"index.ts",
|
||||
"src/main.ts",
|
||||
"src/extract_i18n.ts",
|
||||
"../../../node_modules/@types/node/index.d.ts",
|
||||
"../../../node_modules/@types/jasmine/index.d.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||
]
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Starting point to import all compiler APIs.
|
||||
*/
|
||||
export {COMPILER_PROVIDERS, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileFactoryMetadata, CompileIdentifierMetadata, CompileMetadataWithIdentifier, CompileMetadataWithType, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata, CompilerConfig, DEFAULT_PACKAGE_URL_PROVIDER, DirectiveResolver, NormalizedComponentWithViewDirectives, OfflineCompiler, PipeResolver, RenderTypes, RuntimeCompiler, SourceModule, TEMPLATE_TRANSFORMS, UrlResolver, ViewResolver, XHR, createOfflineCompileUrlResolver} from './src/compiler';
|
||||
export {ElementSchemaRegistry} from './src/schema/element_schema_registry';
|
||||
|
||||
export * from './src/template_ast';
|
||||
export * from './private_export';
|
@ -1 +0,0 @@
|
||||
export '../core/private_export.dart';
|
@ -1,90 +0,0 @@
|
||||
import {__core_private__ as r, __core_private_types__ as t} from '@angular/core';
|
||||
|
||||
export var isDefaultChangeDetectionStrategy: typeof t.isDefaultChangeDetectionStrategy =
|
||||
r.isDefaultChangeDetectionStrategy;
|
||||
export type ChangeDetectorState = t.ChangeDetectorState;
|
||||
export var ChangeDetectorState: typeof t.ChangeDetectorState = r.ChangeDetectorState;
|
||||
export var CHANGE_DETECTION_STRATEGY_VALUES: typeof t.CHANGE_DETECTION_STRATEGY_VALUES =
|
||||
r.CHANGE_DETECTION_STRATEGY_VALUES;
|
||||
export var constructDependencies: typeof t.constructDependencies = r.constructDependencies;
|
||||
export type LifecycleHooks = t.LifecycleHooks;
|
||||
export var LifecycleHooks: typeof t.LifecycleHooks = r.LifecycleHooks;
|
||||
export var LIFECYCLE_HOOKS_VALUES: typeof t.LIFECYCLE_HOOKS_VALUES = r.LIFECYCLE_HOOKS_VALUES;
|
||||
export type ReflectorReader = t.ReflectorReader;
|
||||
export var ReflectorReader: typeof t.ReflectorReader = r.ReflectorReader;
|
||||
export var ReflectorComponentResolver: typeof t.ReflectorComponentResolver =
|
||||
r.ReflectorComponentResolver;
|
||||
export type AppElement = t.AppElement;
|
||||
export var AppElement: typeof t.AppElement = r.AppElement;
|
||||
export var AppView: typeof t.AppView = r.AppView;
|
||||
export type DebugAppView<T> = t.DebugAppView<T>;
|
||||
export var DebugAppView: typeof t.DebugAppView = r.DebugAppView;
|
||||
export type ViewType = t.ViewType;
|
||||
export var ViewType: typeof t.ViewType = r.ViewType;
|
||||
export var MAX_INTERPOLATION_VALUES: typeof t.MAX_INTERPOLATION_VALUES = r.MAX_INTERPOLATION_VALUES;
|
||||
export var checkBinding: typeof t.checkBinding = r.checkBinding;
|
||||
export var flattenNestedViewRenderNodes: typeof t.flattenNestedViewRenderNodes =
|
||||
r.flattenNestedViewRenderNodes;
|
||||
export var interpolate: typeof t.interpolate = r.interpolate;
|
||||
export var ViewUtils: typeof t.ViewUtils = r.ViewUtils;
|
||||
export var VIEW_ENCAPSULATION_VALUES: typeof t.VIEW_ENCAPSULATION_VALUES =
|
||||
r.VIEW_ENCAPSULATION_VALUES;
|
||||
export var DebugContext: typeof t.DebugContext = r.DebugContext;
|
||||
export var StaticNodeDebugInfo: typeof t.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
|
||||
export var devModeEqual: typeof t.devModeEqual = r.devModeEqual;
|
||||
export var uninitialized: typeof t.uninitialized = r.uninitialized;
|
||||
export var ValueUnwrapper: typeof t.ValueUnwrapper = r.ValueUnwrapper;
|
||||
export var TemplateRef_: typeof t.TemplateRef_ = r.TemplateRef_;
|
||||
export type RenderDebugInfo = t.RenderDebugInfo;
|
||||
export var RenderDebugInfo: typeof t.RenderDebugInfo = r.RenderDebugInfo;
|
||||
export var SecurityContext: typeof t.SecurityContext = r.SecurityContext;
|
||||
export type SecurityContext = t.SecurityContext;
|
||||
export var SanitizationService: typeof t.SanitizationService = r.SanitizationService;
|
||||
export type SanitizationService = t.SanitizationService;
|
||||
export var createProvider: typeof t.createProvider = r.createProvider;
|
||||
export var isProviderLiteral: typeof t.isProviderLiteral = r.isProviderLiteral;
|
||||
export var EMPTY_ARRAY: typeof t.EMPTY_ARRAY = r.EMPTY_ARRAY;
|
||||
export var EMPTY_MAP: typeof t.EMPTY_MAP = r.EMPTY_MAP;
|
||||
export var pureProxy1: typeof t.pureProxy1 = r.pureProxy1;
|
||||
export var pureProxy2: typeof t.pureProxy2 = r.pureProxy2;
|
||||
export var pureProxy3: typeof t.pureProxy3 = r.pureProxy3;
|
||||
export var pureProxy4: typeof t.pureProxy4 = r.pureProxy4;
|
||||
export var pureProxy5: typeof t.pureProxy5 = r.pureProxy5;
|
||||
export var pureProxy6: typeof t.pureProxy6 = r.pureProxy6;
|
||||
export var pureProxy7: typeof t.pureProxy7 = r.pureProxy7;
|
||||
export var pureProxy8: typeof t.pureProxy8 = r.pureProxy8;
|
||||
export var pureProxy9: typeof t.pureProxy9 = r.pureProxy9;
|
||||
export var pureProxy10: typeof t.pureProxy10 = r.pureProxy10;
|
||||
export var castByValue: typeof t.castByValue = r.castByValue;
|
||||
export type Console = t.Console;
|
||||
export var Console: typeof t.Console = r.Console;
|
||||
export var reflector: t.Reflector = r.reflector;
|
||||
export type Reflector = t.Reflector;
|
||||
export type NoOpAnimationPlayer = t.NoOpAnimationPlayer;
|
||||
export var NoOpAnimationPlayer: typeof t.NoOpAnimationPlayer = r.NoOpAnimationPlayer;
|
||||
export type AnimationPlayer = t.AnimationPlayer;
|
||||
export var AnimationPlayer: typeof t.AnimationPlayer = r.AnimationPlayer;
|
||||
export type NoOpAnimationDriver = t.NoOpAnimationDriver;
|
||||
export var NoOpAnimationDriver: typeof t.NoOpAnimationDriver = r.NoOpAnimationDriver;
|
||||
export type AnimationDriver = t.AnimationDriver;
|
||||
export var AnimationDriver: typeof t.AnimationDriver = r.AnimationDriver;
|
||||
export type AnimationSequencePlayer = t.AnimationSequencePlayer;
|
||||
export var AnimationSequencePlayer: typeof t.AnimationSequencePlayer = r.AnimationSequencePlayer;
|
||||
export type AnimationGroupPlayer = t.AnimationGroupPlayer;
|
||||
export var AnimationGroupPlayer: typeof t.AnimationGroupPlayer = r.AnimationGroupPlayer;
|
||||
export type AnimationKeyframe = t.AnimationKeyframe;
|
||||
export var AnimationKeyframe: typeof t.AnimationKeyframe = r.AnimationKeyframe;
|
||||
export type AnimationStyles = t.AnimationStyles;
|
||||
export var AnimationStyles: typeof t.AnimationStyles = r.AnimationStyles;
|
||||
export var ANY_STATE = r.ANY_STATE;
|
||||
export var DEFAULT_STATE = r.DEFAULT_STATE;
|
||||
export var EMPTY_STATE = r.EMPTY_STATE;
|
||||
export var FILL_STYLE_FLAG = r.FILL_STYLE_FLAG;
|
||||
export var prepareFinalAnimationStyles: typeof t.prepareFinalAnimationStyles =
|
||||
r.prepareFinalAnimationStyles;
|
||||
export var balanceAnimationKeyframes: typeof t.balanceAnimationKeyframes =
|
||||
r.balanceAnimationKeyframes;
|
||||
export var flattenStyles: typeof t.flattenStyles = r.flattenStyles;
|
||||
export var clearStyles: typeof t.clearStyles = r.clearStyles;
|
||||
export var collectAndResolveStyles: typeof r.collectAndResolveStyles = r.collectAndResolveStyles;
|
||||
export var renderStyles: typeof t.renderStyles = r.renderStyles;
|
@ -1 +0,0 @@
|
||||
export * from './compiler';
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "@angular/compiler",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"jsnext:main": "esm/index.js",
|
||||
"typings": "index.d.ts",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export './src/core/change_detection/constants.dart' show SelectorMatcher, CssSelector;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user