Compare commits

..

2 Commits

1870 changed files with 74210 additions and 100704 deletions

View File

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

View File

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

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

View File

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

View File

@ -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:** dont 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:** dont 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:** dont 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>

View File

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

View File

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

View File

@ -1,20 +1,18 @@
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/angular/tree/master)
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/angular2.svg)](http://badge.fury.io/js/angular2)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](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

View File

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

View File

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

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1 +0,0 @@
export 'index.dart';

View File

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

View File

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

View File

@ -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 }),
]
}

View File

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

View File

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

View File

@ -1 +0,0 @@
../../facade/src

View File

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

View File

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

View File

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

View File

@ -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');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'); }
}

View File

@ -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]; }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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');
});
});
}
});
}

View File

@ -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(); });
});
});
}

View File

@ -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(); });
});
});
}

View File

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

View File

@ -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(); });
});
});
}

View File

@ -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(); });
});
});
}
});
}

View File

@ -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');
});
});
});
}

View File

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

View File

@ -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(); });
});
});
}

View File

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

View File

@ -1,2 +0,0 @@
export {SpyLocation} from './testing/location_mock';
export {MockLocationStrategy} from './testing/mock_location_strategy';

View File

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

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +0,0 @@
@import './shared.css';
.green { color: green }

View File

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

View File

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

View File

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

View File

@ -1,2 +0,0 @@
// Verify we don't try to extract metadata for .d.ts files
export declare var a: string;

View File

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

View File

@ -1,3 +0,0 @@
export function wrapInArray(value: any): any[] {
return [value];
}

View File

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

View File

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

View File

@ -1 +0,0 @@
.blue { color: blue }

View File

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

View File

@ -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');
});
});

View File

@ -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');
});
});

View File

@ -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!');
});
});

View File

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

View File

@ -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": "."
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'); }
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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];
}
}

View File

@ -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"
]
}

View File

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

View File

@ -1 +0,0 @@
export '../core/private_export.dart';

View File

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

View File

@ -1 +0,0 @@
export * from './compiler';

View File

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

View File

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