Compare commits
174 Commits
2.0.0-beta
...
2.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
0898bca939 | |||
d940387beb | |||
27c45b05cf | |||
90f09d551f | |||
3739588e97 | |||
0730b753f2 | |||
cad693de0f | |||
bf911fc992 | |||
fb6d791ce9 | |||
0f8efce799 | |||
69c1405196 | |||
980491b08f | |||
72e24663ad | |||
363ed5140e | |||
b0f585ab08 | |||
8b67b07580 | |||
5c330ea492 | |||
06eaaf0ac5 | |||
9820271243 | |||
b6507e37ef | |||
c194f6695d | |||
967ae3e1b8 | |||
a5e6eaaebc | |||
d4e9b55fb6 | |||
048bd280dd | |||
8326ab3240 | |||
a7fe983be2 | |||
e1f8e54e34 | |||
2b165944ea | |||
ea11b3f1f8 | |||
3bd87147ab | |||
2f581ffc88 | |||
d61aaac400 | |||
310620fd12 | |||
f9fb72fb0e | |||
095db673c5 | |||
70d18b5b53 | |||
f1796d67f4 | |||
cb38d72ff4 | |||
b72bab49aa | |||
201475e8d8 | |||
c25b9fcf97 | |||
8755a8e188 | |||
127fbfd5a6 | |||
f33dda79e9 | |||
293fa5505b | |||
df1f78e302 | |||
43bb31c6c6 | |||
169869a195 | |||
b691da26af | |||
8e3e45097a | |||
aa43d2f87b | |||
128acbb6eb | |||
5824866a83 | |||
0d58b137a7 | |||
b5c769e1e4 | |||
7f22bd62ab | |||
83f0e7c975 | |||
adef68b4d6 | |||
14f0e9ada8 | |||
ef9e40e82b | |||
41e38e4330 | |||
2c7c3e3c69 | |||
756f5d884f | |||
45fd6f0a41 | |||
75ae4a9159 | |||
37d18d0112 | |||
773fe8f8c5 | |||
4da2b19ea0 | |||
9f3547e35d | |||
5a79358727 | |||
85bfbc13c1 | |||
1a01af9e68 | |||
dd95e901df | |||
9782d8c32e | |||
80764c6f71 | |||
d9e78e4fa8 | |||
10fedd0dfc | |||
315e73c47c | |||
912717ff31 | |||
b857fd1eeb | |||
6dce4f49c2 | |||
15e16148f4 | |||
ae49085481 | |||
11e8aa26f6 | |||
81beb1c788 | |||
6402d61f69 | |||
5a59e44765 | |||
7455b907d1 | |||
579b890446 | |||
19a08f3a43 | |||
a3d7629134 | |||
bc9644e86e | |||
a10c02cb41 | |||
9936e347ff | |||
7d44b8230e | |||
75343eb340 | |||
2548ce86db | |||
5586c29492 | |||
1174473e9c | |||
1d49b3e36b | |||
2830df4190 | |||
143cf89b5f | |||
69c1694900 | |||
01fe7f5fac | |||
9aedef208f | |||
39b6e0efba | |||
f60fa14767 | |||
d900f5c075 | |||
391a9edabb | |||
28a78117eb | |||
eeb594c010 | |||
0bb10d6bb6 | |||
59629a0801 | |||
b5e6319fa9 | |||
c9a3df970b | |||
f72f137261 | |||
ee3c580e88 | |||
05c185a7b1 | |||
b47f80ec76 | |||
ebd438ff5e | |||
331b9c1317 | |||
4a93f58b8b | |||
ebe531bf92 | |||
1779caf5f8 | |||
6ef2121e6a | |||
38cb526f60 | |||
f6a8d04c32 | |||
4b3b5d7c53 | |||
abff302e52 | |||
e1f6679c75 | |||
aaafdf03ce | |||
ee298baa1b | |||
d1abada5b7 | |||
ab36ea097b | |||
8bb66a5eb3 | |||
cfc1e56dd8 | |||
a1c3be21ec | |||
edad8e3f56 | |||
d4a4d81173 | |||
e7470d557d | |||
b634a25ae0 | |||
c1a0af514f | |||
c6afea61f1 | |||
ce10fe92b2 | |||
b81b1fb81c | |||
280b86ec55 | |||
2f5a2ba671 | |||
c45ec6f1be | |||
530470e0ce | |||
ce72ccf9e8 | |||
46d9c87ddc | |||
d736c31fea | |||
a7e9bc97f6 | |||
265703b950 | |||
ae275fa4e4 | |||
3478d5d450 | |||
e72dc16dbe | |||
40a043275d | |||
f161b5cc28 | |||
117d57e121 | |||
3dcce706fd | |||
efb89b83e1 | |||
3d96c2337f | |||
19cfb4eb12 | |||
3d715a2f7b | |||
c7261c295c | |||
1a26f8edd6 | |||
fc887774da | |||
7cbf88a691 | |||
1cb1c139cf | |||
1fd924f7d5 | |||
eb688f2c8e | |||
61cf499b0b |
16
.github/ISSUE_TEMPLATE.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
**IMPORTANT**: This repository's issues are reserved for feature requests and bug reports. Do not submit support requests here, see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question.
|
||||
|
||||
|
||||
**Steps to reproduce and a minimal demo of the problem**
|
||||
|
||||
_Use https://plnkr.co or similar -- try this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5_
|
||||
|
||||
_What steps should we try in your demo to see the problem?_
|
||||
|
||||
**Current behavior**
|
||||
|
||||
|
||||
**Expected/desired behavior**
|
||||
|
||||
|
||||
**Other information**
|
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
* **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?** (Bug fix, feature, docs update, ...)
|
||||
|
||||
|
||||
|
||||
* **What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
* **What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
|
||||
* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?)
|
||||
|
||||
|
||||
|
||||
* **Other information**:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,6 +22,7 @@ tmp
|
||||
*.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
|
||||
|
||||
|
159
.travis.yml
159
.travis.yml
@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '5.4.1'
|
||||
- '5.4.1'
|
||||
|
||||
branches:
|
||||
except:
|
||||
@ -9,73 +9,94 @@ branches:
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- $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:
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- E2E_BROWSERS=Dartium
|
||||
- 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
|
||||
# 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="
|
||||
# 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 DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
# Disable dart dev build, which is timing out after 2h. #6823
|
||||
# - MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=dart_ddc DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=build_only DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=payload DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- 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: "MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
|
||||
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- 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:
|
||||
- npm install -g npm@3.5.3
|
||||
- node tools/analytics/build-analytics start ci job
|
||||
- node tools/analytics/build-analytics start ci before_install
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
|
||||
- '[ "${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
|
||||
- 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
|
||||
# Check the size of caches
|
||||
# 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
|
||||
@ -84,33 +105,33 @@ install:
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
- if [[ $TRAVIS_TEST_RESULT -eq 0 ]]; then node tools/analytics/build-analytics success ci job; else node tools/analytics/build-analytics error ci job; fi
|
||||
- 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
|
||||
- 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: false # default: false
|
||||
on_start: never # default: never
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
||||
|
266
CHANGELOG.md
266
CHANGELOG.md
@ -1,3 +1,269 @@
|
||||
<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))
|
||||
|
||||
|
||||
<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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular_1_router:** Renamed require statements after TypeScript files are transpiled ([ae49085](https://github.com/angular/angular/commit/ae49085)), closes [#7049](https://github.com/angular/angular/issues/7049)
|
||||
* **angular1_router:** rename `router` component binding to `$router` ([2548ce8](https://github.com/angular/angular/commit/2548ce8))
|
||||
* **angular1_router:** rename `router` component binding to `$router` ([1174473](https://github.com/angular/angular/commit/1174473))
|
||||
* **angular1_router:** support templateUrl components ([5586c29](https://github.com/angular/angular/commit/5586c29))
|
||||
* **build:** Use fixed version of Chromium Canary that will be updated manually instead of au ([1d49b3e](https://github.com/angular/angular/commit/1d49b3e))
|
||||
* **router:** support outlets within dynamic components ([7d44b82](https://github.com/angular/angular/commit/7d44b82))
|
||||
|
||||
### Features
|
||||
|
||||
* **angular1_router:** Add ng-link-active class to active ng-link ([11e8aa2](https://github.com/angular/angular/commit/11e8aa2)), closes [#6882](https://github.com/angular/angular/issues/6882)
|
||||
* **compiler:** Added spans to HTML parser errors ([19a08f3](https://github.com/angular/angular/commit/19a08f3))
|
||||
* **dart:** Add a dev-mode check for undeclared lifecycle interfaces ([a3d7629](https://github.com/angular/angular/commit/a3d7629)), closes [#6849](https://github.com/angular/angular/issues/6849)
|
||||
* **dart/transform:** Create standalone transformers for phases ([15e1614](https://github.com/angular/angular/commit/15e1614))
|
||||
* **iterable_differ:** support immutable lists ([a10c02c](https://github.com/angular/angular/commit/a10c02c)), closes [#7127](https://github.com/angular/angular/issues/7127)
|
||||
* **router:** add regex matchers ([75343eb](https://github.com/angular/angular/commit/75343eb)), closes [#7325](https://github.com/angular/angular/issues/7325) [#7126](https://github.com/angular/angular/issues/7126)
|
||||
* **router:** Added method to get current instruction ([6dce4f4](https://github.com/angular/angular/commit/6dce4f4))
|
||||
* **transformers:** change 'Missing Identifier' to be an error ([45fd6f0](https://github.com/angular/angular/commit/45fd6f0)), closes [#7403](https://github.com/angular/angular/issues/7403)
|
||||
* **transformers:** collect provider information ([81beb1c](https://github.com/angular/angular/commit/81beb1c))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* 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: '<'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* 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: '<'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0-beta.8"></a>
|
||||
# 2.0.0-beta.8 (2016-03-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular1_router:** rename `$route` service to `$rootRouter` ([a1c3be2](https://github.com/angular/angular/commit/a1c3be2))
|
||||
* **angular1_router:** rename `router` component binding to `$router` ([edad8e3](https://github.com/angular/angular/commit/edad8e3))
|
||||
* **angular1_router:** support templateUrl components ([d4a4d81](https://github.com/angular/angular/commit/d4a4d81))
|
||||
* **change_detection:** allow to destroy `OnPush` components inside of a host event. ([280b86e](https://github.com/angular/angular/commit/280b86e))
|
||||
* **change_detection:** allow to destroy `OnPush` components inside of a host event. ([ebd438f](https://github.com/angular/angular/commit/ebd438f)), closes [#7192](https://github.com/angular/angular/issues/7192)
|
||||
* **core:** support `ngFor` that has an `ngIf` as last node ([1779caf](https://github.com/angular/angular/commit/1779caf)), closes [#6304](https://github.com/angular/angular/issues/6304) [#6878](https://github.com/angular/angular/issues/6878)
|
||||
* **dart/payload:** Fix runtime error in hello_world payload app ([eeb594c](https://github.com/angular/angular/commit/eeb594c)), closes [#7358](https://github.com/angular/angular/issues/7358)
|
||||
* **differ:** clean up stale identity change refs ([ab36ea0](https://github.com/angular/angular/commit/ab36ea0)), closes [#7193](https://github.com/angular/angular/issues/7193)
|
||||
* **DomRenderer:** correctly handle namespaced attributes ([c6afea6](https://github.com/angular/angular/commit/c6afea6))
|
||||
* **Router:** Query strings are copied for HashLocationStrategy ([b47f80e](https://github.com/angular/angular/commit/b47f80e)), closes [#7298](https://github.com/angular/angular/issues/7298)
|
||||
* **test:** fix a broken test ([9aedef2](https://github.com/angular/angular/commit/9aedef2))
|
||||
* **transformers:** record reflection info about abstract classes ([05c185a](https://github.com/angular/angular/commit/05c185a)), closes [#7347](https://github.com/angular/angular/issues/7347)
|
||||
* **transformers:** replace an error with a warning when cannot resolve a symbol ([ee3c580](https://github.com/angular/angular/commit/ee3c580))
|
||||
* **transformers:** special case types some built-in types, so they can be resolved ([331b9c1](https://github.com/angular/angular/commit/331b9c1))
|
||||
* **web_worker:** wait for bindings in kitchen sink spec ([4a93f58](https://github.com/angular/angular/commit/4a93f58))
|
||||
* **web_workers:** make waitForElementText function more stable ([f6a8d04](https://github.com/angular/angular/commit/f6a8d04))
|
||||
* **WebWorker:** Fix PostMessageBusSink and Source undefined error. ([01fe7f5](https://github.com/angular/angular/commit/01fe7f5)), closes [#7156](https://github.com/angular/angular/issues/7156)
|
||||
* **WebWorker:** Make MessageBus EventEmitter synchronous ([69c1694](https://github.com/angular/angular/commit/69c1694))
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add `QueryList.forEach` to public api. ([e7470d5](https://github.com/angular/angular/commit/e7470d5))
|
||||
* **core:** Add `QueryList#forEach` ([b634a25](https://github.com/angular/angular/commit/b634a25))
|
||||
* **core:** add more debug APIs to inspect the application form a browser ([b5e6319](https://github.com/angular/angular/commit/b5e6319)), closes [#7045](https://github.com/angular/angular/issues/7045) [#7161](https://github.com/angular/angular/issues/7161)
|
||||
* **core:** drop `ChangeDetectionStrategy.OnPushObserve` ([f60fa14](https://github.com/angular/angular/commit/f60fa14))
|
||||
* **di:** drop support for injecting types with generics in Dart ([c9a3df9](https://github.com/angular/angular/commit/c9a3df9)), closes [#7262](https://github.com/angular/angular/issues/7262)
|
||||
* **forms/validators:** pattern validator ([38cb526](https://github.com/angular/angular/commit/38cb526)), closes [#5561](https://github.com/angular/angular/issues/5561)
|
||||
* **i18n:** added i18nPlural and i18nSelect pipes ([59629a0](https://github.com/angular/angular/commit/59629a0)), closes [#7268](https://github.com/angular/angular/issues/7268)
|
||||
* **pipes:** add ReplacePipe for string manipulation ([6ef2121](https://github.com/angular/angular/commit/6ef2121))
|
||||
* **test:** add withProviders for per test providers ([c1a0af5](https://github.com/angular/angular/commit/c1a0af5)), closes [#5128](https://github.com/angular/angular/issues/5128)
|
||||
* **transformers:** collect data needed for the template compiler ([ebe531b](https://github.com/angular/angular/commit/ebe531b)), closes [#7299](https://github.com/angular/angular/issues/7299)
|
||||
* **transformers:** collect information for CompileDiDependencyMetadata ([39b6e0e](https://github.com/angular/angular/commit/39b6e0e))
|
||||
* **transformers:** makes the map of resolved identifiers configurable ([0bb10d6](https://github.com/angular/angular/commit/0bb10d6)), closes [#7359](https://github.com/angular/angular/issues/7359)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* `OnPushObserve` was an experimental
|
||||
feature for Dart and had
|
||||
conceptual performance problems,
|
||||
as setting up observables is slow.
|
||||
Use `OnPush` instead.
|
||||
|
||||
* In Dart we used to support injecting types with generics. As this feature is hard to implement with the upcoming codegen we are dropping it.
|
||||
Merge cl/115454020 in G3 with this change.
|
||||
|
||||
* The `$router` injectable service has been renamed to `$rootRouter`
|
||||
|
||||
* 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: '<'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="2.0.0-beta.7"></a>
|
||||
# 2.0.0-beta.7 (2016-02-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular_1_router:** Added DI string tokens ([3478d5d](https://github.com/angular/angular/commit/3478d5d)), closes [#4269](https://github.com/angular/angular/issues/4269) [#7031](https://github.com/angular/angular/issues/7031)
|
||||
* **typing:** Remove re-export of the Promise built-in type. ([265703b](https://github.com/angular/angular/commit/265703b)), closes [#6468](https://github.com/angular/angular/issues/6468)
|
||||
|
||||
<a name="2.0.0-beta.6"></a>
|
||||
# 2.0.0-beta.6 (2016-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular1-router:** add missing wrapper methods ([55122cd](https://github.com/angular/angular/commit/55122cd)), closes [#6763](https://github.com/angular/angular/issues/6763) [#6861](https://github.com/angular/angular/issues/6861) [#6861](https://github.com/angular/angular/issues/6861)
|
||||
* **angular1-router:** add support for using the component helper ([d86be24](https://github.com/angular/angular/commit/d86be24)), closes [angular/angular.js#13860](https://github.com/angular/angular.js/issues/13860) [#6076](https://github.com/angular/angular/issues/6076) [#5278](https://github.com/angular/angular/issues/5278)
|
||||
* **async:** handle synchronous initial value in async pipe ([26e60d6](https://github.com/angular/angular/commit/26e60d6)), closes [#5996](https://github.com/angular/angular/issues/5996)
|
||||
* **build:** don't try to copy .d.ts files into the npm distro ([16b5217](https://github.com/angular/angular/commit/16b5217)), closes [#6921](https://github.com/angular/angular/issues/6921)
|
||||
* **compiler:** fix interpolation regexp ([9b0e10e](https://github.com/angular/angular/commit/9b0e10e)), closes [#6056](https://github.com/angular/angular/issues/6056)
|
||||
* **compiler:** use event names for matching directives ([231773e](https://github.com/angular/angular/commit/231773e)), closes [#6870](https://github.com/angular/angular/issues/6870)
|
||||
* **core:** add detail to dehydrated detector exception ([e7ad03c](https://github.com/angular/angular/commit/e7ad03c)), closes [#6939](https://github.com/angular/angular/issues/6939)
|
||||
* **core:** mute mode printing in console in prod mode ([74be3d3](https://github.com/angular/angular/commit/74be3d3)), closes [#6873](https://github.com/angular/angular/issues/6873)
|
||||
* **di:** throw if a token uses more than 20 dependencies. ([de77700](https://github.com/angular/angular/commit/de77700)), closes [#6690](https://github.com/angular/angular/issues/6690) [#6869](https://github.com/angular/angular/issues/6869)
|
||||
* **forms:** add RadioButtonValueAccessor to the list of default value accessors ([8f47aa3](https://github.com/angular/angular/commit/8f47aa3))
|
||||
* **forms:** add support for radio buttons ([e725542](https://github.com/angular/angular/commit/e725542)), closes [#6877](https://github.com/angular/angular/issues/6877)
|
||||
* **forms:** use strict runtimeType checks instead of instanceof ([50548fb](https://github.com/angular/angular/commit/50548fb)), closes [#6981](https://github.com/angular/angular/issues/6981)
|
||||
* **Headers:** serializable toJSON ([b55f176](https://github.com/angular/angular/commit/b55f176)), closes [#6073](https://github.com/angular/angular/issues/6073) [#6714](https://github.com/angular/angular/issues/6714)
|
||||
* **ngFor:** update view locals if identity changes ([0f10624](https://github.com/angular/angular/commit/0f10624)), closes [#6923](https://github.com/angular/angular/issues/6923)
|
||||
* **router:** Added route data to normalized async route ([df7885c](https://github.com/angular/angular/commit/df7885c)), closes [#6802](https://github.com/angular/angular/issues/6802)
|
||||
* **router:** don't prepend `/` unnecessarily to Location paths ([c603643](https://github.com/angular/angular/commit/c603643)), closes [#6729](https://github.com/angular/angular/issues/6729) [#5502](https://github.com/angular/angular/issues/5502)
|
||||
* **router:** fix incorrect url param value coercion of 1 to true ([995a9e0](https://github.com/angular/angular/commit/995a9e0)), closes [#5346](https://github.com/angular/angular/issues/5346) [#6286](https://github.com/angular/angular/issues/6286)
|
||||
* **router:** fix url path for star segment in path recognizer ([6f1ef33](https://github.com/angular/angular/commit/6f1ef33)), closes [#6976](https://github.com/angular/angular/issues/6976)
|
||||
* **router:** fixed the location wrapper for angular1 ([e73fee7](https://github.com/angular/angular/commit/e73fee7)), closes [#6943](https://github.com/angular/angular/issues/6943)
|
||||
* **typings:** Don't expose typing dependencies to users. ([2a70f4e](https://github.com/angular/angular/commit/2a70f4e)), closes [#5973](https://github.com/angular/angular/issues/5973) [#5807](https://github.com/angular/angular/issues/5807) [#6266](https://github.com/angular/angular/issues/6266) [#5242](https://github.com/angular/angular/issues/5242) [#6817](https://github.com/angular/angular/issues/6817) [#6267](https://github.com/angular/angular/issues/6267)
|
||||
* **upgrade:** fix infinite $rootScope.$digest() ([7e0f02f](https://github.com/angular/angular/commit/7e0f02f)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6386](https://github.com/angular/angular/issues/6386)
|
||||
* **Validators:** fix Validators.required marking number zero as invalid ([c2ceb7f](https://github.com/angular/angular/commit/c2ceb7f)), closes [#6617](https://github.com/angular/angular/issues/6617)
|
||||
* **WebWorkers:** Fix flaky WebWorker test ([da1fcfd](https://github.com/angular/angular/commit/da1fcfd)), closes [#6851](https://github.com/angular/angular/issues/6851)
|
||||
|
||||
### Features
|
||||
|
||||
* **angular1_router:** allow component to bind to router ([0f22dce](https://github.com/angular/angular/commit/0f22dce))
|
||||
* **typings:** install es6-shim typings to a location users can reference. ([f1f5b45](https://github.com/angular/angular/commit/f1f5b45))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
Transitive typings are no longer included in the distribution.
|
||||
|
||||
If you use `--target=es5`, you will need to add a line somewhere in your
|
||||
application (for example, at the top of the `.ts` file where you call `bootstrap`):
|
||||
```
|
||||
///<reference path="node_modules/angular2/typings/browser.d.ts"/>
|
||||
```
|
||||
(Note that if your file is not in the same directory as `node_modules`, you'll
|
||||
need to add one or more `../` to the start of that path.)
|
||||
|
||||
If you have unit tests, you need to install typings in your project using
|
||||
http://github.com/typings/typings
|
||||
And install typings such as `jasmine`, `angular-protractor`, or `selenium-webdriver`
|
||||
to satisfy the type-checker.
|
||||
|
||||
If you rely on es6 APIs other than Promises and Collections, you will need to
|
||||
install the es6-shim typing instead of using the <reference> tag above.
|
||||
Angular previously exposed typings for the entire ES6 API.
|
||||
|
||||
<a name="2.0.0-beta.5"></a>
|
||||
# 2.0.0-beta.5 (2016-02-10)
|
||||
|
||||
This release was incorrect; replaced with beta.6.
|
||||
|
||||
<a name="2.0.0-beta.4"></a>
|
||||
# 2.0.0-beta.4 (2016-02-10)
|
||||
|
||||
This release was incorrect; replaced with beta.6.
|
||||
|
||||
<a name="2.0.0-beta.3"></a>
|
||||
# 2.0.0-beta.3 (2016-02-03)
|
||||
|
||||
|
@ -180,8 +180,8 @@ Must be one of the following:
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
* **build**: Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Any changes to our CI configuration files and scripts (Travis, Circle CI, BrowserStack, SauceLabs)
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
* **chore**: Other changes that don't modify `src` or `test` files
|
||||
|
||||
### Scope
|
||||
|
@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@View({
|
||||
@Component({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
|
@ -4,8 +4,9 @@
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'ChromeBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta should be required:true
|
||||
// https://github.com/angular/angular/issues/7560
|
||||
'FirefoxBeta': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: false}},
|
||||
'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}},
|
||||
|
196
gulpfile.js
196
gulpfile.js
@ -40,9 +40,9 @@ if (cliArgs.projects) {
|
||||
cliArgs.projects.split(',').sort().join(',');
|
||||
}
|
||||
|
||||
// --projects=angular2,angular2_material => {angular2: true, angular2_material: true}
|
||||
// --projects=angular2 => {angular2: true}
|
||||
var allProjects =
|
||||
'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
|
||||
'angular1_router,angular2,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
|
||||
var cliArgsProjects = (cliArgs.projects || allProjects)
|
||||
.split(',')
|
||||
.reduce((map, projectName) => {
|
||||
@ -57,7 +57,7 @@ function printModulesWarning() {
|
||||
console.warn(
|
||||
"Pro Tip: Did you know that you can speed up your build by specifying project name(s)?");
|
||||
console.warn(" It's like pressing the turbo button in the old days, but better!");
|
||||
console.warn(" Examples: --project=angular2 or --project=angular2,angular2_material");
|
||||
console.warn(" Examples: --project=angular2 or --project=angular2");
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,8 @@ var CONFIG = {
|
||||
dev: {es6: 'dist/js/dev/es6', es5: 'dist/js/dev/es5'},
|
||||
prod: {es6: 'dist/js/prod/es6', es5: 'dist/js/prod/es5'},
|
||||
cjs: 'dist/js/cjs',
|
||||
dart2js: 'dist/js/dart2js'
|
||||
dart2js: 'dist/js/dart2js',
|
||||
dart_dev_compiler: 'dist/js/ddc'
|
||||
},
|
||||
dart: 'dist/dart',
|
||||
docs: 'dist/docs',
|
||||
@ -344,9 +345,8 @@ gulp.task('lint', ['build.tools'], function() {
|
||||
gulp.task('build/checkCircularDependencies', function(done) {
|
||||
var madge = require('madge');
|
||||
|
||||
var dependencyObject = madge(CONFIG.dest.js.dev.es5, {
|
||||
var dependencyObject = madge([CONFIG.dest.js.dev.es5], {
|
||||
format: 'cjs',
|
||||
paths: [CONFIG.dest.js.dev.es5],
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||
});
|
||||
@ -370,6 +370,10 @@ function jsServeDartJs() {
|
||||
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart2js, port: 8002})();
|
||||
}
|
||||
|
||||
function jsServeDartDevCompiler() {
|
||||
return jsserve(gulp, gulpPlugins, {path: CONFIG.dest.js.dart_dev_compiler, port: 8003})();
|
||||
}
|
||||
|
||||
function proxyServeDart() {
|
||||
return jsserve(gulp, gulpPlugins, {
|
||||
port: 8002,
|
||||
@ -383,7 +387,7 @@ function proxyServeDart() {
|
||||
|
||||
// ------------------
|
||||
// web servers
|
||||
gulp.task('serve.js.dev', ['build.js.dev'], function(neverDone) {
|
||||
gulp.task('serve.js.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
watch('modules/**', {ignoreInitial: true}, '!broccoli.js.dev');
|
||||
@ -392,24 +396,24 @@ gulp.task('serve.js.dev', ['build.js.dev'], function(neverDone) {
|
||||
|
||||
gulp.task('serve.js.prod', jsServeProd);
|
||||
|
||||
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs', 'build.css.material'],
|
||||
function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
gulp.task('serve.e2e.dev', ['build.js.dev', 'build.js.cjs'], function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
|
||||
jsServeDev();
|
||||
});
|
||||
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!build.js.cjs']);
|
||||
jsServeDev();
|
||||
});
|
||||
|
||||
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs', 'build.css.material'],
|
||||
function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
gulp.task('serve.e2e.prod', ['build.js.prod', 'build.js.cjs'], function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
|
||||
jsServeProd();
|
||||
});
|
||||
watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.prod', '!build.js.cjs']);
|
||||
jsServeProd();
|
||||
});
|
||||
|
||||
gulp.task('serve.js.dart2js', jsServeDartJs);
|
||||
|
||||
gulp.task('serve.js.ddc', jsServeDartDevCompiler);
|
||||
|
||||
gulp.task('!proxyServeDart', proxyServeDart);
|
||||
|
||||
gulp.task('serve.dart', function(done) {
|
||||
@ -441,7 +445,7 @@ gulp.task('serve.e2e.dart', ['build.js.cjs'], function(neverDone) {
|
||||
|
||||
// Note: we are not using build.dart as the dart analyzer takes too long...
|
||||
watch('modules/**', {ignoreInitial: true}, ['!build/tree.dart', '!build.js.cjs']);
|
||||
runSequence('build/packages.dart', 'build/pubspec.dart', 'build.dart.material.css', 'serve.dart');
|
||||
runSequence('build/packages.dart', 'build/pubspec.dart', 'serve.dart');
|
||||
});
|
||||
|
||||
|
||||
@ -468,7 +472,7 @@ gulp.task('test.js', function(done) {
|
||||
|
||||
gulp.task('test.dart', function(done) {
|
||||
runSequence('versions.dart', 'test.transpiler.unittest', 'test.unit.dart/ci',
|
||||
'test.dart.angular2_testing/ci', sequenceComplete(done));
|
||||
sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('versions.dart', function() { dartSdk.logVersion(DART_SDK); });
|
||||
@ -636,8 +640,7 @@ gulp.task('buildRouter.dev', function() {
|
||||
gulp.task('test.unit.dart', function(done) {
|
||||
printModulesWarning();
|
||||
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
|
||||
'!test.unit.dart/karma-server', '!test.unit.dart/karma-run', function(error) {
|
||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
// if initial build failed (likely due to build or formatting step) then exit
|
||||
@ -646,9 +649,10 @@ gulp.task('test.unit.dart', function(done) {
|
||||
done(error);
|
||||
return;
|
||||
}
|
||||
// treatTestErrorsAsFatal = false;
|
||||
|
||||
watch(['modules/angular2/**'], {ignoreInitial: true},
|
||||
['!build/tree.dart', '!test.unit.dart/karma-run']);
|
||||
watch(['modules/angular2/**'],
|
||||
['!build/tree.dart', '!test.unit.dart/run/angular2']);
|
||||
});
|
||||
});
|
||||
|
||||
@ -707,7 +711,7 @@ gulp.task('!build.payload.js.webpack', function() {
|
||||
.then(function() { // pad bundle with mandatory dependencies
|
||||
return new Promise(function(resolve, reject) {
|
||||
gulp.src([
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
CASE_PATH + '/app-bundle.js'
|
||||
@ -775,8 +779,7 @@ gulp.task('!checkAndReport.payload.js', function() {
|
||||
|
||||
gulp.task('watch.dart.dev', function(done) {
|
||||
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
|
||||
function(error) {
|
||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
// if initial build failed (likely due to build or formatting step) then exit
|
||||
@ -790,20 +793,6 @@ gulp.task('watch.dart.dev', function(done) {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('!test.unit.dart/karma-run', function(done) {
|
||||
// run the run command in a new process to avoid duplicate logging by both server and runner from
|
||||
// a single process
|
||||
runKarma('karma-dart.conf.js', done);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('!test.unit.dart/karma-server', function() {
|
||||
var karma = require('karma');
|
||||
|
||||
new karma.Server({configFile: __dirname + '/karma-dart.conf.js', reporters: 'dots'}).start();
|
||||
});
|
||||
|
||||
|
||||
gulp.task('test.unit.router/ci', function(done) {
|
||||
var karma = require('karma');
|
||||
|
||||
@ -851,20 +840,56 @@ gulp.task('test.unit.js.browserstack/ci', function(done) {
|
||||
});
|
||||
|
||||
gulp.task('test.unit.dart/ci', function(done) {
|
||||
var karma = require('karma');
|
||||
|
||||
var browserConf = getBrowsersFromCLI(null, true);
|
||||
new karma.Server(
|
||||
{
|
||||
configFile: __dirname + '/karma-dart.conf.js',
|
||||
singleRun: true,
|
||||
reporters: ['dots'],
|
||||
browsers: browserConf.browsersToRun
|
||||
},
|
||||
done)
|
||||
.start();
|
||||
runSequence('test.dart.dartium_symlink', '!test.unit.dart/run/angular2',
|
||||
'!test.unit.dart/run/angular2_testing', '!test.unit.dart/run/benchpress',
|
||||
sequenceComplete(done));
|
||||
});
|
||||
|
||||
// At the moment, dart test requires dartium to be an executable on the path.
|
||||
// Make a temporary directory and symlink dartium from there (just for this command)
|
||||
// so that it can run.
|
||||
// TODO(juliemr): this won't work with windows - remove the hack and make this platform agnostic.
|
||||
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
|
||||
var dartiumPathPrefix = 'PATH=$PATH:' + dartiumTmpdir + ' ';
|
||||
gulp.task(
|
||||
'test.dart.dartium_symlink',
|
||||
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
|
||||
|
||||
gulp.task('!test.unit.dart/run/angular2', function() {
|
||||
var pubtest = require('./tools/build/pubtest');
|
||||
return pubtest({
|
||||
dir: path.join(CONFIG.dest.dart, 'angular2'),
|
||||
dartiumTmpdir: dartiumTmpdir,
|
||||
command: DART_SDK.PUB,
|
||||
files: '**/*_spec.dart',
|
||||
bunchFiles: true,
|
||||
useExclusiveTests: true
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('!test.unit.dart/run/angular2_testing', function() {
|
||||
var pubtest = require('./tools/build/pubtest');
|
||||
|
||||
return pubtest({
|
||||
dir: path.join(CONFIG.dest.dart, 'angular2_testing'),
|
||||
dartiumTmpdir: dartiumTmpdir,
|
||||
command: DART_SDK.PUB,
|
||||
files: '**/*_test.dart',
|
||||
useExclusiveTests: true
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('!test.unit.dart/run/benchpress', function() {
|
||||
var pubtest = require('./tools/build/pubtest');
|
||||
|
||||
return pubtest({
|
||||
dir: path.join(CONFIG.dest.dart, 'benchpress'),
|
||||
dartiumTmpdir: dartiumTmpdir,
|
||||
command: DART_SDK.PUB,
|
||||
files: '**/*_spec.dart',
|
||||
useExclusiveTests: true
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('test.unit.cjs/ci', function(done) {
|
||||
runJasmineTests(['dist/js/cjs/{angular2,benchpress}/test/**/*_spec.js'], done);
|
||||
@ -931,24 +956,6 @@ gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {dest: 'dist
|
||||
gulp.task('test.transpiler.unittest',
|
||||
function(done) { runJasmineTests(['tools/transpiler/unittest/**/*.js'], done); });
|
||||
|
||||
// At the moment, dart test requires dartium to be an executable on the path.
|
||||
// Make a temporary directory and symlink dartium from there (just for this command)
|
||||
// so that it can run.
|
||||
var dartiumTmpdir = path.join(os.tmpdir(), 'dartium' + new Date().getTime().toString());
|
||||
gulp.task('test.dart.angular2_testing/ci', ['build/pubspec.dart'], function(done) {
|
||||
runSequence('test.dart.angular2_testing_symlink', 'test.dart.angular2_testing',
|
||||
sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'test.dart.angular2_testing_symlink',
|
||||
shell.task(['mkdir ' + dartiumTmpdir, 'ln -s $DARTIUM_BIN ' + dartiumTmpdir + '/dartium']));
|
||||
|
||||
gulp.task('test.dart.angular2_testing',
|
||||
shell.task(['PATH=$PATH:' + dartiumTmpdir + ' pub run test -p dartium'],
|
||||
{'cwd': 'dist/dart/angular2_testing'}));
|
||||
|
||||
|
||||
// -----------------
|
||||
// Pre-test checks
|
||||
|
||||
@ -1018,6 +1025,7 @@ gulp.task('build/pure-packages.dart/standalone', function() {
|
||||
'modules_dart/**/*',
|
||||
'!modules_dart/**/*.proto',
|
||||
'!modules_dart/**/packages{,/**}',
|
||||
'!modules_dart/**/.packages',
|
||||
'!modules_dart/payload{,/**}',
|
||||
'!modules_dart/transform{,/**}',
|
||||
])
|
||||
@ -1049,7 +1057,7 @@ gulp.task('build/packages.dart', function(done) {
|
||||
// Builds and compiles all Dart packages
|
||||
gulp.task('build.dart', function(done) {
|
||||
runSequence('build/packages.dart', 'build/pubspec.dart', 'build/analyze.dart',
|
||||
'build/check.apidocs.dart', 'build.dart.material.css', sequenceComplete(done));
|
||||
'build/check.apidocs.dart', sequenceComplete(done));
|
||||
});
|
||||
|
||||
|
||||
@ -1108,9 +1116,8 @@ gulp.task('!broccoli.js.prod', () => angularBuilder.rebuildBrowserProdTree({
|
||||
useBundles: cliArgs.useBundles
|
||||
}));
|
||||
|
||||
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
|
||||
runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done));
|
||||
});
|
||||
gulp.task('build.js.dev', ['build/clean.js'],
|
||||
function(done) { runSequence('broccoli.js.dev', sequenceComplete(done)); });
|
||||
|
||||
gulp.task('build.js.prod', ['build.tools'],
|
||||
function(done) { runSequence('!broccoli.js.prod', sequenceComplete(done)); });
|
||||
@ -1351,7 +1358,7 @@ gulp.task('!bundle.ng.polyfills', ['clean'],
|
||||
|
||||
var JS_DEV_DEPS = [
|
||||
licenseWrap('node_modules/zone.js/LICENSE', true),
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
licenseWrap('node_modules/reflect-metadata/LICENSE', true),
|
||||
'node_modules/reflect-metadata/Reflect.js'
|
||||
@ -1467,41 +1474,6 @@ gulp.task('!build/change_detect.dart', function(done) {
|
||||
});
|
||||
|
||||
// ------------
|
||||
// angular material testing rules
|
||||
gulp.task('build.css.material', function() {
|
||||
var autoprefixer = require('gulp-autoprefixer');
|
||||
var sass = require('gulp-sass');
|
||||
|
||||
return gulp.src('modules/*/src/**/*.scss')
|
||||
.pipe(sass())
|
||||
.pipe(autoprefixer())
|
||||
.pipe(gulp.dest(CONFIG.dest.js.prod.es5))
|
||||
.pipe(gulp.dest(CONFIG.dest.js.dev.es5))
|
||||
.pipe(gulp.dest(CONFIG.dest.js.dart2js + '/examples/packages'));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('build.js.material', function(done) {
|
||||
runSequence('build.js.dev', 'build.css.material', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('build.dart2js.material', function(done) {
|
||||
runSequence('build.dart', 'build.css.material', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('build.dart.material.css', function() {
|
||||
var autoprefixer = require('gulp-autoprefixer');
|
||||
var sass = require('gulp-sass');
|
||||
|
||||
return gulp.src('dist/dart/angular2_material/src/**/*.scss')
|
||||
.pipe(sass())
|
||||
.pipe(autoprefixer())
|
||||
.pipe(gulp.dest('dist/dart/angular2_material/lib/src'));
|
||||
});
|
||||
|
||||
gulp.task('build.dart.material', ['build/packages.dart'], function(done) {
|
||||
runSequence('build/packages.dart', 'build.dart.material.css', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('cleanup.builder', function() { return angularBuilder.cleanup(); });
|
||||
|
||||
|
@ -1,86 +0,0 @@
|
||||
// This module provides a customFileHandler for karma
|
||||
// that serves files with urls like /packages_<timestamp>/...
|
||||
// with maximum cache.
|
||||
// We are using these urls when we spawn isolates
|
||||
// so that the isolates don't reload files every time.
|
||||
|
||||
var common = require('karma/lib/middleware/common');
|
||||
var fs = require('fs');
|
||||
|
||||
var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/;
|
||||
|
||||
module.exports = createFactory;
|
||||
|
||||
function createFactory(proxyPaths) {
|
||||
return {
|
||||
'framework:dart-evalcache': ['factory', dartEvalCacheFactory]
|
||||
};
|
||||
|
||||
function dartEvalCacheFactory(emitter, logger, customFileHandlers) {
|
||||
var filesPromise = new common.PromiseContainer();
|
||||
emitter.on('file_list_modified', function(files) {
|
||||
filesPromise.set(Promise.resolve(files));
|
||||
});
|
||||
|
||||
var serveFile = common.createServeFile(fs);
|
||||
var log = logger.create('dart-evalcache');
|
||||
|
||||
customFileHandlers.push({
|
||||
urlRegex: DART_EVAL_PATH_RE,
|
||||
handler: handler
|
||||
});
|
||||
|
||||
// See source_files handler
|
||||
function handler(request, response, fa, fb, basePath) {
|
||||
return filesPromise.then(function(files) {
|
||||
try {
|
||||
var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log);
|
||||
// TODO(vojta): change served to be a map rather then an array
|
||||
var file = findByPath(files.served, requestedFilePath);
|
||||
if (file) {
|
||||
serveFile(file.contentPath || file.path, response, function() {
|
||||
common.setHeavyCacheHeaders(response);
|
||||
}, file.content);
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end('Not found');
|
||||
}
|
||||
} catch (e) {
|
||||
log.error(e.stack);
|
||||
response.writeHead(500);
|
||||
response.end('Error', e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function mapUrlToFile(url, proxyPaths, basePath, log) {
|
||||
var originalUrl = url;
|
||||
url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url;
|
||||
var match = DART_EVAL_PATH_RE.exec(url);
|
||||
var packagePath = match[1];
|
||||
var result = null;
|
||||
var lastProxyFromLength = 0;
|
||||
Object.keys(proxyPaths).forEach(function(proxyFrom) {
|
||||
if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) {
|
||||
lastProxyFromLength = proxyFrom.length;
|
||||
result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length);
|
||||
}
|
||||
});
|
||||
return basePath + '/' + result;
|
||||
}
|
||||
|
||||
function startsWith(string, subString) {
|
||||
return string.length >= subString.length && string.slice(0, subString.length) === subString;
|
||||
}
|
||||
|
||||
function findByPath(files, path) {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i].path === path) {
|
||||
return files[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
|
||||
var packageSources = {
|
||||
// Dependencies installed with `pub install`.
|
||||
'unittest': 'packages/unittest',
|
||||
'guinness': 'packages/guinness',
|
||||
'matcher': 'packages/matcher',
|
||||
'stack_trace': 'packages/stack_trace',
|
||||
'collection': 'packages/collection',
|
||||
'path': 'packages/path',
|
||||
'observe': 'packages/observe',
|
||||
'quiver': 'packages/quiver',
|
||||
'intl': 'packages/intl',
|
||||
'smoke': 'packages/smoke',
|
||||
'logging': 'packages/logging',
|
||||
'utf': 'packages/utf',
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'angular2': 'dist/dart/angular2/lib',
|
||||
'angular2/test/': 'dist/dart/angular2/test/',
|
||||
'http': 'dist/dart/http/lib',
|
||||
'angular2_material': 'dist/dart/angular2_material/lib',
|
||||
'benchpress': 'dist/dart/benchpress/lib',
|
||||
'examples': 'dist/dart/examples/lib'
|
||||
};
|
||||
|
||||
var proxyPaths = {};
|
||||
Object.keys(packageSources).map(function(packageName) {
|
||||
var filePath = packageSources[packageName];
|
||||
proxyPaths['/packages/'+packageName] = '/base/'+filePath;
|
||||
});
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['dart-unittest', 'dart-evalcache'],
|
||||
|
||||
files: [
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-init.dart', included: true},
|
||||
// Unit test files needs to be included.
|
||||
{pattern: 'dist/dart/**/*_spec.dart', included: true, watched: false},
|
||||
|
||||
// Karma-dart via the dart-unittest framework generates
|
||||
// `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'dist/dart/**', included: false, watched: false},
|
||||
|
||||
// Dependencies, installed with `pub install`.
|
||||
{pattern: 'packages/**/*.dart', included: false, watched: false},
|
||||
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-main.dart', included: true},
|
||||
{pattern: 'modules/**/test/**/static_assets/**', included: false, watched: false},
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/dart/**/packages/**',
|
||||
'modules/angular1_router/**'
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: proxyPaths,
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877,
|
||||
|
||||
plugins: [
|
||||
require('karma-dart'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-sauce-launcher'),
|
||||
require('./karma-dart-evalcache')(packageSources)
|
||||
]
|
||||
});
|
||||
};
|
@ -17,8 +17,7 @@ module.exports = function(config) {
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.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/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
|
||||
|
39
modules/angular1_router/build.js
vendored
39
modules/angular1_router/build.js
vendored
@ -4,17 +4,20 @@ var fs = require('fs');
|
||||
var ts = require('typescript');
|
||||
|
||||
var files = [
|
||||
'lifecycle_annotations_impl.ts',
|
||||
'utils.ts',
|
||||
'url_parser.ts',
|
||||
'route_recognizer.ts',
|
||||
'route_config_impl.ts',
|
||||
'async_route_handler.ts',
|
||||
'sync_route_handler.ts',
|
||||
'component_recognizer.ts',
|
||||
'lifecycle/lifecycle_annotations_impl.ts',
|
||||
'lifecycle/route_lifecycle_reflector.ts',
|
||||
'route_config/route_config_impl.ts',
|
||||
'route_config/route_config_normalizer.ts',
|
||||
'rules/route_handlers/async_route_handler.ts',
|
||||
'rules/route_handlers/sync_route_handler.ts',
|
||||
'rules/rules.ts',
|
||||
'rules/rule_set.ts',
|
||||
'rules/route_paths/route_path.ts',
|
||||
'rules/route_paths/param_route_path.ts',
|
||||
'rules/route_paths/regex_route_path.ts',
|
||||
'instruction.ts',
|
||||
'path_recognizer.ts',
|
||||
'route_config_nomalizer.ts',
|
||||
'route_lifecycle_reflector.ts',
|
||||
'route_registry.ts',
|
||||
'router.ts'
|
||||
];
|
||||
@ -37,8 +40,10 @@ function main(modulesDirectory) {
|
||||
return prev + transform(fs.readFileSync(dir + file, 'utf8'));
|
||||
}, '');
|
||||
|
||||
var out = moduleTemplate.replace('//{{FACADES}}', facades)
|
||||
.replace('//{{SHARED_CODE}}', sharedCode);
|
||||
// we have to use a function callback for replace to prevent it from interpreting `$`
|
||||
// as a replacement command character
|
||||
var out = moduleTemplate.replace('//{{FACADES}}', function() { return facades; })
|
||||
.replace('//{{SHARED_CODE}}', function() { return sharedCode; });
|
||||
return PRELUDE + transform(directives) + out + POSTLUDE;
|
||||
}
|
||||
|
||||
@ -48,9 +53,10 @@ function main(modulesDirectory) {
|
||||
*/
|
||||
var IMPORT_RE = new RegExp("import \\{?([\\w\\n_, ]+)\\}? from '(.+)';?", 'g');
|
||||
var INJECT_RE = new RegExp("@Inject\\(ROUTER_PRIMARY_COMPONENT\\)", 'g');
|
||||
var IMJECTABLE_RE = new RegExp("@Injectable\\(\\)", 'g');
|
||||
var INJECTABLE_RE = new RegExp("@Injectable\\(\\)", 'g');
|
||||
var REQUIRE_RE = new RegExp("require\\('(.*?)'\\);", 'g');
|
||||
function transform(contents) {
|
||||
contents = contents.replace(INJECT_RE, '').replace(IMJECTABLE_RE, '');
|
||||
contents = contents.replace(INJECT_RE, '').replace(INJECTABLE_RE, '');
|
||||
contents = contents.replace(IMPORT_RE, function (match, imports, includePath) {
|
||||
//TODO: remove special-case
|
||||
if (isFacadeModule(includePath) || includePath === './router_outlet') {
|
||||
@ -58,10 +64,15 @@ function transform(contents) {
|
||||
}
|
||||
return match;
|
||||
});
|
||||
return ts.transpile(contents, {
|
||||
contents = ts.transpile(contents, {
|
||||
target: ts.ScriptTarget.ES5,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
|
||||
// Rename require functions from transpiled imports
|
||||
contents = contents.replace(REQUIRE_RE, 'routerRequire(\'$1\');');
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
function isFacadeModule(modulePath) {
|
||||
|
@ -12,9 +12,9 @@
|
||||
<script src="../../dist/angular_1_router.js"></script>
|
||||
<script>
|
||||
angular.module('myApp', ['ngComponentRouter'])
|
||||
.controller('MyCtrl', ['$router', function ($router) {
|
||||
console.log($router);
|
||||
$router.navigateByUrl('/')
|
||||
.controller('MyCtrl', ['$rootRouter', function ($rootRouter) {
|
||||
console.log($rootRouter);
|
||||
$rootRouter.navigateByUrl('/')
|
||||
.then(console.log.bind(console, 'resolve'), console.log.bind(console, 'reject'));
|
||||
}]);
|
||||
</script>
|
||||
|
@ -316,3 +316,9 @@ Location.prototype.path = function () {
|
||||
Location.prototype.go = function (path, query) {
|
||||
return $location.url(path + query);
|
||||
};
|
||||
Location.prototype.prepareExternalUrl = function(url) {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
return $location.$$html5 ? '.' + url : '#' + $locationHashPrefix + url;
|
||||
};
|
||||
|
80
modules/angular1_router/src/module_template.js
vendored
80
modules/angular1_router/src/module_template.js
vendored
@ -4,9 +4,33 @@ angular.module('ngComponentRouter').
|
||||
// Because Angular 1 has no notion of a root component, we use an object with unique identity
|
||||
// to represent this. Can be overloaded with a component name
|
||||
value('$routerRootComponent', new Object()).
|
||||
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$routerRootComponent', routerFactory]);
|
||||
|
||||
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector, $routerRootComponent) {
|
||||
// Unfortunately, $location doesn't expose what the current hashPrefix is
|
||||
// So we have to monkey patch the $locationProvider to capture this value
|
||||
provider('$locationHashPrefix', ['$locationProvider', $locationHashPrefixProvider]).
|
||||
factory('$rootRouter', ['$q', '$location', '$browser', '$rootScope', '$injector', '$routerRootComponent', '$locationHashPrefix', routerFactory]);
|
||||
|
||||
function $locationHashPrefixProvider($locationProvider) {
|
||||
|
||||
// Get hold of the original hashPrefix method
|
||||
var hashPrefixFn = $locationProvider.hashPrefix.bind($locationProvider);
|
||||
|
||||
// Read the current hashPrefix (in case it was set before this monkey-patch occurred)
|
||||
var hashPrefix = hashPrefixFn();
|
||||
|
||||
// Override the helper so that we can read any changes to the prefix (after this monkey-patch)
|
||||
$locationProvider.hashPrefix = function(prefix) {
|
||||
if (angular.isDefined(prefix)) {
|
||||
hashPrefix = prefix;
|
||||
}
|
||||
return hashPrefixFn(prefix);
|
||||
}
|
||||
|
||||
// Return the final hashPrefix as the value of this service
|
||||
this.$get = function() { return hashPrefix; };
|
||||
}
|
||||
|
||||
function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRootComponent, $locationHashPrefix) {
|
||||
|
||||
// When this file is processed, the line below is replaced with
|
||||
// the contents of `../lib/facades.es5`.
|
||||
@ -17,17 +41,30 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
|
||||
OpaqueToken: function () {},
|
||||
Inject: function () {}
|
||||
};
|
||||
var require = function () {return exports;};
|
||||
var routerRequire = function () {return exports;};
|
||||
|
||||
// When this file is processed, the line below is replaced with
|
||||
// the contents of the compiled TypeScript classes.
|
||||
//{{SHARED_CODE}}
|
||||
|
||||
function getComponentConstructor(name) {
|
||||
var serviceName = name + 'Directive';
|
||||
if ($injector.has(serviceName)) {
|
||||
var definitions = $injector.get(serviceName);
|
||||
if (definitions.length > 1) {
|
||||
throw new BaseException('too many directives named "' + name + '"');
|
||||
}
|
||||
return definitions[0].controller;
|
||||
} else {
|
||||
throw new BaseException('directive "' + name + '" is not registered');
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: this is a hack to replace the exiting implementation at run-time
|
||||
exports.getCanActivateHook = function (directiveName) {
|
||||
var factory = $$directiveIntrospector.getTypeByName(directiveName);
|
||||
return factory && factory.$canActivate && function (next, prev) {
|
||||
return $injector.invoke(factory.$canActivate, null, {
|
||||
var controller = getComponentConstructor(directiveName);
|
||||
return controller.$canActivate && function (next, prev) {
|
||||
return $injector.invoke(controller.$canActivate, null, {
|
||||
$nextInstruction: next,
|
||||
$prevInstruction: prev
|
||||
});
|
||||
@ -45,17 +82,32 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
|
||||
var RouteRegistry = exports.RouteRegistry;
|
||||
var RootRouter = exports.RootRouter;
|
||||
|
||||
// Override this method to actually get hold of the child routes
|
||||
RouteRegistry.prototype.configFromComponent = function (component) {
|
||||
var that = this;
|
||||
if (isString(component)) {
|
||||
// Don't read the annotations component a type more than once –
|
||||
// this prevents an infinite loop if a component routes recursively.
|
||||
if (this._rules.has(component)) {
|
||||
return;
|
||||
}
|
||||
var controller = getComponentConstructor(component);
|
||||
if (angular.isArray(controller.$routeConfig)) {
|
||||
controller.$routeConfig.forEach(function (config) {
|
||||
var loader = config.loader;
|
||||
if (isPresent(loader)) {
|
||||
config = angular.extend({}, config, { loader: () => $injector.invoke(loader) });
|
||||
}
|
||||
that.config(component, config);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var registry = new RouteRegistry($routerRootComponent);
|
||||
var location = new Location();
|
||||
|
||||
$$directiveIntrospector(function (name, factory) {
|
||||
if (angular.isArray(factory.$routeConfig)) {
|
||||
factory.$routeConfig.forEach(function (config) {
|
||||
registry.config(name, config);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var router = new RootRouter(registry, location, $routerRootComponent);
|
||||
$rootScope.$watch(function () { return $location.url(); }, function (path) {
|
||||
if (router.lastNavigationAttempt !== path) {
|
||||
|
@ -1,51 +1,6 @@
|
||||
///<reference path="../typings/angularjs/angular.d.ts"/>
|
||||
|
||||
/*
|
||||
* decorates $compileProvider so that we have access to routing metadata
|
||||
*/
|
||||
function compilerProviderDecorator($compileProvider,
|
||||
$$directiveIntrospectorProvider: DirectiveIntrospectorProvider) {
|
||||
let directive = $compileProvider.directive;
|
||||
$compileProvider.directive = function(name: string, factory: Function) {
|
||||
$$directiveIntrospectorProvider.register(name, factory);
|
||||
return directive.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* private service that holds route mappings for each controller
|
||||
*/
|
||||
class DirectiveIntrospectorProvider {
|
||||
private directiveBuffer: any[] = [];
|
||||
private directiveFactoriesByName: {[name: string]: Function} = {};
|
||||
private onDirectiveRegistered: (name: string, factory: Function) => any = null;
|
||||
|
||||
register(name: string, factory: Function) {
|
||||
if (angular.isArray(factory)) {
|
||||
factory = factory[factory.length - 1];
|
||||
}
|
||||
this.directiveFactoriesByName[name] = factory;
|
||||
if (this.onDirectiveRegistered) {
|
||||
this.onDirectiveRegistered(name, factory);
|
||||
} else {
|
||||
this.directiveBuffer.push({name: name, factory: factory});
|
||||
}
|
||||
}
|
||||
|
||||
$get() {
|
||||
let fn: any = newOnControllerRegistered => {
|
||||
this.onDirectiveRegistered = newOnControllerRegistered;
|
||||
while (this.directiveBuffer.length > 0) {
|
||||
let directive = this.directiveBuffer.pop();
|
||||
this.onDirectiveRegistered(directive.name, directive.factory);
|
||||
}
|
||||
};
|
||||
|
||||
fn.getTypeByName = name => this.directiveFactoriesByName[name];
|
||||
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name ngOutlet
|
||||
@ -61,8 +16,8 @@ class DirectiveIntrospectorProvider {
|
||||
*
|
||||
* The value for the `ngOutlet` attribute is optional.
|
||||
*/
|
||||
function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
let rootRouter = $router;
|
||||
function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
let rootRouter = $rootRouter;
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
@ -145,7 +100,7 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
}
|
||||
|
||||
activate(instruction) {
|
||||
let previousInstruction = this.currentInstruction;
|
||||
this.previousInstruction = this.currentInstruction;
|
||||
this.currentInstruction = instruction;
|
||||
|
||||
let componentName = this.controller.$$componentName = instruction.componentType;
|
||||
@ -154,13 +109,14 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
throw new Error('Component is not a string for ' + instruction.urlPath);
|
||||
}
|
||||
|
||||
this.controller.$$routeParams = instruction.params;
|
||||
this.controller.$$template =
|
||||
'<' + dashCase(componentName) + ' router="$$router"></' + dashCase(componentName) + '>';
|
||||
this.controller.$$template = '<' + dashCase(componentName) + ' $router="::$$router"></' +
|
||||
dashCase(componentName) + '>';
|
||||
this.controller.$$router = this.router.childRouter(instruction.componentType);
|
||||
this.controller.$$outlet = this;
|
||||
|
||||
let newScope = scope.$new();
|
||||
newScope.$$router = this.controller.$$router;
|
||||
this.deferredActivation = $q.defer();
|
||||
|
||||
let clone = $transclude(newScope, clone => {
|
||||
$animate.enter(clone, null, this.currentElement || element);
|
||||
@ -169,15 +125,7 @@ function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
|
||||
this.currentElement = clone;
|
||||
this.currentScope = newScope;
|
||||
|
||||
// TODO: prefer the other directive retrieving the controller
|
||||
// by debug mode
|
||||
this.currentController = this.currentElement.children().eq(0).controller(componentName);
|
||||
|
||||
if (this.currentController && this.currentController.$routerOnActivate) {
|
||||
return this.currentController.$routerOnActivate(instruction, previousInstruction);
|
||||
}
|
||||
return $q.when();
|
||||
return this.deferredActivation.promise;
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,21 +148,32 @@ function ngOutletFillContentDirective($compile) {
|
||||
link: (scope, element, attrs, ctrl) => {
|
||||
let template = ctrl.$$template;
|
||||
element.html(template);
|
||||
let link = $compile(element.contents());
|
||||
link(scope);
|
||||
|
||||
// TODO: move to primary directive
|
||||
let componentInstance = scope[ctrl.$$componentName];
|
||||
if (componentInstance) {
|
||||
ctrl.$$currentComponent = componentInstance;
|
||||
|
||||
componentInstance.$router = ctrl.$$router;
|
||||
componentInstance.$routeParams = ctrl.$$routeParams;
|
||||
}
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
function routerTriggerDirective($q) {
|
||||
return {
|
||||
require: '^ngOutlet',
|
||||
priority: -1000,
|
||||
link: function(scope, element, attr, ngOutletCtrl) {
|
||||
var promise = $q.when();
|
||||
var outlet = ngOutletCtrl.$$outlet;
|
||||
var currentComponent = outlet.currentController =
|
||||
element.controller(ngOutletCtrl.$$componentName);
|
||||
if (currentComponent.$routerOnActivate) {
|
||||
promise = $q.when(currentComponent.$routerOnActivate(outlet.currentInstruction,
|
||||
outlet.previousInstruction));
|
||||
}
|
||||
promise.then(outlet.deferredActivation.resolve, outlet.deferredActivation.reject);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name ngLink
|
||||
* @description
|
||||
@ -227,8 +186,8 @@ function ngOutletFillContentDirective($compile) {
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngComponentRouter'])
|
||||
* .controller('AppController', ['$router', function($router) {
|
||||
* $router.config({ path: '/user/:id', component: 'user' });
|
||||
* .controller('AppController', ['$rootRouter', function($rootRouter) {
|
||||
* $rootRouter.config({ path: '/user/:id', component: 'user' });
|
||||
* this.user = { name: 'Brian', id: 123 };
|
||||
* });
|
||||
* ```
|
||||
@ -239,23 +198,32 @@ function ngOutletFillContentDirective($compile) {
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
function ngLinkDirective($router, $parse) {
|
||||
let rootRouter = $router;
|
||||
|
||||
function ngLinkDirective($rootRouter, $parse) {
|
||||
return {require: '?^^ngOutlet', restrict: 'A', link: ngLinkDirectiveLinkFn};
|
||||
|
||||
function ngLinkDirectiveLinkFn(scope, element, attrs, ctrl) {
|
||||
let router = (ctrl && ctrl.$$router) || rootRouter;
|
||||
let router = (ctrl && ctrl.$$router) || $rootRouter;
|
||||
if (!router) {
|
||||
return;
|
||||
}
|
||||
|
||||
let instruction = null;
|
||||
let navigationInstruction = null;
|
||||
let link = attrs.ngLink || '';
|
||||
|
||||
function getLink(params) {
|
||||
instruction = router.generate(params);
|
||||
return './' + angular.stringifyInstruction(instruction);
|
||||
navigationInstruction = router.generate(params);
|
||||
|
||||
scope.$watch(function() { return router.isRouteActive(navigationInstruction); },
|
||||
function(active) {
|
||||
if (active) {
|
||||
element.addClass('ng-link-active');
|
||||
} else {
|
||||
element.removeClass('ng-link-active');
|
||||
}
|
||||
});
|
||||
|
||||
const navigationHref = navigationInstruction.toLinkUrl();
|
||||
return $rootRouter._location.prepareExternalUrl(navigationHref);
|
||||
}
|
||||
|
||||
let routeParamsGetter = $parse(link);
|
||||
@ -269,11 +237,11 @@ function ngLinkDirective($router, $parse) {
|
||||
}
|
||||
|
||||
element.on('click', event => {
|
||||
if (event.which !== 1 || !instruction) {
|
||||
if (event.which !== 1 || !navigationInstruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
$router.navigateByInstruction(instruction);
|
||||
$rootRouter.navigateByInstruction(navigationInstruction);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
@ -287,13 +255,7 @@ function dashCase(str: string): string {
|
||||
* A module for adding new a routing system Angular 1.
|
||||
*/
|
||||
angular.module('ngComponentRouter', [])
|
||||
.directive('ngOutlet', ngOutletDirective)
|
||||
.directive('ngOutlet', ngOutletFillContentDirective)
|
||||
.directive('ngLink', ngLinkDirective);
|
||||
|
||||
/*
|
||||
* A module for inspecting controller constructors
|
||||
*/
|
||||
angular.module('ng')
|
||||
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
|
||||
.config(compilerProviderDecorator);
|
||||
.directive('ngOutlet', ['$animate', '$q', '$rootRouter', ngOutletDirective])
|
||||
.directive('ngOutlet', ['$compile', ngOutletFillContentDirective])
|
||||
.directive('ngLink', ['$rootRouter', '$parse', ngLinkDirective])
|
||||
.directive('$router', ['$q', routerTriggerDirective]);
|
||||
|
52
modules/angular1_router/src/ng_route_shim.js
vendored
52
modules/angular1_router/src/ng_route_shim.js
vendored
@ -24,12 +24,12 @@
|
||||
.directive('a', anchorLinkDirective)
|
||||
|
||||
// Connects the legacy $routeProvider config shim to Component Router's config.
|
||||
.run(['$route', '$router', function ($route, $router) {
|
||||
.run(['$route', '$rootRouter', function ($route, $rootRouter) {
|
||||
$route.$$subscribe(function (routeDefinition) {
|
||||
if (!angular.isArray(routeDefinition)) {
|
||||
routeDefinition = [routeDefinition];
|
||||
}
|
||||
$router.config(routeDefinition);
|
||||
$rootRouter.config(routeDefinition);
|
||||
});
|
||||
}]);
|
||||
|
||||
@ -116,53 +116,41 @@
|
||||
console.warn('Route for "' + path + '" should use "controllerAs".');
|
||||
}
|
||||
|
||||
var directiveName = routeObjToRouteName(routeCopy, path);
|
||||
var componentName = routeObjToRouteName(routeCopy, path);
|
||||
|
||||
if (!directiveName) {
|
||||
if (!componentName) {
|
||||
throw new Error('Could not determine a name for route "' + path + '".');
|
||||
}
|
||||
|
||||
routeDefinition.component = directiveName;
|
||||
routeDefinition.name = route.name || upperCase(directiveName);
|
||||
routeDefinition.component = componentName;
|
||||
routeDefinition.name = route.name || upperCase(componentName);
|
||||
|
||||
var directiveController = routeCopy.controller;
|
||||
|
||||
var directiveDefinition = {
|
||||
scope: false,
|
||||
var componentDefinition = {
|
||||
controller: directiveController,
|
||||
controllerAs: routeCopy.controllerAs,
|
||||
templateUrl: routeCopy.templateUrl,
|
||||
template: routeCopy.template
|
||||
};
|
||||
controllerAs: routeCopy.controllerAs
|
||||
|
||||
var directiveFactory = function () {
|
||||
return directiveDefinition;
|
||||
};
|
||||
if (routeCopy.templateUrl) componentDefinition.templateUrl = routeCopy.templateUrl;
|
||||
if (routeCopy.template) componentDefinition.template = routeCopy.template;
|
||||
|
||||
|
||||
// if we have route resolve options, prepare a wrapper controller
|
||||
if (directiveController && routeCopy.resolve) {
|
||||
var originalController = directiveController;
|
||||
var resolvedLocals = {};
|
||||
|
||||
directiveDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
|
||||
componentDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
|
||||
var locals = angular.extend({
|
||||
$scope: $scope
|
||||
}, resolvedLocals);
|
||||
|
||||
var ctrl = $injector.instantiate(originalController, locals);
|
||||
|
||||
if (routeCopy.controllerAs) {
|
||||
locals.$scope[routeCopy.controllerAs] = ctrl;
|
||||
}
|
||||
|
||||
return ctrl;
|
||||
return $injector.instantiate(originalController, locals);
|
||||
}];
|
||||
|
||||
// we take care of controllerAs in the directive controller wrapper
|
||||
delete directiveDefinition.controllerAs;
|
||||
|
||||
// we resolve the locals in a canActivate block
|
||||
directiveFactory.$canActivate = function() {
|
||||
componentDefinition.controller.$canActivate = function() {
|
||||
var locals = angular.extend({}, routeCopy.resolve);
|
||||
|
||||
angular.forEach(locals, function(value, key) {
|
||||
@ -179,7 +167,7 @@
|
||||
}
|
||||
|
||||
// register the dynamically created directive
|
||||
$compileProvider.directive(directiveName, directiveFactory);
|
||||
$compileProvider.component(componentName, componentDefinition);
|
||||
}
|
||||
if (subscriptionFn) {
|
||||
subscriptionFn(routeDefinition);
|
||||
@ -253,12 +241,12 @@
|
||||
|
||||
}
|
||||
|
||||
function $routeParamsFactory($router, $rootScope) {
|
||||
function $routeParamsFactory($rootRouter, $rootScope) {
|
||||
// the identity of this object cannot change
|
||||
var paramsObj = {};
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function () {
|
||||
var newParams = $router._currentInstruction && $router._currentInstruction.component.params;
|
||||
var newParams = $rootRouter.currentInstruction && $rootRouter.currentInstruction.component.params;
|
||||
|
||||
angular.forEach(paramsObj, function (val, name) {
|
||||
delete paramsObj[name];
|
||||
@ -274,7 +262,7 @@
|
||||
/**
|
||||
* Allows normal anchor links to kick off routing.
|
||||
*/
|
||||
function anchorLinkDirective($router) {
|
||||
function anchorLinkDirective($rootRouter) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function (scope, element) {
|
||||
@ -293,8 +281,8 @@
|
||||
}
|
||||
|
||||
var href = element.attr(hrefAttrName);
|
||||
if (href && $router.recognize(href)) {
|
||||
$router.navigateByUrl(href);
|
||||
if (href && $rootRouter.recognize(href)) {
|
||||
$rootRouter.navigateByUrl(href);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
@ -1,38 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('$$directiveIntrospector', function () {
|
||||
|
||||
var $compileProvider;
|
||||
|
||||
beforeEach(function() {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function(_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the introspector function whenever a directive factory is registered', inject(function ($$directiveIntrospector) {
|
||||
var spy = jasmine.createSpy();
|
||||
$$directiveIntrospector(spy);
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', myDir);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||
}));
|
||||
|
||||
it('should call the introspector function whenever a directive factory is registered with array annotations', inject(function ($$directiveIntrospector) {
|
||||
var spy = jasmine.createSpy();
|
||||
$$directiveIntrospector(spy);
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||
}));
|
||||
|
||||
it('should retrieve a factory based on directive name', inject(function ($$directiveIntrospector) {
|
||||
function myDir(){}
|
||||
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||
expect($$directiveIntrospector.getTypeByName('myDir')).toBe(myDir);
|
||||
}));
|
||||
});
|
@ -5,7 +5,7 @@ describe('ngOutlet animations', function () {
|
||||
$animate,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$rootRouter,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -17,15 +17,18 @@ describe('ngOutlet animations', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
|
||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$rootRouter_) {
|
||||
$animate = _$animate_;
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
});
|
||||
|
||||
registerComponent('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
|
||||
$routerOnActivate: function(next) {
|
||||
this.$routeParams = next.params;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -38,11 +41,11 @@ describe('ngOutlet animations', function () {
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
@ -51,7 +54,7 @@ describe('ngOutlet animations', function () {
|
||||
expect(item.event).toBe('enter');
|
||||
|
||||
// navigate to pete
|
||||
$router.navigateByUrl('/user/pete');
|
||||
$rootRouter.navigateByUrl('/user/pete');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello pete');
|
||||
|
||||
|
@ -4,7 +4,7 @@ describe('Navigation lifecycle', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$rootRouter,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -14,10 +14,10 @@ describe('Navigation lifecycle', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
});
|
||||
|
||||
registerComponent('oneCmp', {
|
||||
@ -38,12 +38,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -56,12 +56,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
|
||||
@ -75,15 +75,15 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'oneCmp' },
|
||||
{ path: '/post/:id', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
|
||||
instructionFor('oneCmp'));
|
||||
@ -98,12 +98,12 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user');
|
||||
$rootRouter.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(injectedScope).toBeDefined();
|
||||
@ -116,15 +116,15 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/b');
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
@ -136,15 +136,15 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
@ -166,15 +166,15 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/b');
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['deactivate', 'activate']);
|
||||
@ -203,19 +203,19 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/on-reuse/1/a');
|
||||
$rootRouter.navigateByUrl('/on-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$router.navigateByUrl('/on-reuse/2/b');
|
||||
$rootRouter.navigateByUrl('/on-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
@ -245,19 +245,19 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/never-reuse/1/a');
|
||||
$rootRouter.navigateByUrl('/never-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$router.navigateByUrl('/never-reuse/2/b');
|
||||
$rootRouter.navigateByUrl('/never-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(2);
|
||||
@ -274,12 +274,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
@ -296,12 +296,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: activateSpy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(canActivateSpy).toHaveBeenCalled();
|
||||
@ -320,12 +320,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -341,17 +341,17 @@ describe('Navigation lifecycle', function () {
|
||||
|
||||
spy.$inject = ['$nextInstruction', '$http'];
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
var args = spy.calls.mostRecent().args;
|
||||
expect(args[0].params).toEqual({name: 'brian'});
|
||||
expect(args[0].params).toEqual(jasmine.objectContaining({name: 'brian'}));
|
||||
expect(args[1]).toBe($http);
|
||||
}));
|
||||
|
||||
@ -364,17 +364,17 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
});
|
||||
@ -388,17 +388,17 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { one }');
|
||||
});
|
||||
@ -414,12 +414,12 @@ describe('Navigation lifecycle', function () {
|
||||
$routerOnActivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -433,15 +433,15 @@ describe('Navigation lifecycle', function () {
|
||||
$routerCanDeactivate: spy
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
@ -466,10 +466,10 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
|
||||
if (options.$canActivate) {
|
||||
factory.$canActivate = options.$canActivate;
|
||||
controller.$canActivate = options.$canActivate;
|
||||
}
|
||||
if (options.$routeConfig) {
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
controller.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
|
@ -5,7 +5,7 @@ describe('navigation', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$rootRouter,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -15,14 +15,17 @@ describe('navigation', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
});
|
||||
|
||||
registerDirective('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
|
||||
$routerOnActivate: function(next) {
|
||||
this.$routeParams = next.params;
|
||||
}
|
||||
});
|
||||
registerDirective('oneCmp', {
|
||||
template: '<div>{{oneCmp.number}}</div>',
|
||||
@ -49,11 +52,11 @@ describe('navigation', function () {
|
||||
it('should work in a simple case', function () {
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
@ -63,11 +66,11 @@ describe('navigation', function () {
|
||||
it('should work with components created by the `mod.component()` helper', function () {
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/', component: 'threeCmp' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('three');
|
||||
@ -75,16 +78,16 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should navigate between components with different parameters', function () {
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootRouter.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
@ -103,17 +106,17 @@ describe('navigation', function () {
|
||||
controller: ParentController
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/parent/...', component: 'parentCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/parent/user/brian');
|
||||
$rootRouter.navigateByUrl('/parent/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello brian }');
|
||||
|
||||
$router.navigateByUrl('/parent/user/igor');
|
||||
$rootRouter.navigateByUrl('/parent/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello igor }');
|
||||
@ -128,12 +131,12 @@ describe('navigation', function () {
|
||||
]
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/a/...', component: 'childCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$router.navigateByUrl('/a/b');
|
||||
$rootRouter.navigateByUrl('/a/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('outer { inner { one } }');
|
||||
@ -147,12 +150,12 @@ describe('navigation', function () {
|
||||
]
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/...', component: 'childCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('outer { inner { one } }');
|
||||
@ -168,26 +171,26 @@ describe('navigation', function () {
|
||||
{ path: '/end', component: 'oneCmp' }
|
||||
]});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/recur', component: 'recurCmp' },
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
compile('<div>root { <div ng-outlet></div> }</div>');
|
||||
$router.navigateByUrl('/recur/recur/end');
|
||||
$rootRouter.navigateByUrl('/recur/recur/end');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root { one }');
|
||||
});
|
||||
|
||||
|
||||
it('should change location path', inject(function ($location) {
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user');
|
||||
$rootRouter.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
@ -195,13 +198,13 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should pass through query terms to the location', inject(function ($location) {
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/user?x=y');
|
||||
$rootRouter.navigateByUrl('/user?x=y');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
@ -212,12 +215,12 @@ describe('navigation', function () {
|
||||
it('should change location to the canonical route', inject(function ($location) {
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/', redirectTo: ['/User'] },
|
||||
{ path: '/user', component: 'userCmp', name: 'User' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
@ -233,7 +236,7 @@ describe('navigation', function () {
|
||||
]
|
||||
});
|
||||
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/old-parent/old-child', redirectTo: ['/NewParent', 'NewChild'] },
|
||||
{ path: '/old-parent/old-child-two', redirectTo: ['/NewParent', 'NewChildTwo'] },
|
||||
{ path: '/new-parent/...', component: 'childRouter', name: 'NewParent' }
|
||||
@ -241,13 +244,13 @@ describe('navigation', function () {
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/old-parent/old-child');
|
||||
$rootRouter.navigateByUrl('/old-parent/old-child');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child');
|
||||
expect(elt.text()).toBe('inner { one }');
|
||||
|
||||
$router.navigateByUrl('/old-parent/old-child-two');
|
||||
$rootRouter.navigateByUrl('/old-parent/old-child-two');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child-two');
|
||||
@ -256,7 +259,7 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should navigate when the location path changes', inject(function ($location) {
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/one', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
@ -269,7 +272,7 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should navigate when the location query changes', inject(function ($location) {
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/get/params', component: 'getParams' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
@ -281,7 +284,7 @@ describe('navigation', function () {
|
||||
}));
|
||||
|
||||
|
||||
it('should expose a "navigating" property on $router', inject(function ($q) {
|
||||
it('should expose a "navigating" property on $rootRouter', inject(function ($q) {
|
||||
var defer;
|
||||
registerDirective('pendingActivate', {
|
||||
$canActivate: function () {
|
||||
@ -289,28 +292,29 @@ describe('navigation', function () {
|
||||
return defer.promise;
|
||||
}
|
||||
});
|
||||
$router.config([
|
||||
$rootRouter.config([
|
||||
{ path: '/pending-activate', component: 'pendingActivate' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/pending-activate');
|
||||
$rootRouter.navigateByUrl('/pending-activate');
|
||||
$rootScope.$digest();
|
||||
expect($router.navigating).toBe(true);
|
||||
expect($rootRouter.navigating).toBe(true);
|
||||
defer.resolve();
|
||||
$rootScope.$digest();
|
||||
expect($router.navigating).toBe(false);
|
||||
expect($rootRouter.navigating).toBe(false);
|
||||
}));
|
||||
|
||||
function registerDirective(name, options) {
|
||||
var controller = getController(options);
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
controllerAs: name,
|
||||
controller: getController(options)
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
applyStaticProperties(factory, options);
|
||||
applyStaticProperties(controller, options);
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
@ -320,7 +324,7 @@ describe('navigation', function () {
|
||||
template: options.template || '',
|
||||
controller: getController(options),
|
||||
}
|
||||
applyStaticProperties(definition, options);
|
||||
applyStaticProperties(definition.controller, options);
|
||||
$compileProvider.component(name, definition);
|
||||
}
|
||||
|
||||
|
@ -2,30 +2,14 @@
|
||||
|
||||
describe('router', function () {
|
||||
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
var elt, testMod;
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function($provide) {
|
||||
$provide.value('$routerRootComponent', 'app');
|
||||
});
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
testMod = angular.module('testMod', ['ngComponentRouter'])
|
||||
.value('$routerRootComponent', 'app');
|
||||
});
|
||||
|
||||
it('should work with a provided root component', inject(function($location) {
|
||||
it('should work with a provided root component', function() {
|
||||
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home'
|
||||
});
|
||||
@ -37,20 +21,23 @@ describe('router', function () {
|
||||
]
|
||||
});
|
||||
|
||||
compile('<app></app>');
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home');
|
||||
}));
|
||||
inject(function($location, $rootScope) {
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home');
|
||||
});
|
||||
});
|
||||
|
||||
it('should bind the component to the current router', inject(function($location) {
|
||||
it('should bind the component to the current router', function() {
|
||||
var router;
|
||||
registerComponent('homeCmp', {
|
||||
bindings: { router: '=' },
|
||||
bindings: { $router: '=' },
|
||||
controller: function($scope, $element) {
|
||||
this.$routerOnActivate = function() {
|
||||
router = this.router;
|
||||
router = this.$router;
|
||||
};
|
||||
},
|
||||
template: 'Home'
|
||||
@ -63,64 +50,146 @@ describe('router', function () {
|
||||
]
|
||||
});
|
||||
|
||||
compile('<app></app>');
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
var homeElement = elt.find('home-cmp');
|
||||
expect(homeElement.text()).toBe('Home');
|
||||
expect(homeElement.isolateScope().$ctrl.router).toBeDefined();
|
||||
expect(router).toBeDefined();
|
||||
}));
|
||||
inject(function($location, $rootScope) {
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
var homeElement = elt.find('home-cmp');
|
||||
expect(homeElement.text()).toBe('Home');
|
||||
expect(homeElement.isolateScope().$ctrl.$router).toBeDefined();
|
||||
expect(router).toBeDefined();
|
||||
})
|
||||
});
|
||||
|
||||
it('should work when an async route is provided route data', inject(function($location, $q) {
|
||||
registerDirective('homeCmp', {
|
||||
template: 'Home ({{homeCmp.isAdmin}})',
|
||||
it('should work when an async route is provided route data', function() {
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home ({{$ctrl.isAdmin}})',
|
||||
$routerOnActivate: function(next, prev) {
|
||||
this.isAdmin = next.routeData.data.isAdmin;
|
||||
}
|
||||
});
|
||||
|
||||
registerDirective('app', {
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', loader: function() { return $q.when('homeCmp'); }, data: { isAdmin: true } }
|
||||
{ path: '/', loader: function($q) { return $q.when('homeCmp'); }, data: { isAdmin: true } }
|
||||
]
|
||||
});
|
||||
|
||||
compile('<app></app>');
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home (true)');
|
||||
}));
|
||||
inject(function($location, $rootScope) {
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home (true)');
|
||||
});
|
||||
});
|
||||
|
||||
function registerDirective(name, options) {
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
controllerAs: name,
|
||||
controller: getController(options)
|
||||
};
|
||||
}
|
||||
applyStaticProperties(factory, options);
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
it('should work with a templateUrl component', function() {
|
||||
|
||||
var $routerOnActivate = jasmine.createSpy('$routerOnActivate');
|
||||
|
||||
registerComponent('homeCmp', {
|
||||
templateUrl: 'homeCmp.html',
|
||||
$routerOnActivate: $routerOnActivate
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', component: 'homeCmp' }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
|
||||
inject(function($location, $rootScope, $httpBackend) {
|
||||
|
||||
$httpBackend.expectGET('homeCmp.html').respond('Home');
|
||||
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
$httpBackend.flush();
|
||||
var homeElement = elt.find('home-cmp');
|
||||
expect(homeElement.text()).toBe('Home');
|
||||
expect($routerOnActivate).toHaveBeenCalled();
|
||||
})
|
||||
});
|
||||
|
||||
it('should provide the current instruction', function() {
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home ({{homeCmp.isAdmin}})'
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', component: 'homeCmp', name: 'Home' }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
|
||||
inject(function($rootScope, $rootRouter, $location) {
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
var instruction = $rootRouter.generate(['/Home']);
|
||||
expect($rootRouter.currentInstruction).toEqual(instruction);
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide the root level router', function() {
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home ({{homeCmp.isAdmin}})',
|
||||
bindings: {
|
||||
$router: '<'
|
||||
}
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', component: 'homeCmp', name: 'Home' }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
|
||||
inject(function($rootScope, $rootRouter, $location) {
|
||||
compileApp();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
var homeElement = elt.find('home-cmp');
|
||||
expect(homeElement.isolateScope().$ctrl.$router.root).toEqual($rootRouter);
|
||||
});
|
||||
});
|
||||
|
||||
function registerComponent(name, options) {
|
||||
|
||||
var definition = {
|
||||
bindings: options.bindings,
|
||||
template: options.template || '',
|
||||
controller: getController(options),
|
||||
}
|
||||
applyStaticProperties(definition, options);
|
||||
$compileProvider.component(name, definition);
|
||||
};
|
||||
if (options.template) definition.template = options.template;
|
||||
if (options.templateUrl) definition.templateUrl = options.templateUrl;
|
||||
|
||||
applyStaticProperties(definition.controller, options);
|
||||
angular.module('testMod').component(name, definition);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
function compileApp() {
|
||||
inject(function($compile, $rootScope) {
|
||||
elt = $compile('<div><app></app</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
return elt;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ describe('ngRoute shim', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$rootRouter,
|
||||
$compileProvider,
|
||||
$routeProvider;
|
||||
|
||||
@ -18,10 +18,10 @@ describe('ngRoute shim', function () {
|
||||
$routeProvider = _$routeProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
});
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
@ -55,7 +55,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {one}');
|
||||
}));
|
||||
@ -76,7 +76,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('value: 42');
|
||||
@ -94,11 +94,11 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootRouter.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
@ -115,7 +115,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$router.navigateByUrl('/home/foo/bar/123');
|
||||
$rootRouter.navigateByUrl('/home/foo/bar/123');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('rest: foo/bar/123');
|
||||
});
|
||||
@ -129,7 +129,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/home/test');
|
||||
$rootRouter.navigateByUrl('/home/test');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {}');
|
||||
expect(console.warn)
|
||||
@ -150,7 +150,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/');
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
@ -169,7 +169,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$router.navigateByUrl('/somewhere');
|
||||
$rootRouter.navigateByUrl('/somewhere');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
|
304
modules/angular1_router/test/ng_link_spec.js
vendored
304
modules/angular1_router/test/ng_link_spec.js
vendored
@ -2,169 +2,191 @@
|
||||
|
||||
describe('ngLink', function () {
|
||||
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$compileProvider;
|
||||
describe('html5Mode enabled', function () {
|
||||
runHrefTestsAndExpectPrefix('/', true);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
describe('html5Mode disabled', function () {
|
||||
runHrefTestsAndExpectPrefix('', false, '');
|
||||
});
|
||||
|
||||
describe('html5Mode disabled, with hash prefix', function () {
|
||||
runHrefTestsAndExpectPrefix('', false, '!');
|
||||
});
|
||||
|
||||
describe('html5Mode enabled', function () {
|
||||
runHrefTestsAndExpectPrefix('/moo', true);
|
||||
});
|
||||
|
||||
describe('html5Mode disabled', function () {
|
||||
runHrefTestsAndExpectPrefix('/moo', false, '');
|
||||
});
|
||||
|
||||
describe('html5Mode disabled, with hash prefix', function () {
|
||||
runHrefTestsAndExpectPrefix('/moo', false, '!');
|
||||
});
|
||||
|
||||
function runHrefTestsAndExpectPrefix(baseHref, html5Mode, hashPrefix) {
|
||||
var prefix = html5Mode ? '.' : '#' + hashPrefix;
|
||||
|
||||
it('should allow linking from the parent to the child', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
|
||||
var elt = compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
||||
navigateTo('/a');
|
||||
expect(elt.find('a').attr('href')).toBe(prefix + '/b');
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
it('should allow linking from the child and the parent', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
|
||||
var elt = compile('outer { <div ng-outlet></div> }');
|
||||
navigateTo('/b');
|
||||
expect(elt.find('a').attr('href')).toBe(prefix + '/b');
|
||||
});
|
||||
|
||||
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
|
||||
registerComponent('oneCmp', '<div>{{oneCmp.number}}</div>', function () {this.number = 'one'});
|
||||
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||
});
|
||||
|
||||
it('should allow params in routerLink directive', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||
configureRouter([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
|
||||
var elt = compile('<div ng-outlet></div>');
|
||||
navigateTo('/a');
|
||||
expect(elt.find('a').attr('href')).toBe(prefix + '/b/lol');
|
||||
});
|
||||
|
||||
|
||||
it('should allow linking from the parent to the child', function () {
|
||||
$router.config([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
||||
it('should update the href of links with bound params', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: $ctrl.number}]">{{$ctrl.number}}</a></div>', function () {this.number = 43});
|
||||
configureRouter([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b');
|
||||
});
|
||||
|
||||
it('should allow linking from the child and the parent', function () {
|
||||
$router.config([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b');
|
||||
});
|
||||
var elt = compile('<div ng-outlet></div>');
|
||||
navigateTo('/a');
|
||||
expect(elt.find('a').text()).toBe('43');
|
||||
expect(elt.find('a').attr('href')).toBe(prefix + '/b/43');
|
||||
});
|
||||
|
||||
|
||||
it('should allow params in routerLink directive', function () {
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||
it('should navigate on left-mouse click when a link url matches a route', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
var elt = compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
expect(elt.text()).toBe('link | one');
|
||||
expect(elt.find('a').attr('href')).toBe(prefix + '/two');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b/lol');
|
||||
});
|
||||
|
||||
|
||||
it('should update the href of links with bound params', function () {
|
||||
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
|
||||
$router.config([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', name: 'Two' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b/param');
|
||||
});
|
||||
|
||||
|
||||
it('should navigate on left-mouse click when a link url matches a route', function () {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./two');
|
||||
|
||||
elt.find('a')[0].click();
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | two');
|
||||
});
|
||||
|
||||
|
||||
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
elt.find('a').triggerHandler({ type: 'click', which: 3 });
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link | one');
|
||||
}));
|
||||
|
||||
|
||||
// See https://github.com/angular/router/issues/206
|
||||
it('should not navigate a link without an href', function () {
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
expect(function () {
|
||||
compile('<a>link</a>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link');
|
||||
elt.find('a')[0].click();
|
||||
$rootScope.$digest();
|
||||
}).not.toThrow();
|
||||
});
|
||||
inject(function($rootScope) { $rootScope.$digest(); });
|
||||
expect(elt.text()).toBe('link | two');
|
||||
});
|
||||
|
||||
|
||||
function registerComponent(name, template, config) {
|
||||
var controller = function () {};
|
||||
it('should not navigate on non-left mouse click when a link url matches a route', function() {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
var elt = compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||
expect(elt.text()).toBe('link | one');
|
||||
elt.find('a').triggerHandler({ type: 'click', which: 3 });
|
||||
inject(function($rootScope) { $rootScope.$digest(); });
|
||||
expect(elt.text()).toBe('link | one');
|
||||
});
|
||||
|
||||
|
||||
// See https://github.com/angular/router/issues/206
|
||||
it('should not navigate a link without an href', function () {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/', component: 'oneCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
]);
|
||||
expect(function () {
|
||||
var elt = compile('<a>link</a>');
|
||||
expect(elt.text()).toBe('link');
|
||||
elt.find('a')[0].click();
|
||||
inject(function($rootScope) { $rootScope.$digest(); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should add an ng-link-active class on the current link', function() {
|
||||
setup({baseHref: baseHref, html5Mode: html5Mode, hashPrefix: hashPrefix});
|
||||
configureRouter([
|
||||
{ path: '/', component: 'oneCmp', name: 'One' }
|
||||
]);
|
||||
|
||||
var elt = compile('<a ng-link="[\'/One\']">one</a> | <div ng-outlet></div>');
|
||||
navigateTo('/');
|
||||
expect(elt.find('a').attr('class')).toBe('ng-link-active');
|
||||
});
|
||||
}
|
||||
|
||||
function registerComponent(name, template, controller) {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.component(name, {
|
||||
template: template,
|
||||
controllerAs: name,
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!template) {
|
||||
template = '';
|
||||
}
|
||||
if (angular.isArray(config)) {
|
||||
factory.annotations = [new angular.annotations.RouteConfig(config)];
|
||||
} else if (typeof config === 'function') {
|
||||
controller = config;
|
||||
} else if (typeof config === 'object') {
|
||||
if (config.canActivate) {
|
||||
controller.$canActivate = config.canActivate;
|
||||
}
|
||||
}
|
||||
$compileProvider.directive(name, factory);
|
||||
function setup(config) {
|
||||
module(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate) {
|
||||
$delegate.baseHref = function() { return config.baseHref; };
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
module('ngComponentRouter');
|
||||
module(function($locationProvider) {
|
||||
$locationProvider.html5Mode(config.html5Mode);
|
||||
$locationProvider.hashPrefix(config.hashPrefix);
|
||||
});
|
||||
registerComponent('userCmp', '<div>hello {{$ctrl.$routeParams.name}}</div>', function () {});
|
||||
registerComponent('oneCmp', '<div>{{$ctrl.number}}</div>', function () {this.number = 'one'});
|
||||
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{$ctrl.number}}</a></div>', function () {this.number = 'two'});
|
||||
}
|
||||
|
||||
function configureRouter(routeConfig) {
|
||||
inject(function($rootRouter) {
|
||||
$rootRouter.config(routeConfig);
|
||||
});
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var elt;
|
||||
inject(function($compile, $rootScope) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
return elt;
|
||||
}
|
||||
|
||||
function navigateTo(url) {
|
||||
inject(function($rootRouter, $rootScope) {
|
||||
$rootRouter.navigateByUrl(url);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -22,7 +22,7 @@ function provideHelpers(fn, preInject) {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$rootRouter,
|
||||
$templateCache,
|
||||
$controllerProvider;
|
||||
|
||||
@ -32,10 +32,10 @@ function provideHelpers(fn, preInject) {
|
||||
$controllerProvider = _$controllerProvider_;
|
||||
});
|
||||
|
||||
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
||||
inject(function(_$compile_, _$rootScope_, _$rootRouter_, _$templateCache_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$templateCache = _$templateCache_;
|
||||
});
|
||||
|
||||
@ -72,7 +72,7 @@ function provideHelpers(fn, preInject) {
|
||||
|
||||
fn({
|
||||
registerComponent: registerComponent,
|
||||
$router: $router,
|
||||
$rootRouter: $rootRouter,
|
||||
put: put,
|
||||
compile: compile
|
||||
})
|
||||
|
@ -72,7 +72,7 @@ An example of an Angular 2 project built with WebPack can be found in the [angul
|
||||
|
||||
Polyfills are required for Angular 2 to function properly (the exact list depends on the browser used) and external dependencies ([zone.js](https://github.com/angular/zone.js)).
|
||||
To ease setup of Angular 2 applications there is one file - `angular2-polyfills.js` - that combines:
|
||||
* a pollyfill mandatory for all browsers: [reflect-metadata](https://www.npmjs.com/package/reflect-metadata)
|
||||
* a polyfill mandatory for all browsers: [reflect-metadata](https://www.npmjs.com/package/reflect-metadata)
|
||||
* [zone.js](https://github.com/angular/zone.js)
|
||||
|
||||
**Note**: `angular2-polyfills.js` contains code that should be loaded into the browser as the very first code of the web application even before the module loader. The preferred solution is to load the mentioned file in a `script` tag as early as possible.
|
||||
|
@ -4,7 +4,7 @@ Bootstrapping
|
||||
@description
|
||||
{@target ts}`import {bootstrap} from 'angular2/platform/browser';`{@endtarget}
|
||||
{@target js}Available from the `ng.platform.browser` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/bootstrap.dart';`{@endtarget}
|
||||
{@target dart}`import 'package:angular2/platform/browser.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
|
@ -112,9 +112,7 @@ Example of a component:
|
||||
'title', | - title mapped to component title
|
||||
'open' | - open attribute mapped to component's open property
|
||||
], |
|
||||
}) |
|
||||
@View({ | View annotation
|
||||
templateUrl: 'pane.html' | - URL of template HTML
|
||||
templateUrl: 'pane.html' | URL of template HTML
|
||||
}) |
|
||||
class Pane { | Component controller class
|
||||
title:string; | - title property
|
||||
@ -227,11 +225,9 @@ class MyService {} | Assume a service which needs to be inject
|
||||
|
|
||||
@Component({ | Assume a top level application component which
|
||||
selector: 'my-app', | configures the services to be injected.
|
||||
viewBindings: [MyService] |
|
||||
}) |
|
||||
@View({ | Assume we have a template that needs to be
|
||||
templateUrl: 'my_app.html', | configured with directives to be injected.
|
||||
directives: [House] |
|
||||
viewBindings: [MyService], |
|
||||
templateUrl: 'my_app.html', | Assume we have a template that needs to be
|
||||
directives: [House] | configured with directives to be injected.
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@ -273,8 +269,6 @@ Here is an example of the kinds of injections which can be achieved:
|
||||
```
|
||||
@Component({ |
|
||||
selector: 'my-app' |
|
||||
}) |
|
||||
@View({ |
|
||||
templateUrl: 'my_app.html', |
|
||||
directives: [Form, FieldSet, |
|
||||
Field, Primary] |
|
||||
@ -329,8 +323,6 @@ Shadow DOM provides an encapsulation for components, so as a general rule it doe
|
||||
```
|
||||
@Component({
|
||||
selector: '[kid]'
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'kid.html',
|
||||
directives: []
|
||||
})
|
||||
@ -347,8 +339,6 @@ class Kid {
|
||||
|
||||
@Component({
|
||||
selector: '[dad]'
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'dad.html',
|
||||
directives: [Kid]
|
||||
})
|
||||
@ -361,9 +351,7 @@ class Dad {
|
||||
|
||||
@Component({
|
||||
selector: '[grandpa]',
|
||||
viewBindings: []
|
||||
})
|
||||
@View({
|
||||
viewBindings: [],
|
||||
templateUrl: 'grandpa.html',
|
||||
directives: [Dad]
|
||||
})
|
||||
|
@ -179,9 +179,7 @@ Both `MyComponent` and `MyDirective` are created on the same element.
|
||||
],
|
||||
viewBindings: [
|
||||
bind('viewService').toValue('View_MyComponentService')
|
||||
]
|
||||
})
|
||||
@View({
|
||||
],
|
||||
template: `<needs-view-service></needs-view-service>`,
|
||||
directives: [NeedsViewService]
|
||||
})
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {UrlResolver} from 'angular2/compiler';
|
||||
|
||||
var MyApp;
|
||||
var MyApp: any;
|
||||
|
||||
// #docregion url_resolver
|
||||
class MyUrlResolver extends UrlResolver {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {DebugElement} from 'angular2/core';
|
||||
|
||||
var debugElement: DebugElement;
|
||||
var predicate;
|
||||
var predicate: any;
|
||||
|
||||
// #docregion scope_all
|
||||
debugElement.query(predicate);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {NG_VALIDATORS} from 'angular2/common';
|
||||
import {Provider} from 'angular2/core';
|
||||
|
||||
let MyApp = null;
|
||||
let myValidator = null;
|
||||
let MyApp: Function = null;
|
||||
let myValidator: any = null;
|
||||
|
||||
// #docregion ng_validators
|
||||
bootstrap(MyApp, [new Provider(NG_VALIDATORS, {useValue: myValidator, multi: true})]);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {Observable, Subscriber} from 'rxjs/Rx';
|
||||
|
||||
// #docregion AsyncPipe
|
||||
@Component({
|
||||
@ -37,8 +37,9 @@ export class AsyncPipeExample {
|
||||
// #docregion AsyncPipeObservable
|
||||
@Component({selector: "task-cmp", template: "Time: {{ time | async }}"})
|
||||
class Task {
|
||||
time = new Observable<number>(
|
||||
observer => { setInterval(_ => observer.next(new Date().getTime()), 500); });
|
||||
time = new Observable<number>((observer: Subscriber<number>) => {
|
||||
setInterval(_ => observer.next(new Date().getTime()), 500);
|
||||
});
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
|
||||
// #docregion DatePipe
|
||||
@Component({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
|
||||
// #docregion JsonPipe
|
||||
@Component({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
|
||||
// #docregion LowerUpperPipe
|
||||
@Component({
|
||||
@ -12,7 +12,7 @@ import {bootstrap} from 'angular2/bootstrap';
|
||||
})
|
||||
export class LowerUpperPipeExample {
|
||||
value: string;
|
||||
change(value) { this.value = value; }
|
||||
change(value: string) { this.value = value; }
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
|
||||
// #docregion NumberPipe
|
||||
@Component({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
|
||||
// #docregion SlicePipe_string
|
||||
@Component({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Component, Attribute, Directive, Pipe} from 'angular2/core';
|
||||
|
||||
var CustomDirective;
|
||||
var CustomDirective: Function;
|
||||
|
||||
// #docregion component
|
||||
@Component({selector: 'greet', template: 'Hello {{name}}!', directives: [CustomDirective]})
|
||||
@ -20,7 +20,7 @@ class Page {
|
||||
// #docregion attributeMetadata
|
||||
@Directive({selector: 'input'})
|
||||
class InputAttrDirective {
|
||||
constructor(@Attribute('type') type) {
|
||||
constructor(@Attribute('type') type: string) {
|
||||
// type would be 'text' in this example
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,6 @@ class InputDirective {
|
||||
// #docregion pipe
|
||||
@Pipe({name: 'lowercase'})
|
||||
class Lowercase {
|
||||
transform(v, args) { return v.toLowerCase(); }
|
||||
transform(v: string, args: any[]) { return v.toLowerCase(); }
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -1,7 +1,7 @@
|
||||
// #docregion enableProdMode
|
||||
import {enableProdMode} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {MyComponent} from 'my_component';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {MyComponent} from './my_component';
|
||||
|
||||
enableProdMode();
|
||||
bootstrap(MyComponent);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// #docregion Observable
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
var obs = new Observable<number>(obs => {
|
||||
import {Observable, Subscriber} from 'rxjs/Rx';
|
||||
var obs = new Observable<number>((obs: Subscriber<number>) => {
|
||||
var i = 0;
|
||||
setInterval(_ => { obs.next(++i); }, 1000);
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
// #docregion Observable
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observable, Subscriber} from 'rxjs/Rx';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
var obs = new Observable(obs => {
|
||||
var obs = new Observable<number>((obs: Subscriber<any>) => {
|
||||
var i = 0;
|
||||
setInterval(_ => obs.next(++i), 1000);
|
||||
});
|
||||
obs.map(i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
|
||||
obs.map((i: number) => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
|
||||
// #enddocregion
|
||||
|
@ -1,10 +1,10 @@
|
||||
// #docregion Observable
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observable, Subscriber} from 'rxjs/Rx';
|
||||
import {map} from 'rxjs/operator/map';
|
||||
|
||||
var obs = new Observable(obs => {
|
||||
var obs = new Observable<number>((sub: Subscriber<number>) => {
|
||||
var i = 0;
|
||||
setInterval(_ => obs.next(++i), 1000);
|
||||
setInterval(_ => sub.next(++i), 1000);
|
||||
});
|
||||
map.call(obs, i => `${i} seconds elapsed`).subscribe(msg => console.log(msg));
|
||||
map.call(obs, (i: number) => `${i} seconds elapsed`).subscribe((msg: string) => console.log(msg));
|
||||
// #enddocregion
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {provide, Component} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {
|
||||
CanActivate,
|
||||
RouteConfig,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
function waitForElement(selector) {
|
||||
function waitForElement(selector: string) {
|
||||
var EC = (<any>protractor).ExpectedConditions;
|
||||
// Waits for the element with id 'abc' to be present on the dom.
|
||||
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {provide, Component} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {
|
||||
CanDeactivate,
|
||||
RouteConfig,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
function waitForElement(selector) {
|
||||
function waitForElement(selector: string) {
|
||||
var EC = (<any>protractor).ExpectedConditions;
|
||||
// Waits for the element with id 'abc' to be present on the dom.
|
||||
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {
|
||||
OnActivate,
|
||||
ComponentInstruction,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
function waitForElement(selector) {
|
||||
function waitForElement(selector: string) {
|
||||
var EC = (<any>protractor).ExpectedConditions;
|
||||
// Waits for the element with id 'abc' to be present on the dom.
|
||||
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, Injectable, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {
|
||||
OnDeactivate,
|
||||
ComponentInstruction,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
function waitForElement(selector) {
|
||||
function waitForElement(selector: string) {
|
||||
var EC = (<any>protractor).ExpectedConditions;
|
||||
// Waits for the element with id 'abc' to be present on the dom.
|
||||
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Component, provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/bootstrap';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {
|
||||
CanActivate,
|
||||
RouteConfig,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {verifyNoBrowserErrors} from 'angular2/src/testing/e2e_util';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
|
||||
function waitForElement(selector) {
|
||||
function waitForElement(selector: string) {
|
||||
var EC = (<any>protractor).ExpectedConditions;
|
||||
// Waits for the element with id 'abc' to be present on the dom.
|
||||
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||
|
@ -73,7 +73,7 @@ describe('some component', () => {
|
||||
// #docregion beforeEachProviders
|
||||
describe('some component', () => {
|
||||
beforeEachProviders(() => [provide(MyService, {useClass: MyMockService})]);
|
||||
it('uses MyService', inject([MyService], (service) => {
|
||||
it('uses MyService', inject([MyService], (service: MyMockService) => {
|
||||
// service is an instance of MyMockService.
|
||||
}));
|
||||
});
|
||||
@ -81,7 +81,7 @@ describe('some component', () => {
|
||||
|
||||
// #docregion afterEach
|
||||
describe('some component', () => {
|
||||
afterEach((done) => { db.reset().then((_) => done()); });
|
||||
afterEach((done: Function) => { db.reset().then((_: any) => done()); });
|
||||
it('uses the db', () => {
|
||||
// This test can leave the database in a dirty state.
|
||||
// The afterEach will ensure it gets reset.
|
||||
|
8
modules/angular2/i18n.ts
Normal file
8
modules/angular2/i18n.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Entry point to i18n
|
||||
*/
|
||||
export * from './src/i18n/message';
|
||||
export * from './src/i18n/xmb_serializer';
|
||||
export * from './src/i18n/message_extractor';
|
@ -8,14 +8,7 @@
|
||||
"license": "<%= packageJson.license %>",
|
||||
"repository": <%= JSON.stringify(packageJson.repository) %>,
|
||||
"devDependencies": <%= JSON.stringify(packageJson.defaultDevDependencies) %>,
|
||||
"dependencies": {
|
||||
"typings": "0.6.6"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "typings install --ambient --name es6-promise github:DefinitelyTyped/DefinitelyTyped/es6-promise/es6-promise.d.ts#830e8ebd9ef137d039d5c7ede24a421f08595f83; typings install --ambient --name es6-collections github:DefinitelyTyped/DefinitelyTyped/es6-collections/es6-collections.d.ts#9f97e2a2bc1f502550c9b4fcaad1c48df5521d37"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"es6-promise": "<%= packageJson.dependencies['es6-promise'] %>",
|
||||
"es6-shim": "<%= packageJson.dependencies['es6-shim'] %>",
|
||||
"reflect-metadata": "<%= packageJson.dependencies['reflect-metadata'] %>",
|
||||
"rxjs": "<%= packageJson.dependencies['rxjs'] %>",
|
||||
|
@ -13,7 +13,6 @@ export {
|
||||
} from 'angular2/src/platform/browser_common';
|
||||
|
||||
import {Type, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/promise';
|
||||
import {
|
||||
BROWSER_PROVIDERS,
|
||||
BROWSER_APP_COMMON_PROVIDERS
|
||||
|
@ -12,7 +12,6 @@ export {
|
||||
} from 'angular2/src/platform/browser_common';
|
||||
|
||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/promise';
|
||||
import {
|
||||
BROWSER_PROVIDERS,
|
||||
BROWSER_APP_COMMON_PROVIDERS
|
||||
|
@ -15,7 +15,7 @@ import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
||||
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
|
||||
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location/location_strategy';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
|
||||
import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";
|
||||
|
@ -16,7 +16,7 @@ import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
||||
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
|
||||
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location/location_strategy';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
|
||||
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';
|
||||
|
@ -18,15 +18,17 @@ dependencies:
|
||||
logging: '>=0.9.0 <0.12.0'
|
||||
observe: '^0.13.1'
|
||||
protobuf: '^0.5.0'
|
||||
quiver: '^0.21.4'
|
||||
source_span: '^1.0.0'
|
||||
stack_trace: '^1.1.1'
|
||||
build: '>=0.0.1'
|
||||
dev_dependencies:
|
||||
code_transformers: '>=0.2.9+4 <0.4.0'
|
||||
transformer_test: '^0.2.0'
|
||||
guinness: '^0.1.18'
|
||||
guinness2: '0.0.5'
|
||||
quiver: '^0.21.4'
|
||||
test: '^0.12.6'
|
||||
transformers:
|
||||
- angular2
|
||||
- angular2/transform/codegen
|
||||
- $dart2js:
|
||||
commandLineOptions:
|
||||
- --show-package-warnings
|
||||
|
@ -5,26 +5,26 @@
|
||||
*/
|
||||
|
||||
export {Router} from './src/router/router';
|
||||
export {RouterOutlet} from './src/router/router_outlet';
|
||||
export {RouterLink} from './src/router/router_link';
|
||||
export {RouterOutlet} from './src/router/directives/router_outlet';
|
||||
export {RouterLink} from './src/router/directives/router_link';
|
||||
export {RouteParams, RouteData} from './src/router/instruction';
|
||||
export {PlatformLocation} from './src/router/platform_location';
|
||||
export {PlatformLocation} from './src/router/location/platform_location';
|
||||
export {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './src/router/route_registry';
|
||||
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
|
||||
export {HashLocationStrategy} from './src/router/hash_location_strategy';
|
||||
export {PathLocationStrategy} from './src/router/path_location_strategy';
|
||||
export {Location} from './src/router/location';
|
||||
export * from './src/router/route_config_decorator';
|
||||
export {LocationStrategy, APP_BASE_HREF} from './src/router/location/location_strategy';
|
||||
export {HashLocationStrategy} from './src/router/location/hash_location_strategy';
|
||||
export {PathLocationStrategy} from './src/router/location/path_location_strategy';
|
||||
export {Location} from './src/router/location/location';
|
||||
export * from './src/router/route_config/route_config_decorator';
|
||||
export * from './src/router/route_definition';
|
||||
export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/router/interfaces';
|
||||
export {CanActivate} from './src/router/lifecycle_annotations';
|
||||
export {CanActivate} from './src/router/lifecycle/lifecycle_annotations';
|
||||
export {Instruction, ComponentInstruction} from './src/router/instruction';
|
||||
export {OpaqueToken} from 'angular2/core';
|
||||
export {ROUTER_PROVIDERS_COMMON} from 'angular2/src/router/router_providers_common';
|
||||
export {ROUTER_PROVIDERS, ROUTER_BINDINGS} from 'angular2/src/router/router_providers';
|
||||
|
||||
import {RouterOutlet} from './src/router/router_outlet';
|
||||
import {RouterLink} from './src/router/router_link';
|
||||
import {RouterOutlet} from './src/router/directives/router_outlet';
|
||||
import {RouterLink} from './src/router/directives/router_link';
|
||||
import {CONST_EXPR} from './src/facade/lang';
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {TEMPLATE_TRANSFORMS} from 'angular2/compiler';
|
||||
import {Provider} from 'angular2/core';
|
||||
import {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
|
||||
import {RouterLinkTransform} from 'angular2/src/router/directives/router_link_transform';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
export {RouterLinkTransform} from 'angular2/src/router/router_link_transform';
|
||||
export {RouterLinkTransform} from 'angular2/src/router/directives/router_link_transform';
|
||||
|
||||
/**
|
||||
* Enables the router link DSL.
|
||||
|
@ -52,7 +52,7 @@ export class Animation {
|
||||
this.startTime = DateWrapper.toMillis(DateWrapper.now());
|
||||
this._stringPrefix = DOM.getAnimationPrefix();
|
||||
this.setup();
|
||||
this.wait(timestamp => this.start());
|
||||
this.wait((timestamp: any) => this.start());
|
||||
}
|
||||
|
||||
wait(callback: Function) {
|
||||
@ -97,7 +97,7 @@ export class Animation {
|
||||
* @param styles
|
||||
*/
|
||||
applyStyles(styles: {[key: string]: any}): void {
|
||||
StringMapWrapper.forEach(styles, (value, key) => {
|
||||
StringMapWrapper.forEach(styles, (value: any, key: string) => {
|
||||
var dashCaseKey = camelCaseToDashCase(key);
|
||||
if (isPresent(DOM.getStyle(this.element, dashCaseKey))) {
|
||||
DOM.setStyle(this.element, dashCaseKey, value.toString());
|
||||
|
@ -17,7 +17,7 @@ export class BrowserDetails {
|
||||
DOM.setAttribute(div, 'style', `position: absolute; top: -9999px; left: -9999px; width: 1px;
|
||||
height: 1px; transition: all 1ms linear 1ms;`);
|
||||
// Firefox requires that we wait for 2 frames for some reason
|
||||
this.raf(timestamp => {
|
||||
this.raf((timestamp: any) => {
|
||||
DOM.on(div, 'transitionend', (event: any) => {
|
||||
var elapsed = Math.round(event.elapsedTime * 1000);
|
||||
this.elapsedTimeIncludesDelay = elapsed == 2;
|
||||
@ -37,7 +37,8 @@ class RafQueue {
|
||||
currentFrameId: number;
|
||||
constructor(public callback: Function, public frames: number) { this._raf(); }
|
||||
private _raf() {
|
||||
this.currentFrameId = DOM.requestAnimationFrame(timestamp => this._nextFrame(timestamp));
|
||||
this.currentFrameId =
|
||||
DOM.requestAnimationFrame((timestamp: number) => this._nextFrame(timestamp));
|
||||
}
|
||||
private _nextFrame(timestamp: number) {
|
||||
this.frames--;
|
||||
|
@ -9,7 +9,7 @@ import {CORE_DIRECTIVES} from './directives';
|
||||
* NgModel).
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` or `@View` decorators.
|
||||
* property of the `@Component` decorator.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
|
@ -8,5 +8,6 @@ export {NgFor} from './directives/ng_for';
|
||||
export {NgIf} from './directives/ng_if';
|
||||
export {NgStyle} from './directives/ng_style';
|
||||
export {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './directives/ng_switch';
|
||||
export {NgPlural, NgPluralCase, NgLocalization} from './directives/ng_plural';
|
||||
export * from './directives/observable_list_diff';
|
||||
export {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
export {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
|
@ -4,13 +4,14 @@ import {NgFor} from './ng_for';
|
||||
import {NgIf} from './ng_if';
|
||||
import {NgStyle} from './ng_style';
|
||||
import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
|
||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@View` annotation.
|
||||
* property of the `@Component` annotation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
|
||||
*
|
||||
@ -45,5 +46,14 @@ import {NgSwitch, NgSwitchWhen, NgSwitchDefault} from './ng_switch';
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const CORE_DIRECTIVES: Type[] =
|
||||
CONST_EXPR([NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchWhen, NgSwitchDefault]);
|
||||
export const CORE_DIRECTIVES: Type[] = CONST_EXPR([
|
||||
NgClass,
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgStyle,
|
||||
NgSwitch,
|
||||
NgSwitchWhen,
|
||||
NgSwitchDefault,
|
||||
NgPlural,
|
||||
NgPluralCase
|
||||
]);
|
||||
|
@ -1,14 +1,16 @@
|
||||
import {isPresent, isString, StringWrapper, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||
import {isPresent, isString, isArray} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
DoCheck,
|
||||
OnDestroy,
|
||||
Directive,
|
||||
ElementRef,
|
||||
IterableDiffer,
|
||||
IterableDiffers,
|
||||
KeyValueDiffer,
|
||||
KeyValueDiffers,
|
||||
Renderer
|
||||
Renderer,
|
||||
IterableDiffer,
|
||||
KeyValueDiffer,
|
||||
CollectionChangeRecord,
|
||||
KeyValueChangeRecord
|
||||
} from 'angular2/core';
|
||||
import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||
|
||||
@ -73,66 +75,68 @@ import {StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collecti
|
||||
*/
|
||||
@Directive({selector: '[ngClass]', inputs: ['rawClass: ngClass', 'initialClasses: class']})
|
||||
export class NgClass implements DoCheck, OnDestroy {
|
||||
private _differ: any;
|
||||
private _mode: string;
|
||||
private _initialClasses = [];
|
||||
private _rawClass;
|
||||
private _iterableDiffer: IterableDiffer;
|
||||
private _keyValueDiffer: KeyValueDiffer;
|
||||
private _initialClasses: string[] = [];
|
||||
private _rawClass: string[] | Set<string>;
|
||||
|
||||
constructor(private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
set initialClasses(v) {
|
||||
set initialClasses(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
||||
this._applyInitialClasses(false);
|
||||
this._applyClasses(this._rawClass, false);
|
||||
}
|
||||
|
||||
set rawClass(v) {
|
||||
set rawClass(v: string | string[] | Set<string>| {[key: string]: any}) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
|
||||
if (isString(v)) {
|
||||
v = v.split(' ');
|
||||
v = (<string>v).split(' ');
|
||||
}
|
||||
|
||||
this._rawClass = v;
|
||||
this._rawClass = <string[] | Set<string>>v;
|
||||
this._iterableDiffer = null;
|
||||
this._keyValueDiffer = null;
|
||||
if (isPresent(v)) {
|
||||
if (isListLikeIterable(v)) {
|
||||
this._differ = this._iterableDiffers.find(v).create(null);
|
||||
this._mode = 'iterable';
|
||||
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
|
||||
} else {
|
||||
this._differ = this._keyValueDiffers.find(v).create(null);
|
||||
this._mode = 'keyValue';
|
||||
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
|
||||
}
|
||||
} else {
|
||||
this._differ = null;
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._rawClass);
|
||||
if (isPresent(this._iterableDiffer)) {
|
||||
var changes = this._iterableDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
if (this._mode == 'iterable') {
|
||||
this._applyIterableChanges(changes);
|
||||
} else {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
this._applyIterableChanges(changes);
|
||||
}
|
||||
}
|
||||
if (isPresent(this._keyValueDiffer)) {
|
||||
var changes = this._keyValueDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this._cleanupClasses(this._rawClass); }
|
||||
|
||||
private _cleanupClasses(rawClassVal): void {
|
||||
private _cleanupClasses(rawClassVal: string[] | Set<string>| {[key: string]: any}): void {
|
||||
this._applyClasses(rawClassVal, true);
|
||||
this._applyInitialClasses(false);
|
||||
}
|
||||
|
||||
private _applyKeyValueChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record) => {
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
||||
if (record.previousValue) {
|
||||
this._toggleClass(record.key, false);
|
||||
}
|
||||
@ -140,15 +144,17 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
|
||||
}
|
||||
|
||||
private _applyInitialClasses(isCleanup: boolean) {
|
||||
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
|
||||
}
|
||||
|
||||
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: string},
|
||||
private _applyClasses(rawClassVal: string[] | Set<string>| {[key: string]: any},
|
||||
isCleanup: boolean) {
|
||||
if (isPresent(rawClassVal)) {
|
||||
if (isArray(rawClassVal)) {
|
||||
@ -156,14 +162,15 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
} else if (rawClassVal instanceof Set) {
|
||||
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else {
|
||||
StringMapWrapper.forEach(<{[k: string]: string}>rawClassVal, (expVal, className) => {
|
||||
if (expVal) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
StringMapWrapper.forEach(<{[k: string]: any}>rawClassVal,
|
||||
(expVal: any, className: string) => {
|
||||
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleClass(className: string, enabled): void {
|
||||
private _toggleClass(className: string, enabled: boolean): void {
|
||||
className = className.trim();
|
||||
if (className.length > 0) {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
|
@ -10,6 +10,10 @@ import {
|
||||
TrackByFn
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
DefaultIterableDiffer,
|
||||
CollectionChangeRecord
|
||||
} from "../../core/change_detection/differs/default_iterable_differ";
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||
@ -92,19 +96,19 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes) {
|
||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
||||
// TODO(rado): check if change detection can produce a change record that is
|
||||
// easier to consume than current.
|
||||
var recordViewTuples = [];
|
||||
changes.forEachRemovedItem((removedRecord) =>
|
||||
var recordViewTuples: RecordViewTuple[] = [];
|
||||
changes.forEachRemovedItem((removedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(removedRecord, null)));
|
||||
|
||||
changes.forEachMovedItem((movedRecord) =>
|
||||
changes.forEachMovedItem((movedRecord: CollectionChangeRecord) =>
|
||||
recordViewTuples.push(new RecordViewTuple(movedRecord, null)));
|
||||
|
||||
var insertTuples = this._bulkRemove(recordViewTuples);
|
||||
|
||||
changes.forEachAddedItem((addedRecord) =>
|
||||
changes.forEachAddedItem((addedRecord: CollectionChangeRecord) =>
|
||||
insertTuples.push(new RecordViewTuple(addedRecord, null)));
|
||||
|
||||
this._bulkInsert(insertTuples);
|
||||
@ -124,7 +128,7 @@ export class NgFor implements DoCheck {
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view, record) {
|
||||
private _perViewChange(view: EmbeddedViewRef, record: CollectionChangeRecord) {
|
||||
view.setLocal('\$implicit', record.item);
|
||||
view.setLocal('index', record.currentIndex);
|
||||
view.setLocal('even', (record.currentIndex % 2 == 0));
|
||||
@ -132,8 +136,9 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
|
||||
tuples.sort((a, b) => a.record.previousIndex - b.record.previousIndex);
|
||||
var movedTuples = [];
|
||||
tuples.sort((a: RecordViewTuple, b: RecordViewTuple) =>
|
||||
a.record.previousIndex - b.record.previousIndex);
|
||||
var movedTuples: RecordViewTuple[] = [];
|
||||
for (var i = tuples.length - 1; i >= 0; i--) {
|
||||
var tuple = tuples[i];
|
||||
// separate moved views from removed views.
|
||||
@ -165,7 +170,7 @@ export class NgFor implements DoCheck {
|
||||
class RecordViewTuple {
|
||||
view: EmbeddedViewRef;
|
||||
record: any;
|
||||
constructor(record, view) {
|
||||
constructor(record: any, view: EmbeddedViewRef) {
|
||||
this.record = record;
|
||||
this.view = view;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class NgIf {
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
|
||||
|
||||
set ngIf(newCondition /* boolean */) {
|
||||
set ngIf(newCondition: any /* boolean */) {
|
||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
||||
this._prevCondition = true;
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
||||
|
146
modules/angular2/src/common/directives/ng_plural.ts
Normal file
146
modules/angular2/src/common/directives/ng_plural.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import {
|
||||
Directive,
|
||||
ViewContainerRef,
|
||||
TemplateRef,
|
||||
ContentChildren,
|
||||
QueryList,
|
||||
Attribute,
|
||||
AfterContentInit,
|
||||
Input
|
||||
} from 'angular2/core';
|
||||
import {isPresent, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
const _CATEGORY_DEFAULT = 'other';
|
||||
|
||||
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;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
_view: SwitchView;
|
||||
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef,
|
||||
viewContainer: ViewContainerRef) {
|
||||
this._view = new SwitchView(viewContainer, template);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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); }
|
||||
}
|
@ -7,6 +7,7 @@ import {
|
||||
Renderer
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||
import {KeyValueChangeRecord} from "../../core/change_detection/differs/default_keyvalue_differ";
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
@ -62,14 +63,14 @@ import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||
@Directive({selector: '[ngStyle]', inputs: ['rawStyle: ngStyle']})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_rawStyle;
|
||||
_rawStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
|
||||
constructor(private _differs: KeyValueDiffers, private _ngEl: ElementRef,
|
||||
private _renderer: Renderer) {}
|
||||
|
||||
set rawStyle(v) {
|
||||
set rawStyle(v: {[key: string]: string}) {
|
||||
this._rawStyle = v;
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._rawStyle).create(null);
|
||||
@ -86,9 +87,12 @@ export class NgStyle implements DoCheck {
|
||||
}
|
||||
|
||||
private _applyChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
|
@ -4,7 +4,6 @@ import {ListWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
const _WHEN_DEFAULT = CONST_EXPR(new Object());
|
||||
|
||||
/** @internal */
|
||||
export class SwitchView {
|
||||
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
|
||||
|
||||
@ -32,8 +31,8 @@ export class SwitchView {
|
||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({selector: 'app'})
|
||||
* @View({
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
@ -76,7 +75,7 @@ export class NgSwitch {
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
|
||||
set ngSwitch(value) {
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
@ -93,7 +92,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onWhenValueChanged(oldWhen, newWhen, view: SwitchView): void {
|
||||
_onWhenValueChanged(oldWhen: any, newWhen: any, view: SwitchView): void {
|
||||
this._deregisterView(oldWhen, view);
|
||||
this._registerView(newWhen, view);
|
||||
|
||||
@ -137,7 +136,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value, view: SwitchView): void {
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
views = [];
|
||||
@ -147,7 +146,7 @@ export class NgSwitch {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value, view: SwitchView): void {
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
// `_WHEN_DEFAULT` is used a marker for non-registered whens
|
||||
if (value === _WHEN_DEFAULT) return;
|
||||
var views = this._valueViews.get(value);
|
||||
@ -182,7 +181,7 @@ export class NgSwitchWhen {
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
set ngSwitchWhen(value) {
|
||||
set ngSwitchWhen(value: any) {
|
||||
this._switch._onWhenValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ export {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator,
|
||||
Validator
|
||||
} from './forms/directives/validators';
|
||||
export {FormBuilder} from './forms/form_builder';
|
||||
|
@ -14,7 +14,12 @@ import {
|
||||
SelectControlValueAccessor,
|
||||
NgSelectOption
|
||||
} from './directives/select_control_value_accessor';
|
||||
import {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
|
||||
import {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
} from './directives/validators';
|
||||
|
||||
export {NgControlName} from './directives/ng_control_name';
|
||||
export {NgFormControl} from './directives/ng_form_control';
|
||||
@ -34,13 +39,18 @@ export {
|
||||
SelectControlValueAccessor,
|
||||
NgSelectOption
|
||||
} from './directives/select_control_value_accessor';
|
||||
export {RequiredValidator, MinLengthValidator, MaxLengthValidator} from './directives/validators';
|
||||
export {
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
} from './directives/validators';
|
||||
export {NgControl} from './directives/ng_control';
|
||||
export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||
|
||||
/**
|
||||
*
|
||||
* A list of all the form directives used as part of a `@View` annotation.
|
||||
* A list of all the form directives used as part of a `@Component` annotation.
|
||||
*
|
||||
* This is a shorthand for importing them each individually.
|
||||
*
|
||||
@ -73,5 +83,6 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
|
||||
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator
|
||||
MaxLengthValidator,
|
||||
PatternValidator
|
||||
]);
|
||||
|
@ -21,7 +21,7 @@ const CHECKBOX_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
providers: [CHECKBOX_VALUE_ACCESSOR]
|
||||
})
|
||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
@ -24,7 +24,7 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
bindings: [DEFAULT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
/**
|
||||
* A base class that all control directive extend.
|
||||
@ -12,8 +13,8 @@ export abstract class NgControl extends AbstractControlDirective {
|
||||
name: string = null;
|
||||
valueAccessor: ControlValueAccessor = null;
|
||||
|
||||
get validator(): Function { return unimplemented(); }
|
||||
get asyncValidator(): Function { return unimplemented(); }
|
||||
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
|
||||
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
|
||||
|
||||
abstract viewToModelUpdate(newValue: any): void;
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ import {ControlContainer} from './control_container';
|
||||
import {controlPath, composeValidators, composeAsyncValidators} from './shared';
|
||||
import {ControlGroup} from '../model';
|
||||
import {Form} from './form_interface';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
const controlGroupProvider =
|
||||
CONST_EXPR(new Provider(ControlContainer, {useExisting: forwardRef(() => NgControlGroup)}));
|
||||
@ -32,8 +33,6 @@ const controlGroupProvider =
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>Angular2 Control & ControlGroup Example</h2>
|
||||
@ -106,7 +105,7 @@ export class NgControlGroup extends ControlContainer implements OnInit,
|
||||
*/
|
||||
get formDirective(): Form { return this._parent.formDirective; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ import {
|
||||
selectValueAccessor
|
||||
} from './shared';
|
||||
import {Control} from '../model';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
|
||||
const controlNameBinding =
|
||||
@ -136,9 +137,9 @@ export class NgControlName extends NgControl implements OnChanges,
|
||||
|
||||
get formDirective(): any { return this._parent.formDirective; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
get control(): Control { return this.formDirective.getControl(this); }
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
isPropertyUpdated,
|
||||
selectValueAccessor
|
||||
} from './shared';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
const formControlBinding =
|
||||
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgFormControl)}));
|
||||
@ -110,9 +111,9 @@ export class NgFormControl extends NgControl implements OnChanges {
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
get control(): Control { return this.form; }
|
||||
|
||||
|
@ -3,7 +3,6 @@ import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
Query,
|
||||
Directive,
|
||||
forwardRef,
|
||||
Provider,
|
||||
@ -14,7 +13,7 @@ import {
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {Control} from '../model';
|
||||
import {Validators, NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {NG_VALIDATORS, NG_ASYNC_VALIDATORS} from '../validators';
|
||||
import {
|
||||
setUpControl,
|
||||
isPropertyUpdated,
|
||||
@ -22,6 +21,7 @@ import {
|
||||
composeValidators,
|
||||
composeAsyncValidators
|
||||
} from './shared';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
const formControlBinding =
|
||||
CONST_EXPR(new Provider(NgControl, {useExisting: forwardRef(() => NgModel)}));
|
||||
@ -88,9 +88,9 @@ export class NgModel extends NgControl implements OnChanges {
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): Function { return composeValidators(this._validators); }
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): Function { return composeAsyncValidators(this._asyncValidators); }
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
|
@ -10,3 +10,11 @@ Function normalizeValidator(dynamic validator){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Function normalizeAsyncValidator(dynamic validator){
|
||||
if (validator is Validator) {
|
||||
return (c) => validator.validate(c);
|
||||
} else {
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
import {Validator} from './validators';
|
||||
import {AbstractControl} from "../model";
|
||||
import {Validator, ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
export function normalizeValidator(validator: Function | Validator): Function {
|
||||
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c) => (<Validator>validator).validate(c);
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <Function>validator;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
||||
bindings: [NUMBER_VALUE_ACCESSOR]
|
||||
})
|
||||
export class NumberValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
@ -41,7 +41,7 @@ export class NgSelectOption {
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: string;
|
||||
onChange = (_) => {};
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef,
|
||||
|
@ -14,7 +14,8 @@ import {NumberValueAccessor} from './number_value_accessor';
|
||||
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
||||
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
||||
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
||||
import {normalizeValidator} from './normalize_validator';
|
||||
import {normalizeValidator, normalizeAsyncValidator} from './normalize_validator';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './validators';
|
||||
|
||||
|
||||
export function controlPath(name: string, parent: ControlContainer): string[] {
|
||||
@ -32,14 +33,14 @@ export function setUpControl(control: Control, dir: NgControl): void {
|
||||
dir.valueAccessor.writeValue(control.value);
|
||||
|
||||
// view -> model
|
||||
dir.valueAccessor.registerOnChange(newValue => {
|
||||
dir.valueAccessor.registerOnChange((newValue: any) => {
|
||||
dir.viewToModelUpdate(newValue);
|
||||
control.updateValue(newValue, {emitModelToViewChange: false});
|
||||
control.markAsDirty();
|
||||
});
|
||||
|
||||
// model -> view
|
||||
control.registerOnChange(newValue => dir.valueAccessor.writeValue(newValue));
|
||||
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
|
||||
|
||||
// touched
|
||||
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
|
||||
@ -56,13 +57,14 @@ function _throwError(dir: AbstractControlDirective, message: string): void {
|
||||
throw new BaseException(`${message} '${path}'`);
|
||||
}
|
||||
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): Function {
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
|
||||
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
|
||||
}
|
||||
|
||||
export function composeAsyncValidators(
|
||||
validators: /* Array<Validator|Function> */ any[]): Function {
|
||||
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeValidator)) : null;
|
||||
validators: /* Array<Validator|Function> */ any[]): AsyncValidatorFn {
|
||||
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
|
||||
null;
|
||||
}
|
||||
|
||||
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
|
||||
@ -78,10 +80,10 @@ export function selectValueAccessor(dir: NgControl,
|
||||
valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
|
||||
if (isBlank(valueAccessors)) return null;
|
||||
|
||||
var defaultAccessor;
|
||||
var builtinAccessor;
|
||||
var customAccessor;
|
||||
valueAccessors.forEach(v => {
|
||||
var defaultAccessor: ControlValueAccessor;
|
||||
var builtinAccessor: ControlValueAccessor;
|
||||
var customAccessor: ControlValueAccessor;
|
||||
valueAccessors.forEach((v: ControlValueAccessor) => {
|
||||
if (hasConstructor(v, DefaultValueAccessor)) {
|
||||
defaultAccessor = v;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import {forwardRef, Provider, OpaqueToken, Attribute, Directive} from 'angular2/core';
|
||||
import {forwardRef, Provider, Attribute, Directive} from 'angular2/core';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Validators, NG_VALIDATORS} from '../validators';
|
||||
import {Control} from '../model';
|
||||
import {AbstractControl} from '../model';
|
||||
import * as modelModule from '../model';
|
||||
import {NumberWrapper} from "angular2/src/facade/lang";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface that can be implemented by classes that can act as validators.
|
||||
*
|
||||
@ -23,7 +24,7 @@ import {NumberWrapper} from "angular2/src/facade/lang";
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface Validator { validate(c: modelModule.Control): {[key: string]: any}; }
|
||||
export interface Validator { validate(c: modelModule.AbstractControl): {[key: string]: any}; }
|
||||
|
||||
const REQUIRED_VALIDATOR =
|
||||
CONST_EXPR(new Provider(NG_VALIDATORS, {useValue: Validators.required, multi: true}));
|
||||
@ -45,6 +46,11 @@ const REQUIRED_VALIDATOR =
|
||||
export class RequiredValidator {
|
||||
}
|
||||
|
||||
export interface ValidatorFn { (c: AbstractControl): {[key: string]: any}; }
|
||||
export interface AsyncValidatorFn {
|
||||
(c: AbstractControl): any /*Promise<{[key: string]: any}>|Observable<{[key: string]: any}>*/;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provivder which adds {@link MinLengthValidator} to {@link NG_VALIDATORS}.
|
||||
*
|
||||
@ -64,13 +70,13 @@ const MIN_LENGTH_VALIDATOR = CONST_EXPR(
|
||||
providers: [MIN_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MinLengthValidator implements Validator {
|
||||
private _validator: Function;
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("minlength") minLength: string) {
|
||||
this._validator = Validators.minLength(NumberWrapper.parseInt(minLength, 10));
|
||||
}
|
||||
|
||||
validate(c: Control): {[key: string]: any} { return this._validator(c); }
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,11 +98,40 @@ const MAX_LENGTH_VALIDATOR = CONST_EXPR(
|
||||
providers: [MAX_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MaxLengthValidator implements Validator {
|
||||
private _validator: Function;
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("maxlength") maxLength: string) {
|
||||
this._validator = Validators.maxLength(NumberWrapper.parseInt(maxLength, 10));
|
||||
}
|
||||
|
||||
validate(c: Control): {[key: string]: any} { return this._validator(c); }
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A Directive that adds the `pattern` validator to any controls marked with the
|
||||
* `pattern` attribute, via the {@link NG_VALIDATORS} binding. Uses attribute value
|
||||
* as the regex to validate Control value against. Follows pattern attribute
|
||||
* semantics; i.e. regex must match entire Control value.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input [ngControl]="fullName" pattern="[a-zA-Z ]*">
|
||||
* ```
|
||||
*/
|
||||
const PATTERN_VALIDATOR = CONST_EXPR(
|
||||
new Provider(NG_VALIDATORS, {useExisting: forwardRef(() => PatternValidator), multi: true}));
|
||||
@Directive({
|
||||
selector: '[pattern][ngControl],[pattern][ngFormControl],[pattern][ngModel]',
|
||||
providers: [PATTERN_VALIDATOR]
|
||||
})
|
||||
export class PatternValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute("pattern") pattern: string) {
|
||||
this._validator = Validators.pattern(pattern);
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {Injectable} from 'angular2/core';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isArray, CONST_EXPR, Type} from 'angular2/src/facade/lang';
|
||||
import * as modelModule from './model';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
|
||||
/**
|
||||
@ -56,16 +57,18 @@ export class FormBuilder {
|
||||
group(controlsConfig: {[key: string]: any},
|
||||
extra: {[key: string]: any} = null): modelModule.ControlGroup {
|
||||
var controls = this._reduceControls(controlsConfig);
|
||||
var optionals = isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null;
|
||||
var validator = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
|
||||
var asyncValidator = isPresent(extra) ? StringMapWrapper.get(extra, "asyncValidator") : null;
|
||||
var optionals = <{[key: string]: boolean}>(
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, "optionals") : null);
|
||||
var validator: ValidatorFn = isPresent(extra) ? StringMapWrapper.get(extra, "validator") : null;
|
||||
var asyncValidator: AsyncValidatorFn =
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, "asyncValidator") : null;
|
||||
return new modelModule.ControlGroup(controls, optionals, validator, asyncValidator);
|
||||
}
|
||||
/**
|
||||
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
|
||||
*/
|
||||
control(value: Object, validator: Function = null,
|
||||
asyncValidator: Function = null): modelModule.Control {
|
||||
control(value: Object, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): modelModule.Control {
|
||||
return new modelModule.Control(value, validator, asyncValidator);
|
||||
}
|
||||
|
||||
@ -73,16 +76,17 @@ export class FormBuilder {
|
||||
* Construct an array of {@link Control}s from the given `controlsConfig` array of
|
||||
* configuration, with the given optional `validator` and `asyncValidator`.
|
||||
*/
|
||||
array(controlsConfig: any[], validator: Function = null,
|
||||
asyncValidator: Function = null): modelModule.ControlArray {
|
||||
array(controlsConfig: any[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): modelModule.ControlArray {
|
||||
var controls = controlsConfig.map(c => this._createControl(c));
|
||||
return new modelModule.ControlArray(controls, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceControls(controlsConfig: any): {[key: string]: modelModule.AbstractControl} {
|
||||
_reduceControls(controlsConfig: {[k: string]:
|
||||
any}): {[key: string]: modelModule.AbstractControl} {
|
||||
var controls: {[key: string]: modelModule.AbstractControl} = {};
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig, controlName) => {
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
|
||||
controls[controlName] = this._createControl(controlConfig);
|
||||
});
|
||||
return controls;
|
||||
@ -97,12 +101,12 @@ export class FormBuilder {
|
||||
|
||||
} else if (isArray(controlConfig)) {
|
||||
var value = controlConfig[0];
|
||||
var validator = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||
var asyncValidator = controlConfig.length > 2 ? controlConfig[2] : null;
|
||||
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
|
||||
return this.control(value, validator, asyncValidator);
|
||||
|
||||
} else {
|
||||
return this.control(controlConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import {StringWrapper, isPresent, isBlank, normalizeBool} from 'angular2/src/facade/lang';
|
||||
import {isPresent, isBlank, normalizeBool} from 'angular2/src/facade/lang';
|
||||
import {Observable, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
/**
|
||||
* Indicates that a Control is valid, i.e. that no errors exist in the input value.
|
||||
@ -62,9 +63,9 @@ export abstract class AbstractControl {
|
||||
private _pristine: boolean = true;
|
||||
private _touched: boolean = false;
|
||||
private _parent: ControlGroup | ControlArray;
|
||||
private _asyncValidationSubscription;
|
||||
private _asyncValidationSubscription: any;
|
||||
|
||||
constructor(public validator: Function, public asyncValidator: Function) {}
|
||||
constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}
|
||||
|
||||
get value(): any { return this._value; }
|
||||
|
||||
@ -137,15 +138,17 @@ export abstract class AbstractControl {
|
||||
}
|
||||
}
|
||||
|
||||
private _runValidator() { return isPresent(this.validator) ? this.validator(this) : null; }
|
||||
private _runValidator(): {[key: string]: any} {
|
||||
return isPresent(this.validator) ? this.validator(this) : null;
|
||||
}
|
||||
|
||||
private _runAsyncValidator(emitEvent: boolean): void {
|
||||
if (isPresent(this.asyncValidator)) {
|
||||
this._status = PENDING;
|
||||
this._cancelExistingSubscription();
|
||||
var obs = toObservable(this.asyncValidator(this));
|
||||
this._asyncValidationSubscription =
|
||||
ObservableWrapper.subscribe(obs, res => this.setErrors(res, {emitEvent: emitEvent}));
|
||||
this._asyncValidationSubscription = ObservableWrapper.subscribe(
|
||||
obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +271,8 @@ export class Control extends AbstractControl {
|
||||
/** @internal */
|
||||
_onChange: Function;
|
||||
|
||||
constructor(value: any = null, validator: Function = null, asyncValidator: Function = null) {
|
||||
constructor(value: any = null, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._value = value;
|
||||
this.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
||||
@ -331,8 +335,8 @@ export class ControlGroup extends AbstractControl {
|
||||
private _optionals: {[key: string]: boolean};
|
||||
|
||||
constructor(public controls: {[key: string]: AbstractControl},
|
||||
optionals: {[key: string]: boolean} = null, validator: Function = null,
|
||||
asyncValidator: Function = null) {
|
||||
optionals: {[key: string]: boolean} = null, validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._optionals = isPresent(optionals) ? optionals : {};
|
||||
this._initObservables();
|
||||
@ -379,7 +383,8 @@ export class ControlGroup extends AbstractControl {
|
||||
|
||||
/** @internal */
|
||||
_setParentForControls() {
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => { control.setParent(this); });
|
||||
StringMapWrapper.forEach(
|
||||
this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -388,7 +393,7 @@ export class ControlGroup extends AbstractControl {
|
||||
/** @internal */
|
||||
_anyControlsHaveStatus(status: string): boolean {
|
||||
var res = false;
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
|
||||
res = res || (this.contains(name) && control.status == status);
|
||||
});
|
||||
return res;
|
||||
@ -396,16 +401,17 @@ export class ControlGroup extends AbstractControl {
|
||||
|
||||
/** @internal */
|
||||
_reduceValue() {
|
||||
return this._reduceChildren({}, (acc, control, name) => {
|
||||
acc[name] = control.value;
|
||||
return acc;
|
||||
});
|
||||
return this._reduceChildren(
|
||||
{}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
|
||||
acc[name] = control.value;
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceChildren(initValue: any, fn: Function) {
|
||||
var res = initValue;
|
||||
StringMapWrapper.forEach(this.controls, (control, name) => {
|
||||
StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
|
||||
if (this._included(name)) {
|
||||
res = fn(res, control, name);
|
||||
}
|
||||
@ -442,8 +448,8 @@ export class ControlGroup extends AbstractControl {
|
||||
* ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
|
||||
*/
|
||||
export class ControlArray extends AbstractControl {
|
||||
constructor(public controls: AbstractControl[], validator: Function = null,
|
||||
asyncValidator: Function = null) {
|
||||
constructor(public controls: AbstractControl[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null) {
|
||||
super(validator, asyncValidator);
|
||||
this._initObservables();
|
||||
this._setParentForControls();
|
||||
|
@ -5,6 +5,7 @@ import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {OpaqueToken} from 'angular2/core';
|
||||
|
||||
import * as modelModule from './model';
|
||||
import {ValidatorFn, AsyncValidatorFn} from './directives/validators';
|
||||
|
||||
/**
|
||||
* Providers for validators to be used for {@link Control}s in a form.
|
||||
@ -43,7 +44,7 @@ export class Validators {
|
||||
/**
|
||||
* Validator that requires controls to have a non-empty value.
|
||||
*/
|
||||
static required(control: modelModule.Control): {[key: string]: boolean} {
|
||||
static required(control: modelModule.AbstractControl): {[key: string]: boolean} {
|
||||
return isBlank(control.value) || (isString(control.value) && control.value == "") ?
|
||||
{"required": true} :
|
||||
null;
|
||||
@ -52,8 +53,8 @@ export class Validators {
|
||||
/**
|
||||
* Validator that requires controls to have a value of a minimum length.
|
||||
*/
|
||||
static minLength(minLength: number): Function {
|
||||
return (control: modelModule.Control): {[key: string]: any} => {
|
||||
static minLength(minLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length < minLength ?
|
||||
@ -65,8 +66,8 @@ export class Validators {
|
||||
/**
|
||||
* Validator that requires controls to have a value of a maximum length.
|
||||
*/
|
||||
static maxLength(maxLength: number): Function {
|
||||
return (control: modelModule.Control): {[key: string]: any} => {
|
||||
static maxLength(maxLength: number): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
var v: string = control.value;
|
||||
return v.length > maxLength ?
|
||||
@ -75,16 +76,29 @@ export class Validators {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that requires a control to match a regex to its value.
|
||||
*/
|
||||
static pattern(pattern: string): ValidatorFn {
|
||||
return (control: modelModule.AbstractControl): {[key: string]: any} => {
|
||||
if (isPresent(Validators.required(control))) return null;
|
||||
let regex = new RegExp(`^${pattern}$`);
|
||||
let v: string = control.value;
|
||||
return regex.test(v) ? null :
|
||||
{"pattern": {"requiredPattern": `^${pattern}$`, "actualValue": v}};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op validator.
|
||||
*/
|
||||
static nullValidator(c: any): {[key: string]: boolean} { return null; }
|
||||
static nullValidator(c: modelModule.AbstractControl): {[key: string]: boolean} { return null; }
|
||||
|
||||
/**
|
||||
* Compose multiple validators into a single function that returns the union
|
||||
* of the individual error maps.
|
||||
*/
|
||||
static compose(validators: Function[]): Function {
|
||||
static compose(validators: ValidatorFn[]): ValidatorFn {
|
||||
if (isBlank(validators)) return null;
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
@ -94,13 +108,13 @@ export class Validators {
|
||||
};
|
||||
}
|
||||
|
||||
static composeAsync(validators: Function[]): Function {
|
||||
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn {
|
||||
if (isBlank(validators)) return null;
|
||||
var presentValidators = validators.filter(isPresent);
|
||||
if (presentValidators.length == 0) return null;
|
||||
|
||||
return function(control: modelModule.AbstractControl) {
|
||||
let promises = _executeValidators(control, presentValidators).map(_convertToPromise);
|
||||
let promises = _executeAsyncValidators(control, presentValidators).map(_convertToPromise);
|
||||
return PromiseWrapper.all(promises).then(_mergeErrors);
|
||||
};
|
||||
}
|
||||
@ -110,13 +124,20 @@ function _convertToPromise(obj: any): any {
|
||||
return PromiseWrapper.isPromise(obj) ? obj : ObservableWrapper.toPromise(obj);
|
||||
}
|
||||
|
||||
function _executeValidators(control: modelModule.AbstractControl, validators: Function[]): any[] {
|
||||
function _executeValidators(control: modelModule.AbstractControl,
|
||||
validators: ValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
function _executeAsyncValidators(control: modelModule.AbstractControl,
|
||||
validators: AsyncValidatorFn[]): any[] {
|
||||
return validators.map(v => v(control));
|
||||
}
|
||||
|
||||
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
|
||||
var res = arrayOfErrors.reduce((res, errors) => {
|
||||
return isPresent(errors) ? StringMapWrapper.merge(<any>res, <any>errors) : res;
|
||||
}, {});
|
||||
var res: {[key: string]: any} =
|
||||
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
|
||||
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res;
|
||||
}, {});
|
||||
return StringMapWrapper.isEmpty(res) ? null : res;
|
||||
}
|
||||
|
@ -3,14 +3,6 @@
|
||||
* @description
|
||||
* This module provides a set of common Pipes.
|
||||
*/
|
||||
import {AsyncPipe} from './pipes/async_pipe';
|
||||
import {UpperCasePipe} from './pipes/uppercase_pipe';
|
||||
import {LowerCasePipe} from './pipes/lowercase_pipe';
|
||||
import {JsonPipe} from './pipes/json_pipe';
|
||||
import {SlicePipe} from './pipes/slice_pipe';
|
||||
import {DatePipe} from './pipes/date_pipe';
|
||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
export {AsyncPipe} from './pipes/async_pipe';
|
||||
export {DatePipe} from './pipes/date_pipe';
|
||||
@ -19,22 +11,7 @@ export {SlicePipe} from './pipes/slice_pipe';
|
||||
export {LowerCasePipe} from './pipes/lowercase_pipe';
|
||||
export {NumberPipe, DecimalPipe, PercentPipe, CurrencyPipe} from './pipes/number_pipe';
|
||||
export {UpperCasePipe} from './pipes/uppercase_pipe';
|
||||
|
||||
/**
|
||||
* A collection of Angular core pipes that are likely to be used in each and every
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
|
||||
* property of the `@Component` or `@View` decorators.
|
||||
*/
|
||||
export const COMMON_PIPES = CONST_EXPR([
|
||||
AsyncPipe,
|
||||
UpperCasePipe,
|
||||
LowerCasePipe,
|
||||
JsonPipe,
|
||||
SlicePipe,
|
||||
DecimalPipe,
|
||||
PercentPipe,
|
||||
CurrencyPipe,
|
||||
DatePipe
|
||||
]);
|
||||
export {ReplacePipe} from './pipes/replace_pipe';
|
||||
export {I18nPluralPipe} from './pipes/i18n_plural_pipe';
|
||||
export {I18nSelectPipe} from './pipes/i18n_select_pipe';
|
||||
export {COMMON_PIPES} from './pipes/common_pipes';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {isBlank, isPresent, isPromise, CONST} from 'angular2/src/facade/lang';
|
||||
import {Promise, ObservableWrapper, Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
import {ObservableWrapper, Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
import {
|
||||
Pipe,
|
||||
Injectable,
|
||||
@ -22,7 +22,7 @@ class ObservableStrategy {
|
||||
}
|
||||
|
||||
class PromiseStrategy {
|
||||
createSubscription(async: any, updateLatestValue: any): any {
|
||||
createSubscription(async: Promise<any>, updateLatestValue: (v: any) => any): any {
|
||||
return async.then(updateLatestValue);
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ class PromiseStrategy {
|
||||
|
||||
var _promiseStrategy = new PromiseStrategy();
|
||||
var _observableStrategy = new ObservableStrategy();
|
||||
|
||||
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
|
||||
|
||||
/**
|
||||
* The `async` pipe subscribes to an Observable or Promise and returns the latest value it has
|
||||
@ -102,8 +102,8 @@ export class AsyncPipe implements PipeTransform, OnDestroy {
|
||||
_subscribe(obj: Observable<any>| Promise<any>| EventEmitter<any>): void {
|
||||
this._obj = obj;
|
||||
this._strategy = this._selectStrategy(obj);
|
||||
this._subscription =
|
||||
this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value));
|
||||
this._subscription = this._strategy.createSubscription(
|
||||
obj, (value: Object) => this._updateLatestValue(obj, value));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -10,6 +10,9 @@ import {JsonPipe} from './json_pipe';
|
||||
import {SlicePipe} from './slice_pipe';
|
||||
import {DatePipe} from './date_pipe';
|
||||
import {DecimalPipe, PercentPipe, CurrencyPipe} from './number_pipe';
|
||||
import {ReplacePipe} from './replace_pipe';
|
||||
import {I18nPluralPipe} from './i18n_plural_pipe';
|
||||
import {I18nSelectPipe} from './i18n_select_pipe';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
@ -17,7 +20,7 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in pipes in the `pipes`
|
||||
* property of the `@Component` or `@View` decorators.
|
||||
* property of the `@Component` decorator.
|
||||
*/
|
||||
export const COMMON_PIPES = CONST_EXPR([
|
||||
AsyncPipe,
|
||||
@ -28,5 +31,8 @@ export const COMMON_PIPES = CONST_EXPR([
|
||||
DecimalPipe,
|
||||
PercentPipe,
|
||||
CurrencyPipe,
|
||||
DatePipe
|
||||
DatePipe,
|
||||
ReplacePipe,
|
||||
I18nPluralPipe,
|
||||
I18nSelectPipe
|
||||
]);
|
||||
|
62
modules/angular2/src/common/pipes/i18n_plural_pipe.ts
Normal file
62
modules/angular2/src/common/pipes/i18n_plural_pipe.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import {
|
||||
CONST,
|
||||
isStringMap,
|
||||
StringWrapper,
|
||||
isPresent,
|
||||
RegExpWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
var interpolationExp: RegExp = RegExpWrapper.create('#');
|
||||
|
||||
/**
|
||||
*
|
||||
* Maps a value to a string that pluralizes the value properly.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nPlural:mapping
|
||||
*
|
||||
* where `expression` is a number and `mapping` is an object that indicates the proper text for
|
||||
* when the `expression` evaluates to 0, 1, or some other number. You can interpolate the actual
|
||||
* value into the text using the `#` sign.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ messages.length | i18nPlural: messageMapping }}
|
||||
* </div>
|
||||
*
|
||||
* class MyApp {
|
||||
* messages: any[];
|
||||
* messageMapping: any = {
|
||||
* '=0': 'No messages.',
|
||||
* '=1': 'One message.',
|
||||
* 'other': '# messages.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'i18nPlural', pure: true})
|
||||
@Injectable()
|
||||
export class I18nPluralPipe implements PipeTransform {
|
||||
transform(value: number, args: any[] = null): string {
|
||||
var key: string;
|
||||
var valueStr: string;
|
||||
var pluralMap: {[count: string]: string} = <{[count: string]: string}>(args[0]);
|
||||
|
||||
if (!isStringMap(pluralMap)) {
|
||||
throw new InvalidPipeArgumentException(I18nPluralPipe, pluralMap);
|
||||
}
|
||||
|
||||
key = value === 0 || value === 1 ? `=${value}` : 'other';
|
||||
valueStr = isPresent(value) ? value.toString() : '';
|
||||
|
||||
return StringWrapper.replaceAll(pluralMap[key], interpolationExp, valueStr);
|
||||
}
|
||||
}
|
47
modules/angular2/src/common/pipes/i18n_select_pipe.ts
Normal file
47
modules/angular2/src/common/pipes/i18n_select_pipe.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {CONST, isStringMap} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic selector that displays the string that matches the current value.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* expression | i18nSelect:mapping
|
||||
*
|
||||
* where `mapping` is an object that indicates the text that should be displayed
|
||||
* for different values of the provided `expression`.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* <div>
|
||||
* {{ gender | i18nSelect: inviteMap }}
|
||||
* </div>
|
||||
*
|
||||
* class MyApp {
|
||||
* gender: string = 'male';
|
||||
* inviteMap: any = {
|
||||
* 'male': 'Invite her.',
|
||||
* 'female': 'Invite him.',
|
||||
* 'other': 'Invite them.'
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@CONST()
|
||||
@Pipe({name: 'i18nSelect', pure: true})
|
||||
@Injectable()
|
||||
export class I18nSelectPipe implements PipeTransform {
|
||||
transform(value: string, args: any[] = null): string {
|
||||
var mapping: {[key: string]: string} = <{[count: string]: string}>(args[0]);
|
||||
if (!isStringMap(mapping)) {
|
||||
throw new InvalidPipeArgumentException(I18nSelectPipe, mapping);
|
||||
}
|
||||
|
||||
return StringMapWrapper.contains(mapping, value) ? mapping[value] : mapping['other'];
|
||||
}
|
||||
}
|
91
modules/angular2/src/common/pipes/replace_pipe.ts
Normal file
91
modules/angular2/src/common/pipes/replace_pipe.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import {
|
||||
isBlank,
|
||||
isString,
|
||||
isNumber,
|
||||
isFunction,
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Injectable, PipeTransform, Pipe} from 'angular2/core';
|
||||
import {InvalidPipeArgumentException} from './invalid_pipe_argument_exception';
|
||||
|
||||
/**
|
||||
* Creates a new String with some or all of the matches of a pattern replaced by
|
||||
* a replacement.
|
||||
*
|
||||
* The pattern to be matched is specified by the 'pattern' parameter.
|
||||
*
|
||||
* The replacement to be set is specified by the 'replacement' parameter.
|
||||
*
|
||||
* An optional 'flags' parameter can be set.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* expression | replace:pattern:replacement
|
||||
*
|
||||
* All behavior is based on the expected behavior of the JavaScript API
|
||||
* String.prototype.replace() function.
|
||||
*
|
||||
* Where the input expression is a [String] or [Number] (to be treated as a string),
|
||||
* the `pattern` is a [String] or [RegExp],
|
||||
* the 'replacement' is a [String] or [Function].
|
||||
*
|
||||
* --Note--: The 'pattern' parameter will be converted to a RegExp instance. Make sure to escape the
|
||||
* string properly if you are matching for regular expression special characters like parenthesis,
|
||||
* brackets etc.
|
||||
*/
|
||||
|
||||
@Pipe({name: 'replace'})
|
||||
@Injectable()
|
||||
export class ReplacePipe implements PipeTransform {
|
||||
transform(value: any, args: any[]): any {
|
||||
if (isBlank(args) || args.length !== 2) {
|
||||
throw new BaseException('ReplacePipe requires two arguments');
|
||||
}
|
||||
|
||||
if (isBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!this._supportedInput(value)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, value);
|
||||
}
|
||||
|
||||
var input = value.toString();
|
||||
var pattern = args[0];
|
||||
var replacement = args[1];
|
||||
|
||||
|
||||
if (!this._supportedPattern(pattern)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, pattern);
|
||||
}
|
||||
if (!this._supportedReplacement(replacement)) {
|
||||
throw new InvalidPipeArgumentException(ReplacePipe, replacement);
|
||||
}
|
||||
// template fails with literal RegExp e.g /pattern/igm
|
||||
// var rgx = pattern instanceof RegExp ? pattern : RegExpWrapper.create(pattern);
|
||||
|
||||
if (isFunction(replacement)) {
|
||||
var rgxPattern = isString(pattern) ? RegExpWrapper.create(pattern) : pattern;
|
||||
|
||||
return StringWrapper.replaceAllMapped(input, rgxPattern, replacement);
|
||||
}
|
||||
if (pattern instanceof RegExp) {
|
||||
// use the replaceAll variant
|
||||
return StringWrapper.replaceAll(input, pattern, replacement);
|
||||
}
|
||||
|
||||
return StringWrapper.replace(input, pattern, replacement);
|
||||
}
|
||||
|
||||
private _supportedInput(input: any): boolean { return isString(input) || isNumber(input); }
|
||||
|
||||
private _supportedPattern(pattern: any): boolean {
|
||||
return isString(pattern) || pattern instanceof RegExp;
|
||||
}
|
||||
|
||||
private _supportedReplacement(replacement: any): boolean {
|
||||
return isString(replacement) || isFunction(replacement);
|
||||
}
|
||||
}
|
@ -146,8 +146,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||
var directiveMetadata = ast.directive;
|
||||
var outputsArray = [];
|
||||
StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
|
||||
[dirProperty, eventName]));
|
||||
StringMapWrapper.forEach(
|
||||
ast.directive.outputs,
|
||||
(eventName: string, dirProperty: string) => outputsArray.push([dirProperty, eventName]));
|
||||
var directiveRecord = new DirectiveRecord({
|
||||
directiveIndex: directiveIndex,
|
||||
callAfterContentInit:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user