Compare commits
2 Commits
2.0.0-beta
...
2.0.0-alph
Author | SHA1 | Date | |
---|---|---|---|
7196243628 | |||
b34340e456 |
4
.bowerrc
4
.bowerrc
@ -1,3 +1,3 @@
|
||||
{
|
||||
"directory" : "bower_components"
|
||||
}
|
||||
"directory": "bower_components"
|
||||
}
|
16
.github/ISSUE_TEMPLATE.md
vendored
16
.github/ISSUE_TEMPLATE.md
vendored
@ -1,16 +0,0 @@
|
||||
**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
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,24 +0,0 @@
|
||||
* **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**:
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -22,7 +22,6 @@ 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
|
||||
|
||||
@ -30,7 +29,6 @@ tmp
|
||||
pubspec.lock
|
||||
.c9
|
||||
.idea/
|
||||
.settings/
|
||||
*.swo
|
||||
modules/.settings
|
||||
.vscode
|
||||
@ -46,6 +44,3 @@ npm-debug.log
|
||||
|
||||
# build-analytics
|
||||
.build-analytics
|
||||
|
||||
# built dart payload tests
|
||||
/modules_dart/payload/**/build
|
||||
|
12
.settings/settings.json
Normal file
12
.settings/settings.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
".git" : true,
|
||||
".idea": true,
|
||||
"node_modules" : true,
|
||||
"bower_components" : true,
|
||||
"packages" : true,
|
||||
"build" : true,
|
||||
"dist" : true,
|
||||
"tmp" : true
|
||||
}
|
||||
}
|
180
.travis.yml
180
.travis.yml
@ -1,137 +1,123 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '5.4.1'
|
||||
- '4.2.1'
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3_v2_0
|
||||
- g3sync
|
||||
|
||||
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:
|
||||
# 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="
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- E2E_BROWSERS=Dartium
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- 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="
|
||||
matrix:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
- MODE=dart
|
||||
- MODE=dart DART_CHANNEL=dev
|
||||
- MODE=saucelabs_required
|
||||
- MODE=browserstack_required
|
||||
- MODE=saucelabs_optional
|
||||
- MODE=browserstack_optional
|
||||
- MODE=dart_ddc
|
||||
- MODE=js
|
||||
- MODE=router
|
||||
- MODE=build_only
|
||||
- MODE=typescript_next
|
||||
- MODE=lint
|
||||
- MODE=payload
|
||||
# Order: slowest build on top, so that we don't hog VMs while waiting for others to complete.
|
||||
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=dart_experimental 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=lint DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs_optional"
|
||||
- env: "MODE=browserstack_optional"
|
||||
# Tracked in https://github.com/angular/angular/issues/7050
|
||||
- env: "MODE=typescript_next"
|
||||
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
|
||||
addons:
|
||||
firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
before_install:
|
||||
- node tools/analytics/build-analytics start ci job
|
||||
- node tools/analytics/build-analytics start ci before_install
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
- export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
# Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
- node tools/analytics/build-analytics success ci before_install
|
||||
- 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'
|
||||
|
||||
install:
|
||||
- node tools/analytics/build-analytics start ci install
|
||||
# Install version of npm that we are locked against
|
||||
- npm install -g npm@3.5.3
|
||||
# Install version of Chromium that we are locked against
|
||||
- ./scripts/ci/install_chromium.sh
|
||||
# Install version of Dart based on the matrix build variables
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
# Print the size of caches to ease debugging
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies
|
||||
# check-node-modules will exit(1) if we don't need to install
|
||||
# we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
- node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
- node tools/analytics/build-analytics success ci install
|
||||
- npm 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
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/ci/presubmit-queue-setup.sh
|
||||
|
||||
script:
|
||||
- node tools/analytics/build-analytics start ci script
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
- node tools/analytics/build-analytics success ci script
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
|
||||
after_script:
|
||||
- node tools/analytics/build-analytics start ci after_script
|
||||
- ./scripts/ci/print-logs.sh
|
||||
- ./scripts/ci/after-script.sh
|
||||
- ./scripts/publish/publish-build-artifacts.sh
|
||||
- node tools/analytics/build-analytics success ci after_script
|
||||
- tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
||||
- ./scripts/ci/print-logs.sh
|
||||
- ./scripts/ci/after-script.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# trigger Buildtime Trend Service to parse Travis CI log
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
- http://104.197.9.155:8484/hubot/travis/activity
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# trigger Buildtime Trend Service to parse Travis CI log
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: never # default: never
|
||||
on_start: false # default: false
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
||||
deploy:
|
||||
- provider: gcs
|
||||
# This is for project angular-github-babysitter
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
# this bucket has a lifecycle to delete after 90 days:
|
||||
# $ echo '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 90}}]}' > lifecycle.json
|
||||
# $ gsutil lifecycle set lifecycle.json gs://angular2-snapshots
|
||||
bucket: angular2-snapshots
|
||||
# don't delete generated files
|
||||
skip_cleanup: true
|
||||
# serve to public at https://storage.googleapis.com/angular2-snapshots/SHA/dart_stable/dist.tgz
|
||||
acl: public-read
|
||||
# upload the .tgz archive created in scripts/ci/build_and_test.sh
|
||||
local-dir: deploy
|
||||
# create a "subdirectory" for each commit
|
||||
upload-dir: $TRAVIS_COMMIT/dart_stable
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = dart && $DART_CHANNEL = stable"
|
||||
- provider: gcs
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
bucket: angular2-snapshots
|
||||
skip_cleanup: true
|
||||
acl: public-read
|
||||
local-dir: deploy
|
||||
upload-dir: $TRAVIS_COMMIT/js
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = js"
|
||||
|
1065
CHANGELOG.md
1065
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -11,11 +11,8 @@ Someone with committer access will do the rest.
|
||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
||||
Angular committers and also prevent breakages on master.
|
||||
|
||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
||||
so the caretaker reviews the changes manually.
|
||||
|
||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||
When a PR is ready to merge, a project member in the CoreTeamMember list (see below) can add the special label,
|
||||
`PR: merge`.
|
||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
||||
is notified that the label was added by an authorized person,
|
||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
||||
@ -29,6 +26,6 @@ Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||
|
||||
## Administration
|
||||
|
||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
|
||||
The list of users who can trigger a merge by adding the label is stored in our appengine app datastore.
|
||||
Edit the contents of the [CoreTeamMember Table](
|
||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
||||
|
@ -48,18 +48,14 @@ features, by not reporting duplicate issues. Providing the following informatio
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Angular Version** - what version of Angular is affected (e.g. 2.0.0-alpha.53)
|
||||
* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Browsers and Operating System** - is this a problem with all browsers?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker],
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps
|
||||
[JSFiddle][jsfiddle] or [Runnable][runnable]) or a unambiguous set of steps.
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
You can file new issues by providing the above information [here](https://github.com/angular/angular/issues/new).
|
||||
|
||||
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
@ -179,10 +175,9 @@ Must be one of the following:
|
||||
semi-colons, etc)
|
||||
* **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 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
|
||||
* **test**: Adding missing tests
|
||||
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||
generation
|
||||
|
||||
### Scope
|
||||
The scope could be anything specifying place of the commit change. For example
|
||||
|
85
DEVELOPER.md
85
DEVELOPER.md
@ -7,9 +7,8 @@ JS and Dart versions. It also explains the basic mechanics of using `git`, `node
|
||||
* [Getting the Sources](#getting-the-sources)
|
||||
* [Environment Variable Setup](#environment-variable-setup)
|
||||
* [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
* [Build commands](#build-commands)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
* [Code Style](#code-style)
|
||||
* [Formatting](#clang-format)
|
||||
* [Project Information](#project-information)
|
||||
* [CI using Travis](#ci-using-travis)
|
||||
* [Transforming Dart code](#transforming-dart-code)
|
||||
@ -23,16 +22,7 @@ if you'd like to contribute to Angular.
|
||||
Before you can build and test Angular, you must install and configure the
|
||||
following products on your development machine:
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* *Optional*: [Dart](https://www.dartlang.org) (version ` >=1.13.2 <2.0.0`), specifically the Dart-SDK and
|
||||
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
|
||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||
@ -42,6 +32,19 @@ following products on your development machine:
|
||||
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
|
||||
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=4.2.1 <5`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=2.14.7 <3.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||
bleeding edge functionality, built especially for developers (and early adopters).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
@ -92,7 +95,7 @@ export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
||||
PATH+=":$DART_SDK/bin"
|
||||
```
|
||||
|
||||
And specify where the pub’s dependencies are downloaded. By default, this directory is located under .pub_cache
|
||||
And specify where the pub’s dependencies are downloaded. By default, this directory is located under .pub_cache
|
||||
in your home directory (on Mac and Linux), or in AppData\Roaming\Pub\Cache (on Windows).
|
||||
|
||||
```shell
|
||||
@ -142,6 +145,12 @@ You can selectively build either the JS or Dart versions as follows:
|
||||
* `$(npm bin)/gulp build.js`
|
||||
* `$(npm bin)/gulp build.dart`
|
||||
|
||||
Also note that in order for the whole test suite to succeed you will need to generate the type definitions by running:
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/typings
|
||||
```
|
||||
|
||||
To clean out the `dist` folder, run:
|
||||
|
||||
```shell
|
||||
@ -186,27 +195,6 @@ tests respectively.
|
||||
**Note**: **watch mode** needs symlinks to work, so if you're using windows, ensure you have the
|
||||
rights to built them in your operating system.
|
||||
|
||||
### Unit tests with Sauce Labs or Browser Stack
|
||||
|
||||
First, in a terminal, create a tunnel with [Sauce Connect](https://docs.saucelabs.com/reference/sauce-connect/) or [Browser Stack Local](https://www.browserstack.com/local-testing#command-line), and valid credentials.
|
||||
|
||||
Then, in another terminal:
|
||||
- Define the credentials as environment variables, e.g.:
|
||||
```
|
||||
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
|
||||
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
|
||||
```
|
||||
- Then run `gulp test.unit.js.(sauce|browserstack) --browsers=option1,option2,..,optionN`
|
||||
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
|
||||
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
|
||||
|
||||
Some examples of commands:
|
||||
```
|
||||
gulp test.unit.js.sauce --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
|
||||
gulp test.unit.js.sauce --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
```
|
||||
|
||||
### E2E tests
|
||||
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||
@ -227,9 +215,7 @@ Angular specific command line options when running protractor:
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
## Code Style
|
||||
|
||||
### Formatting with <a name="clang-format">clang-format</a>
|
||||
## Formatting with <a name="clang-format">clang-format</a>
|
||||
|
||||
We use [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to automatically enforce code
|
||||
style for our TypeScript code. This allows us to focus our code reviews more on the content, and
|
||||
@ -247,7 +233,12 @@ Your life will be easier if you include the formatter in your standard workflow.
|
||||
likely forget to check the formatting, and waste time waiting for a build on Travis that fails due
|
||||
to some whitespace difference.
|
||||
|
||||
* Use `$(npm bin)/clang-format -i [file name]` to format a file (or multiple).
|
||||
* Install clang-format with `npm install -g clang-format`.
|
||||
* Use `clang-format -i [file name]` to format a file (or multiple).
|
||||
Note that `clang-format` tries to load a `clang-format` node module close to the sources being
|
||||
formatted, or from the `$CWD`, and only then uses the globally installed one - so the version used
|
||||
should automatically match the one required by the project.
|
||||
Use `clang-format -version` in case you get confused.
|
||||
* Use `gulp enforce-format` to check if your code is `clang-format` clean. This also gives
|
||||
you a command line to format your code.
|
||||
* `clang-format` also includes a git hook, run `git clang-format` to format all files you
|
||||
@ -269,24 +260,16 @@ to some whitespace difference.
|
||||
- Synchronize files after execution: checked
|
||||
- Open console: not checked
|
||||
- Show in: Editor menu
|
||||
- Program: `$ProjectFileDir$/node_modules/.bin/clang-format`
|
||||
- Program: [path to clang-format, try `$ echo $(npm config get prefix)/bin/clang-format`]
|
||||
- Parameters: `-i -style=file $FilePath$`
|
||||
- Working directory: `$ProjectFileDir$`
|
||||
* `clang-format` integrations are also available for many popular editors (`vim`, `emacs`,
|
||||
`Sublime Text`, etc.).
|
||||
|
||||
### Linting
|
||||
|
||||
We use [tslint](https://github.com/palantir/tslint) for linting. See linting rules in [gulpfile](gulpfile.js). To lint, run
|
||||
|
||||
```shell
|
||||
$ gulp lint
|
||||
```
|
||||
|
||||
|
||||
## Generating the API documentation
|
||||
|
||||
The following gulp task will generate the API docs in the `dist/angular.io/partials/api/angular2`:
|
||||
|
||||
|
||||
```shell
|
||||
$(npm bin)/gulp docs/angular.io
|
||||
```
|
||||
@ -296,8 +279,8 @@ You can serve the generated documentation to check how it would render on [angul
|
||||
- install dependencies as described in the [angular.io README](https://github.com/angular/angular.io/blob/master/README.md),
|
||||
- copy the generated documentation from your local angular repo at `angular/dist/angular.io/partials/api/angular2` to your local angular.io repo at `angular.io/public/docs/js/latest/api`,
|
||||
- run `harp compile` at the root of the angular.io repo to check the generated documentation for errors,
|
||||
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
|
||||
|
||||
- run `harp server` and open a browser at `http://localhost:9000/docs/js/latest/api/` to check the rendered documentation.
|
||||
|
||||
## Project Information
|
||||
|
||||
### Folder structure
|
||||
|
215
LICENSE
215
LICENSE
@ -1,21 +1,202 @@
|
||||
The MIT License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1. Definitions.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
13
README.md
13
README.md
@ -12,12 +12,20 @@ Angular
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
|
||||
Angular 2 is currently in **Beta**.
|
||||
Angular 2 is currently in **Developer Preview**. We recommend using Angular 1.X for production
|
||||
applications:
|
||||
|
||||
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
|
||||
## Want to help?
|
||||
|
||||
@ -28,7 +36,8 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[dartium]: http://www.dartlang.org/tools/dartium
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[download]: http://angular.io/download/
|
||||
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
|
@ -21,7 +21,7 @@ By default the debugging tools are disabled.
|
||||
Enable the debugging tools as follows:
|
||||
|
||||
```dart
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
import 'package:angular2/tools.dart';
|
||||
|
||||
main() async {
|
||||
var appRef = await bootstrap(Application);
|
||||
|
@ -14,7 +14,7 @@ Ctrl + Shift + j.
|
||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||
|
||||
```typescript
|
||||
import {enableDebugTools} from 'angular2/platform/browser';
|
||||
import 'angular2/tools';
|
||||
|
||||
bootstrap(Application).then((appRef) => {
|
||||
enableDebugTools(appRef);
|
||||
@ -117,7 +117,7 @@ speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
@View({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
|
@ -1,318 +0,0 @@
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
// FirefoxBeta 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}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
// TODO(mlaval): iOS9 deactivated as not reliable, reactivate after https://github.com/angular/angular/issues/5408
|
||||
'iOS9': { unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] },
|
||||
'ChromeNoSandbox': {
|
||||
base: 'Chrome',
|
||||
flags: ['--no-sandbox'] },
|
||||
'SL_CHROME': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '46'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_CHROMEDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'dev'
|
||||
},
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '42'
|
||||
},
|
||||
'SL_FIREFOXBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_FIREFOXDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 'dev'
|
||||
},
|
||||
'SL_SAFARI7': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
'SL_SAFARI8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
},
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
version: '9.0'
|
||||
},
|
||||
'SL_IOS7': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '7.1'
|
||||
},
|
||||
'SL_IOS8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8.4'
|
||||
},
|
||||
'SL_IOS9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'iphone',
|
||||
platform: 'OS X 10.10',
|
||||
version: '9.1'
|
||||
},
|
||||
'SL_IE9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2008',
|
||||
version: '9'
|
||||
},
|
||||
'SL_IE10': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2012',
|
||||
version: '10'
|
||||
},
|
||||
'SL_IE11': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
'SL_EDGE': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'microsoftedge',
|
||||
platform: 'Windows 10',
|
||||
version: '20.10240'
|
||||
},
|
||||
'SL_ANDROID4.1': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.1'
|
||||
},
|
||||
'SL_ANDROID4.2': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.2'
|
||||
},
|
||||
'SL_ANDROID4.3': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.3'
|
||||
},
|
||||
'SL_ANDROID4.4': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '4.4'
|
||||
},
|
||||
'SL_ANDROID5': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
version: '5.1'
|
||||
},
|
||||
|
||||
'BS_CHROME': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'OS X',
|
||||
os_version: 'Yosemite'
|
||||
},
|
||||
'BS_FIREFOX': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_SAFARI7': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Mavericks'
|
||||
},
|
||||
'BS_SAFARI8': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Yosemite'
|
||||
},
|
||||
'BS_SAFARI9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'El Capitan'
|
||||
},
|
||||
'BS_IOS7': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 5S',
|
||||
os: 'ios',
|
||||
os_version: '7.0'
|
||||
},
|
||||
'BS_IOS8': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6',
|
||||
os: 'ios',
|
||||
os_version: '8.3'
|
||||
},
|
||||
'BS_IOS9': {
|
||||
base: 'BrowserStack',
|
||||
device: 'iPhone 6S',
|
||||
os: 'ios',
|
||||
os_version: '9.0'
|
||||
},
|
||||
'BS_IE9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '9.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE10': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '10.0',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE11': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '11.0',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_EDGE': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'edge',
|
||||
os: 'Windows',
|
||||
os_version: '10'
|
||||
},
|
||||
'BS_WINDOWSPHONE' : {
|
||||
base: 'BrowserStack',
|
||||
device: 'Nokia Lumia 930',
|
||||
os: 'winphone',
|
||||
os_version: '8.1'
|
||||
},
|
||||
'BS_ANDROID5': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 5',
|
||||
os: 'android',
|
||||
os_version: '5.0'
|
||||
},
|
||||
'BS_ANDROID4.4': {
|
||||
base: 'BrowserStack',
|
||||
device: 'HTC One M8',
|
||||
os: 'android',
|
||||
os_version: '4.4'
|
||||
},
|
||||
'BS_ANDROID4.3': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Samsung Galaxy S4',
|
||||
os: 'android',
|
||||
os_version: '4.3'
|
||||
},
|
||||
'BS_ANDROID4.2': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 4',
|
||||
os: 'android',
|
||||
os_version: '4.2'
|
||||
},
|
||||
'BS_ANDROID4.1': {
|
||||
base: 'BrowserStack',
|
||||
device: 'Google Nexus 7',
|
||||
os: 'android',
|
||||
os_version: '4.1'
|
||||
}
|
||||
};
|
||||
|
||||
var sauceAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
||||
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
|
||||
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
customLaunchers: customLaunchers,
|
||||
sauceAliases: sauceAliases,
|
||||
browserstackAliases: browserstackAliases
|
||||
}
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
|
||||
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
|
||||
}
|
||||
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
}
|
21
circle.yml
21
circle.yml
@ -1,21 +0,0 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
override:
|
||||
- npm install:
|
||||
environment:
|
||||
# Token for tsd to increase github rate limit
|
||||
# See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# This is not hidden using https://circleci.com/docs/fork-pr-builds#details
|
||||
# because those are not visible for pull requests, and those should also be reliable.
|
||||
# This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# (password is in Valentine)
|
||||
TSD_GITHUB_TOKEN: ef474500309daea53d5991b3079159a29520a40b
|
||||
|
||||
test:
|
||||
override:
|
||||
- npm run build
|
121
docs/typescript-definition-package/index.js
Normal file
121
docs/typescript-definition-package/index.js
Normal file
@ -0,0 +1,121 @@
|
||||
var Package = require('dgeni').Package;
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var nunjucksPackage = require('dgeni-packages/nunjucks');
|
||||
var typescriptPackage = require('../typescript-package');
|
||||
var gitPackage = require('dgeni-packages/git');
|
||||
var path = require('canonical-path');
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('angular-v2-docs', [jsdocPackage, nunjucksPackage, typescriptPackage, gitPackage])
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/createTypeDefinitionFile'))
|
||||
|
||||
.config(function(readFilesProcessor, inlineTagProcessor) {
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
// Don't run unwanted processors
|
||||
readFilesProcessor.$enabled = false;
|
||||
inlineTagProcessor.$enabled = false;
|
||||
})
|
||||
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'info';
|
||||
})
|
||||
|
||||
|
||||
.config(function(renderDocsProcessor, versionInfo) {
|
||||
renderDocsProcessor.extraData.versionInfo = versionInfo;
|
||||
})
|
||||
|
||||
.config(function(readFilesProcessor, inlineTagProcessor, readTypeScriptModules, createTypeDefinitionFile) {
|
||||
|
||||
// Don't run unwanted processors
|
||||
readFilesProcessor.$enabled = false; // We are not using the normal file reading processor
|
||||
inlineTagProcessor.$enabled = false; // We are not actually processing the inline link tags
|
||||
|
||||
// Configure file reading
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
readTypeScriptModules.sourceFiles = [
|
||||
'angular2/angular2.ts',
|
||||
'angular2/web_worker/worker.ts',
|
||||
'angular2/web_worker/ui.ts',
|
||||
'angular2/router.ts',
|
||||
'angular2/http.ts',
|
||||
'angular2/testing.ts'
|
||||
];
|
||||
readTypeScriptModules.basePath = path.resolve(path.resolve(__dirname, '../../modules'));
|
||||
|
||||
createTypeDefinitionFile.typeDefinitions = [
|
||||
{
|
||||
id: 'angular2/angular2',
|
||||
references: [],
|
||||
modules: {
|
||||
'angular2/angular2': {namespace: 'ng', id: 'angular2/angular2'},
|
||||
'angular2/web_worker/worker': {namespace: 'ngWorker', id: 'angular2/web_worker/worker'},
|
||||
'angular2/web_worker/ui': {namespace: 'ngUi', id: 'angular2/web_worker/ui'}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'angular2/router',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: {Type: 'ng.Type', InjectableReference: 'ng.InjectableReference', ElementRef: 'ng.ElementRef', DynamicComponentLoader: 'ng.DynamicComponentLoader'},
|
||||
modules: {'angular2/router': {namespace: 'ngRouter', id: 'angular2/router'}}
|
||||
},
|
||||
{
|
||||
id: 'angular2/http',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: {Type: 'ng.Type', Observable: 'ng.Observable', EventEmitter: 'ng.EventEmitter', InjectableReference: 'ng.InjectableReference' },
|
||||
modules: {'angular2/http': {namespace: 'ngHttp', id: 'angular2/http'}}
|
||||
},
|
||||
{
|
||||
id: 'angular2/testing',
|
||||
references: ['./angular2.d.ts'],
|
||||
remapTypes: { Type: 'ng.Type', Binding: 'ng.Binding', Provider: 'ng.Provider', ViewMetadata: 'ng.ViewMetadata', Injector: 'ng.Injector',
|
||||
Predicate: 'ng.Predicate', ElementRef: 'ng.ElementRef', DebugElement: 'ng.DebugElement',
|
||||
InjectableReference: 'ng.InjectableReference', ComponentRef: 'ng.ComponentRef' },
|
||||
modules: {'angular2/testing': {namespace: 'ngTesting', id: 'angular2/testing'}}
|
||||
}
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
// We actually don't want to parse param docs in this package as we are getting the data out using TS
|
||||
parseTagsProcessor.tagDefinitions.forEach(function(tagDef) {
|
||||
if (tagDef.name === 'param') {
|
||||
tagDef.docProperty = 'paramData';
|
||||
tagDef.transforms = [];
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/docs';
|
||||
})
|
||||
|
||||
|
||||
// Configure rendering
|
||||
.config(function(templateFinder, templateEngine) {
|
||||
|
||||
// Nunjucks and Angular conflict in their template bindings so change Nunjucks
|
||||
templateEngine.config.tags = {
|
||||
variableStart: '{$',
|
||||
variableEnd: '$}'
|
||||
};
|
||||
|
||||
templateFinder.templateFolders
|
||||
.unshift(path.resolve(__dirname, 'templates'));
|
||||
|
||||
templateFinder.templatePatterns = [
|
||||
'${ doc.template }',
|
||||
'${ doc.id }.${ doc.docType }.template.html',
|
||||
'${ doc.id }.template.html',
|
||||
'${ doc.docType }.template.html',
|
||||
'common.template.html'
|
||||
];
|
||||
});
|
11
docs/typescript-definition-package/mocks/mockPackage.js
Normal file
11
docs/typescript-definition-package/mocks/mockPackage.js
Normal file
@ -0,0 +1,11 @@
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
module.exports = function mockPackage() {
|
||||
|
||||
return new Package('mockPackage', [require('../')])
|
||||
|
||||
// provide a mock log service
|
||||
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
|
||||
// .factory('templateEngine', function() { return {}; });
|
||||
|
||||
};
|
220
docs/typescript-definition-package/processors/code_gen.js
Normal file
220
docs/typescript-definition-package/processors/code_gen.js
Normal file
@ -0,0 +1,220 @@
|
||||
'use strict';
|
||||
|
||||
function DtsSerializer(remap) {
|
||||
this.remap = remap;
|
||||
}
|
||||
|
||||
DtsSerializer.prototype = {
|
||||
_initializerRegex: /\s*=[^>][^,}]*/g,
|
||||
|
||||
constructor: DtsSerializer,
|
||||
|
||||
declaration: function(buffer, ast) {
|
||||
buffer.push(ast.name);
|
||||
if (ast.optional) buffer.push('?');
|
||||
if (ast.typeParameters) {
|
||||
buffer.push('<');
|
||||
buffer.push(ast.typeParameters.join(', '));
|
||||
buffer.push('>');
|
||||
}
|
||||
if (ast.parameters) {
|
||||
buffer.push('(');
|
||||
var parameters = ast.parameters;
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = parameters[i].replace(this._initializerRegex, '');
|
||||
}
|
||||
buffer.push(parameters.join(', '));
|
||||
buffer.push(')');
|
||||
}
|
||||
if (!isConstructor(ast)) {
|
||||
if (ast.returnType) {
|
||||
buffer.push(': ', ast.returnType);
|
||||
} else if (ast.parameters) {
|
||||
buffer.push(': void');
|
||||
} else {
|
||||
buffer.push(': any');
|
||||
}
|
||||
}
|
||||
buffer.push(';\n');
|
||||
},
|
||||
|
||||
comment: function(buffer, commentText) {
|
||||
if (!(commentText && commentText.match(/\S/))) return;
|
||||
|
||||
buffer.push('/**\n');
|
||||
commentText.replace(/\n*$/, '').split('\n').forEach(function(line) {
|
||||
buffer.push(' * ' + line + '\n');
|
||||
});
|
||||
buffer.push(' */\n');
|
||||
},
|
||||
|
||||
member: function(buffer, ast) {
|
||||
if (ast.private || ast.internal) return;
|
||||
|
||||
buffer.push('\n');
|
||||
this.comment(buffer, ast.content);
|
||||
|
||||
if (ast.isStatic) buffer.push('static ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
interfaceOrClass: function(buffer, ast, isInterface) {
|
||||
if (ast.abstract) {
|
||||
buffer.push('abstract ');
|
||||
}
|
||||
|
||||
buffer.push(isInterface ? 'interface ' : 'class ');
|
||||
buffer.push(ast.name);
|
||||
buffer.push(ast.typeParams);
|
||||
buffer.push(ast.heritage);
|
||||
buffer.push(' {');
|
||||
buffer.indent();
|
||||
if (ast.newMember) this.member(buffer, ast.newMember);
|
||||
if (ast.callMember) this.member(buffer, ast.callMember);
|
||||
if (ast.constructorDoc) this.member(buffer, ast.constructorDoc);
|
||||
|
||||
ast.statics.forEach(function(staticMember) {
|
||||
this.member(buffer, staticMember);
|
||||
}.bind(this));
|
||||
|
||||
ast.members.forEach(function(member) {
|
||||
this.member(buffer, member);
|
||||
}.bind(this));
|
||||
|
||||
buffer.unindent();
|
||||
buffer.push('}');
|
||||
},
|
||||
|
||||
enum: function(buffer, ast) {
|
||||
buffer.push('enum ');
|
||||
buffer.push(ast.name);
|
||||
buffer.push(ast.typeParams);
|
||||
buffer.push(ast.heritage);
|
||||
buffer.push(' {');
|
||||
buffer.indent();
|
||||
|
||||
ast.members.forEach(function(member, index) {
|
||||
buffer.push('\n');
|
||||
this.comment(buffer, member.content);
|
||||
buffer.push(member.name);
|
||||
if (index !== (ast.members.length - 1)) {
|
||||
buffer.push(',\n');
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
buffer.unindent();
|
||||
buffer.push('}\n');
|
||||
},
|
||||
|
||||
function: function(buffer, ast) {
|
||||
buffer.push('function ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
var: function(buffer, ast) {
|
||||
buffer.push('var ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
let: function(buffer, ast) {
|
||||
buffer.push('let ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
const: function(buffer, ast) {
|
||||
buffer.push('const ');
|
||||
this.declaration(buffer, ast);
|
||||
},
|
||||
|
||||
typeAlias: function(buffer, ast) {
|
||||
buffer.push('type ', ast.name, ' = ', ast.returnType);
|
||||
},
|
||||
|
||||
serializeExport: function(ast) {
|
||||
var buffer = new Buffer();
|
||||
buffer.push('\n');
|
||||
|
||||
try {
|
||||
this.comment(buffer, ast.content);
|
||||
|
||||
switch (ast.docType) {
|
||||
case 'class': this.interfaceOrClass(buffer, ast, false); break;
|
||||
case 'interface': this.interfaceOrClass(buffer, ast, true); break;
|
||||
case 'function': this.function(buffer, ast); break;
|
||||
case 'enum': this.enum(buffer, ast); break;
|
||||
case 'var': this.var(buffer, ast); break;
|
||||
case 'let': this.let(buffer, ast); break;
|
||||
case 'const': this.const(buffer, ast); break;
|
||||
case 'type-alias': this.typeAlias(buffer, ast); break;
|
||||
default: throw new Error("unknown docType: " + ast.docType);
|
||||
}
|
||||
|
||||
var string = buffer.toString();
|
||||
for (var key in this.remap) {
|
||||
if (this.remap.hasOwnProperty(key)) {
|
||||
string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), this.remap[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
} catch (e) {
|
||||
console.log(e.toString(), e.stack);
|
||||
return 'ERROR: ' + e.toString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function Buffer() {
|
||||
this._globalBuffer = [];
|
||||
this._indentedBuffer = [];
|
||||
this._indentationLevel = 1;
|
||||
}
|
||||
|
||||
Buffer.prototype = {
|
||||
constructor: Buffer,
|
||||
|
||||
push: function() {
|
||||
this._indentedBuffer.push.apply(this._indentedBuffer, arguments);
|
||||
},
|
||||
|
||||
indent: function() {
|
||||
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
|
||||
this._indentationLevel++;
|
||||
this._indentedBuffer = [];
|
||||
},
|
||||
|
||||
unindent: function() {
|
||||
this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')});
|
||||
this._indentationLevel--;
|
||||
this._indentedBuffer = [];
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
if (this._indentationLevel !== 1) {
|
||||
throw new Exception("Forgot to unindent? Indentation level: " + this._indentationLevel);
|
||||
}
|
||||
|
||||
this.unindent();
|
||||
|
||||
var string = '';
|
||||
|
||||
this._globalBuffer.forEach(function(indentedChunk) {
|
||||
var indentation = (new Array(indentedChunk.indentationLevel * 2 + 1)).join(' ');
|
||||
indentedChunk.content.split('\n').forEach(function(line) {
|
||||
string += indentation + line + '\n';
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
function isConstructor(ast) {
|
||||
return ast.parameters && ast.name === "constructor";
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DtsSerializer: DtsSerializer
|
||||
};
|
||||
|
@ -0,0 +1,72 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
var codeGen = require('./code_gen.js');
|
||||
|
||||
module.exports = function createTypeDefinitionFile(log, convertPrivateClassesToInterfaces) {
|
||||
|
||||
return {
|
||||
$runAfter: ['processing-docs'],
|
||||
$runBefore: ['docs-processed'],
|
||||
$validate: {
|
||||
dtsPath: { presence: true },
|
||||
dtsExtension: { presence: true },
|
||||
typeDefinitions: { presence: true }
|
||||
},
|
||||
dtsPath: 'typings',
|
||||
dtsExtension: '.d.ts',
|
||||
typeDefinitions: [],
|
||||
$process: function(docs) {
|
||||
var dtsPath = this.dtsPath;
|
||||
var dtsExtension = this.dtsExtension;
|
||||
|
||||
// For each type definition that we wish to create we define a dgeni "doc" for it
|
||||
var typeDefDocs = _.map(this.typeDefinitions, function(def) {
|
||||
|
||||
var id = def.id + dtsExtension;
|
||||
var docPath = path.join(dtsPath, id);
|
||||
|
||||
return {
|
||||
docType: 'type-definition',
|
||||
id: id,
|
||||
aliases: [id],
|
||||
path: docPath,
|
||||
outputPath: docPath,
|
||||
// A type definition may include a number of top level modules
|
||||
// And those modules could be aliased (such as 'angular2/angular2.api' ->
|
||||
// 'angular2/angular2')
|
||||
moduleDocs: _.transform(def.modules,
|
||||
function(moduleDocs, props, alias) {
|
||||
moduleDocs[props.id] = {
|
||||
id: alias,
|
||||
doc: null, namespace: props.namespace,
|
||||
references: def.references
|
||||
};
|
||||
}),
|
||||
dts: new codeGen.DtsSerializer(def.remapTypes)
|
||||
};
|
||||
});
|
||||
|
||||
// Now add all the module docs to their corresponding type definition doc
|
||||
_.forEach(docs, function(doc) {
|
||||
_.forEach(typeDefDocs, function(typeDefDoc) {
|
||||
if(typeDefDoc.moduleDocs[doc.id]) {
|
||||
// Add a copy, because we are going to modify it
|
||||
typeDefDoc.moduleDocs[doc.id].doc = doc;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return _.filter(typeDefDocs, function(doc) {
|
||||
_.forEach(doc.moduleDocs, function(modDoc, alias) {
|
||||
if (!doc || !modDoc.doc) {
|
||||
log.error('createTypeDefinitionFile processor: no such module "' + alias + '" (Did you forget to add it to the modules to load?)');
|
||||
doc = null;
|
||||
return;
|
||||
}
|
||||
convertPrivateClassesToInterfaces(modDoc.doc.exports, true);
|
||||
});
|
||||
return !!doc;
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('createTypeDefinitionFile processor', function() {
|
||||
var dgeni, injector, processor;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
processor = injector.get('createTypeDefinitionFile');
|
||||
|
||||
// Initialize the processor
|
||||
processor.typeDefinitions = [{
|
||||
id: 'angular2/angular2',
|
||||
modules: {
|
||||
'angular2/angular2': {
|
||||
id: 'angular2/angular2',
|
||||
namespace: 'ng'
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('classes with @internal constructors', function() {
|
||||
|
||||
it('should convert heritage from `implements` into `extends`', function() {
|
||||
|
||||
// Create some mock docs for testing
|
||||
var docs = [
|
||||
{
|
||||
id: 'angular2/angular2',
|
||||
exports: [
|
||||
{ docType: 'class', heritage: 'implements Xyz', constructorDoc: { internal: true } }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
docs = processor.$process(docs);
|
||||
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].docType).toEqual('type-definition');
|
||||
|
||||
var moduleDoc = docs[0].moduleDocs['angular2/angular2'].doc;
|
||||
expect(moduleDoc.exports.length).toEqual(2);
|
||||
expect(moduleDoc.exports[0].heritage).toEqual('extends Xyz');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
{% extends '../type-definition.template.html' %}
|
||||
{% block staticDeclarations %}
|
||||
|
||||
interface Map<K,V> {}
|
||||
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
declare module {$ module.namespace $} {
|
||||
// See https://github.com/Microsoft/TypeScript/issues/1168
|
||||
class BaseException /* extends Error */ {
|
||||
message: string;
|
||||
stack: string;
|
||||
toString(): string;
|
||||
}
|
||||
interface InjectableReference {}
|
||||
}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
@ -0,0 +1,43 @@
|
||||
|
||||
{%- macro commentBlock(doc, level) -%}
|
||||
{%- if doc.content | trim %}
|
||||
|
||||
{% if level > 1 %}{$ '/**' | indent(level-1, true) | replace(r/\n$/, "") $}{% else %}/**{% endif %}
|
||||
{$ doc.content | trim | replace(r/^/gm, "* ") | indent(level, true) | replace(r/\n$/, "") $}
|
||||
{$ '*/' | indent(level, true) | replace(r/\n$/, "") $}{% endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
|
||||
// Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $}
|
||||
// Project: http://angular.io/
|
||||
// Definitions by: angular team <https://github.com/angular/>
|
||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||
|
||||
// ***********************************************************
|
||||
// This file is generated by the Angular build process.
|
||||
// Please do not create manual edits or send pull requests
|
||||
// modifying this file.
|
||||
// ***********************************************************
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
{%- if module.references.length %}
|
||||
// {$ alias $} depends transitively on these libraries.
|
||||
// If you don't have them installed you can install them using TSD
|
||||
// https://github.com/DefinitelyTyped/tsd
|
||||
{%- endif %}
|
||||
{% for reference in module.references %}
|
||||
///<reference path="{$ reference $}"/>{% endfor %}{% endfor %}
|
||||
{% block staticDeclarations %}{% endblock %}
|
||||
{% for alias, module in doc.moduleDocs %}
|
||||
{$ commentBlock(module.doc, 1) $}
|
||||
declare module {$ module.namespace $} {
|
||||
|
||||
{%- for export in module.doc.exports -%}
|
||||
{$ doc.dts.serializeExport(export) $}
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
declare module "{$ alias $}" {
|
||||
export = {$ module.namespace $};
|
||||
}
|
||||
|
||||
{% endfor %}
|
74
docs/typescript-package/index.js
Normal file
74
docs/typescript-package/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
var basePackage = require('dgeni-packages/base');
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var Package = require('dgeni').Package;
|
||||
var path = require('canonical-path');
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('typescript-parsing', [basePackage, jsdocPackage])
|
||||
|
||||
// Register the services and file readers
|
||||
.factory(require('./services/modules'))
|
||||
.factory(require('./services/tsParser'))
|
||||
.factory(require('./services/tsParser/createCompilerHost'))
|
||||
.factory(require('./services/tsParser/getFileInfo'))
|
||||
.factory(require('./services/tsParser/getExportDocType'))
|
||||
.factory(require('./services/tsParser/getContent'))
|
||||
|
||||
.factory(require('./services/convertPrivateClassesToInterfaces'))
|
||||
|
||||
.factory('EXPORT_DOC_TYPES', function() {
|
||||
return [
|
||||
'class',
|
||||
'interface',
|
||||
'function',
|
||||
'var',
|
||||
'const',
|
||||
'enum',
|
||||
'type-alias'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/readTypeScriptModules'))
|
||||
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'warn';
|
||||
})
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push({ name: 'internal', transforms: function() { return true; } });
|
||||
})
|
||||
|
||||
// Configure ids and paths
|
||||
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['member'],
|
||||
idTemplate: '${classDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['member'],
|
||||
pathTemplate: '${classDoc.path}/${name}',
|
||||
getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc
|
||||
});
|
||||
|
||||
var MODULES_DOCS_PATH = 'partials/modules';
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module'],
|
||||
pathTemplate: '/${id}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${id}/index.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
pathTemplate: '${moduleDoc.path}/${name}',
|
||||
outputPathTemplate: MODULES_DOCS_PATH + '/${path}/index.html'
|
||||
});
|
||||
|
||||
});
|
11
docs/typescript-package/mocks/mockPackage.js
Normal file
11
docs/typescript-package/mocks/mockPackage.js
Normal file
@ -0,0 +1,11 @@
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
module.exports = function mockPackage() {
|
||||
|
||||
return new Package('mockPackage', [require('../')])
|
||||
|
||||
// provide a mock log service
|
||||
.factory('log', function() { return require('dgeni/lib/mocks/log')(false); })
|
||||
.factory('templateEngine', function() { return {}; });
|
||||
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
export var __esModule = true;
|
||||
export class OKToExport {}
|
||||
export function _thisIsPrivate() {}
|
||||
export var thisIsOK = '!';
|
@ -0,0 +1,5 @@
|
||||
export interface MyInterface {
|
||||
optionalProperty? : string
|
||||
<T, U extends Findable<T>>(param: T) : U
|
||||
new (param: number) : MyInterface
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export class Test {
|
||||
firstItem;
|
||||
constructor() { this.doStuff(); }
|
||||
otherMethod() {}
|
||||
doStuff() {}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export var x = 10;
|
@ -0,0 +1 @@
|
||||
export { x as y} from './privateModule';
|
1
docs/typescript-package/mocks/tsParser/importedSrc.ts
Normal file
1
docs/typescript-package/mocks/tsParser/importedSrc.ts
Normal file
@ -0,0 +1 @@
|
||||
export var x = 100;
|
34
docs/typescript-package/mocks/tsParser/testSrc.ts
Normal file
34
docs/typescript-package/mocks/tsParser/testSrc.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This is the module description
|
||||
*/
|
||||
|
||||
export * from 'importedSrc';
|
||||
|
||||
/**
|
||||
* This is some random other comment
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is MyClass
|
||||
*/
|
||||
export class MyClass {
|
||||
message: String;
|
||||
|
||||
/**
|
||||
* Create a new MyClass
|
||||
* @param {String} name The name to say hello to
|
||||
*/
|
||||
constructor(name) { this.message = 'hello ' + name; }
|
||||
|
||||
/**
|
||||
* Return a greeting message
|
||||
*/
|
||||
greet() { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* An exported function
|
||||
*/
|
||||
export var myFn = (val: number) => return val * 2;
|
413
docs/typescript-package/processors/readTypeScriptModules.js
Normal file
413
docs/typescript-package/processors/readTypeScriptModules.js
Normal file
@ -0,0 +1,413 @@
|
||||
var glob = require('glob');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo,
|
||||
getExportDocType, getContent, log) {
|
||||
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
|
||||
$validate: {
|
||||
sourceFiles: {presence: true},
|
||||
basePath: {presence: true},
|
||||
hidePrivateMembers: {inclusion: [true, false]},
|
||||
sortClassMembers: {inclusion: [true, false]},
|
||||
ignoreExportsMatching: {}
|
||||
},
|
||||
|
||||
// A collection of globs that identify those modules for which we should create docs
|
||||
sourceFiles: [],
|
||||
// The base path from which to load the source files
|
||||
basePath: '.',
|
||||
// We can ignore members of classes that are private
|
||||
hidePrivateMembers: true,
|
||||
// We leave class members sorted in order of declaration
|
||||
sortClassMembers: false,
|
||||
// We can provide a collection of strings or regexes to ignore exports whose export names match
|
||||
ignoreExportsMatching: ['___esModule'],
|
||||
|
||||
$process: function(docs) {
|
||||
|
||||
// Convert ignoreExportsMatching to an array of regexes
|
||||
var ignoreExportsMatching = convertToRegexCollection(this.ignoreExportsMatching);
|
||||
|
||||
var hidePrivateMembers = this.hidePrivateMembers;
|
||||
var sortClassMembers = this.sortClassMembers;
|
||||
|
||||
var basePath = path.resolve(this.basePath);
|
||||
var filesPaths = expandSourceFiles(this.sourceFiles, basePath);
|
||||
var parseInfo = tsParser.parse(filesPaths, this.basePath);
|
||||
var moduleSymbols = parseInfo.moduleSymbols;
|
||||
|
||||
// Iterate through each of the modules that were parsed and generate a module doc
|
||||
// as well as docs for each module's exports.
|
||||
moduleSymbols.forEach(function(moduleSymbol) {
|
||||
|
||||
var moduleDoc = createModuleDoc(moduleSymbol, basePath);
|
||||
|
||||
// Add this module doc to the module lookup collection and the docs collection
|
||||
modules[moduleDoc.id] = moduleDoc;
|
||||
docs.push(moduleDoc);
|
||||
|
||||
// Iterate through this module's exports and generate a doc for each
|
||||
moduleSymbol.exportArray.forEach(function(exportSymbol) {
|
||||
|
||||
// Ignore exports starting with an underscore
|
||||
if (anyMatches(ignoreExportsMatching, exportSymbol.name)) return;
|
||||
|
||||
// If the symbol is an Alias then for most things we want the original resolved symbol
|
||||
var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol;
|
||||
var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker);
|
||||
log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id);
|
||||
|
||||
exportDoc.members = [];
|
||||
exportDoc.statics = [];
|
||||
|
||||
// Generate docs for each of the export's members
|
||||
if (resolvedExport.flags & ts.SymbolFlags.HasMembers) {
|
||||
|
||||
for(var memberName in resolvedExport.members) {
|
||||
// FIXME(alexeagle): why do generic type params appear in members?
|
||||
if (memberName === 'T') {
|
||||
continue;
|
||||
}
|
||||
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
|
||||
var memberSymbol = resolvedExport.members[memberName];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
|
||||
// We special case the constructor and sort the other members alphabetically
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
|
||||
exportDoc.constructorDoc = memberDoc;
|
||||
docs.push(memberDoc);
|
||||
} else if (!hidePrivateMembers || memberSymbol.name.charAt(0) !== '_') {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.members.push(memberDoc);
|
||||
} else if (memberSymbol.name === '__call' && memberSymbol.flags & ts.SymbolFlags.Signature) {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.callMember = memberDoc;
|
||||
} else if (memberSymbol.name === '__new' && memberSymbol.flags & ts.SymbolFlags.Signature) {
|
||||
docs.push(memberDoc);
|
||||
exportDoc.newMember = memberDoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exportDoc.docType === 'enum') {
|
||||
for(var memberName in resolvedExport.exports) {
|
||||
log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id);
|
||||
var memberSymbol = resolvedExport.exports[memberName];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
docs.push(memberDoc);
|
||||
exportDoc.members.push(memberDoc);
|
||||
}
|
||||
} else if (resolvedExport.flags & ts.SymbolFlags.HasExports) {
|
||||
for (var exported in resolvedExport.exports) {
|
||||
if (exported === 'prototype') continue;
|
||||
if (hidePrivateMembers && exported.charAt(0) === '_') continue;
|
||||
var memberSymbol = resolvedExport.exports[exported];
|
||||
var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker);
|
||||
memberDoc.isStatic = true;
|
||||
docs.push(memberDoc);
|
||||
exportDoc.statics.push(memberDoc);
|
||||
}
|
||||
}
|
||||
|
||||
if (sortClassMembers) {
|
||||
exportDoc.members.sort(function(a, b) {
|
||||
if (a.name > b.name) return 1;
|
||||
if (a.name < b.name) return -1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Add this export doc to its module doc
|
||||
moduleDoc.exports.push(exportDoc);
|
||||
docs.push(exportDoc);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function createModuleDoc(moduleSymbol, basePath) {
|
||||
var id = moduleSymbol.name.replace(/^"|"$/g, '');
|
||||
var moduleDoc = {
|
||||
docType: 'module',
|
||||
id: id,
|
||||
aliases: [id],
|
||||
moduleTree: moduleSymbol,
|
||||
content: getContent(moduleSymbol),
|
||||
exports: [],
|
||||
fileInfo: getFileInfo(moduleSymbol, basePath),
|
||||
location: getLocation(moduleSymbol)
|
||||
};
|
||||
return moduleDoc;
|
||||
}
|
||||
|
||||
function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) {
|
||||
var typeParamString = '';
|
||||
var heritageString = '';
|
||||
var typeDefinition = '';
|
||||
|
||||
exportSymbol.declarations.forEach(function(decl) {
|
||||
var sourceFile = ts.getSourceFileOfNode(decl);
|
||||
|
||||
if (decl.typeParameters) {
|
||||
typeParamString = '<' + getText(sourceFile, decl.typeParameters) + '>';
|
||||
}
|
||||
|
||||
if (decl.symbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
typeDefinition = getText(sourceFile, decl.type);
|
||||
}
|
||||
|
||||
if (decl.heritageClauses) {
|
||||
decl.heritageClauses.forEach(function(heritage) {
|
||||
|
||||
if (heritage.token == ts.SyntaxKind.ExtendsKeyword) {
|
||||
heritageString += " extends";
|
||||
heritage.types.forEach(function(typ, idx) {
|
||||
heritageString += (idx > 0 ? ',' : '') + typ.getFullText();
|
||||
});
|
||||
}
|
||||
|
||||
if (heritage.token == ts.SyntaxKind.ImplementsKeyword) {
|
||||
heritageString += " implements";
|
||||
heritage.types.forEach(function(typ, idx) {
|
||||
heritageString += (idx > 0 ? ', ' : '') + typ.getFullText();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//Make sure duplicate aliases aren't created, so "Ambiguous link" warnings are prevented
|
||||
var aliasNames = [name, moduleDoc.id + '/' + name];
|
||||
if (typeParamString) {
|
||||
aliasNames.push(name + typeParamString);
|
||||
aliasNames.push(moduleDoc.id + '/' + name + typeParamString);
|
||||
}
|
||||
|
||||
var exportDoc = {
|
||||
docType: getExportDocType(exportSymbol),
|
||||
name: name,
|
||||
id: moduleDoc.id + '/' + name,
|
||||
typeParams: typeParamString,
|
||||
heritage: heritageString,
|
||||
decorators: getDecorators(exportSymbol),
|
||||
aliases: aliasNames,
|
||||
moduleDoc: moduleDoc,
|
||||
content: getContent(exportSymbol),
|
||||
fileInfo: getFileInfo(exportSymbol, basePath),
|
||||
location: getLocation(exportSymbol)
|
||||
};
|
||||
|
||||
if (exportDoc.docType === 'var' || exportDoc.docType === 'const') {
|
||||
exportDoc.symbolTypeName = exportSymbol.valueDeclaration.type &&
|
||||
exportSymbol.valueDeclaration.type.typeName &&
|
||||
exportSymbol.valueDeclaration.type.typeName.text;
|
||||
}
|
||||
|
||||
if (exportDoc.docType === 'type-alias') {
|
||||
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
|
||||
}
|
||||
|
||||
if(exportSymbol.flags & ts.SymbolFlags.Function) {
|
||||
exportDoc.parameters = getParameters(typeChecker, exportSymbol);
|
||||
}
|
||||
if(exportSymbol.flags & ts.SymbolFlags.Value) {
|
||||
exportDoc.returnType = getReturnType(typeChecker, exportSymbol);
|
||||
}
|
||||
if (exportSymbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
exportDoc.typeDefinition = typeDefinition;
|
||||
}
|
||||
if (isAbstract(exportSymbol)) {
|
||||
exportDoc.abstract = true;
|
||||
}
|
||||
|
||||
// Compute the original module name from the relative file path
|
||||
exportDoc.originalModule = exportDoc.fileInfo.relativePath
|
||||
.replace(new RegExp('\.' + exportDoc.fileInfo.extension + '$'), '');
|
||||
|
||||
return exportDoc;
|
||||
}
|
||||
|
||||
function createMemberDoc(memberSymbol, classDoc, basePath, typeChecker) {
|
||||
var memberDoc = {
|
||||
docType: 'member',
|
||||
classDoc: classDoc,
|
||||
name: memberSymbol.name,
|
||||
decorators: getDecorators(memberSymbol),
|
||||
content: getContent(memberSymbol),
|
||||
fileInfo: getFileInfo(memberSymbol, basePath),
|
||||
location: getLocation(memberSymbol)
|
||||
};
|
||||
|
||||
memberDoc.typeParameters = getTypeParameters(typeChecker, memberSymbol);
|
||||
|
||||
if(memberSymbol.flags & (ts.SymbolFlags.Signature) ) {
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
|
||||
switch(memberDoc.name) {
|
||||
case '__call':
|
||||
memberDoc.name = '';
|
||||
break;
|
||||
case '__new':
|
||||
memberDoc.name = 'new';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Method) {
|
||||
// NOTE: we use the property name `parameters` here so we don't conflict
|
||||
// with the `params` property that will be updated by dgeni reading the
|
||||
// `@param` tags from the docs
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
}
|
||||
|
||||
if (memberSymbol.flags & ts.SymbolFlags.Constructor) {
|
||||
memberDoc.parameters = getParameters(typeChecker, memberSymbol);
|
||||
memberDoc.name = 'constructor';
|
||||
}
|
||||
|
||||
if(memberSymbol.flags & ts.SymbolFlags.Value) {
|
||||
memberDoc.returnType = getReturnType(typeChecker, memberSymbol);
|
||||
}
|
||||
|
||||
if(memberSymbol.flags & ts.SymbolFlags.Optional) {
|
||||
memberDoc.optional = true;
|
||||
}
|
||||
|
||||
return memberDoc;
|
||||
}
|
||||
|
||||
|
||||
function getDecorators(symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
|
||||
var decorators = declaration.decorators && declaration.decorators.map(function(decorator) {
|
||||
decorator = decorator.expression;
|
||||
return {
|
||||
name: decorator.expression ? decorator.expression.text : decorator.text,
|
||||
arguments: decorator.arguments && decorator.arguments.map(function(argument) {
|
||||
return getText(sourceFile, argument).trim();
|
||||
})
|
||||
};
|
||||
});
|
||||
return decorators;
|
||||
}
|
||||
|
||||
function getParameters(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (!declaration.parameters) {
|
||||
var location = getLocation(symbol);
|
||||
throw new Error('missing declaration parameters for "' + symbol.name +
|
||||
'" in ' + sourceFile.fileName +
|
||||
' at line ' + location.start.line);
|
||||
}
|
||||
return declaration.parameters.map(function(parameter) {
|
||||
var paramText = '';
|
||||
if (parameter.dotDotDotToken) {
|
||||
paramText += '...';
|
||||
}
|
||||
paramText += getText(sourceFile, parameter.name);
|
||||
if (parameter.questionToken || parameter.initializer) {
|
||||
paramText += '?';
|
||||
}
|
||||
if (parameter.type) {
|
||||
paramText += ':' + getType(sourceFile, parameter.type);
|
||||
} else {
|
||||
paramText += ': any';
|
||||
if (parameter.dotDotDotToken) {
|
||||
paramText += '[]';
|
||||
}
|
||||
}
|
||||
return paramText.trim();
|
||||
});
|
||||
}
|
||||
|
||||
function getTypeParameters(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (!declaration.typeParameters) return;
|
||||
var typeParams = declaration.typeParameters.map(function(type) {
|
||||
return getText(sourceFile, type).trim();
|
||||
});
|
||||
return typeParams;
|
||||
}
|
||||
|
||||
function getReturnType(typeChecker, symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
if (declaration.type) {
|
||||
return getType(sourceFile, declaration.type).trim();
|
||||
}
|
||||
}
|
||||
|
||||
function isAbstract(symbol) {
|
||||
var declaration = symbol.valueDeclaration || symbol.declarations[0];
|
||||
return declaration.flags & ts.NodeFlags.Abstract;
|
||||
}
|
||||
|
||||
function expandSourceFiles(sourceFiles, basePath) {
|
||||
var filePaths = [];
|
||||
sourceFiles.forEach(function(sourcePattern) {
|
||||
filePaths = filePaths.concat(glob.sync(sourcePattern, { cwd: basePath }));
|
||||
});
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
|
||||
function getText(sourceFile, node) {
|
||||
return sourceFile.text.substring(node.pos, node.end);
|
||||
}
|
||||
|
||||
|
||||
// Strip any local renamed imports from the front of types
|
||||
function getType(sourceFile, type) {
|
||||
var text = getText(sourceFile, type);
|
||||
while (text.indexOf(".") >= 0) {
|
||||
// Keep namespaced symbols in RxNext
|
||||
if (text.match(/^\s*RxNext\./)) break;
|
||||
// handle the case List<thing.stuff> -> List<stuff>
|
||||
text = text.replace(/([^.<]*)\.([^>]*)/, "$2");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function getLocation(symbol) {
|
||||
var node = symbol.valueDeclaration || symbol.declarations[0];
|
||||
var sourceFile = ts.getSourceFileOfNode(node);
|
||||
var location = {
|
||||
start: ts.getLineAndCharacterOfPosition(sourceFile, node.pos),
|
||||
end: ts.getLineAndCharacterOfPosition(sourceFile, node.end)
|
||||
};
|
||||
return location;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function convertToRegexCollection(items) {
|
||||
if (!items) return [];
|
||||
|
||||
// Must be an array
|
||||
if (!_.isArray(items)) {
|
||||
items = [items];
|
||||
}
|
||||
|
||||
// Convert string to exact matching regexes
|
||||
return items.map(function(item) {
|
||||
return _.isString(item) ? new RegExp('^' + item + '$') : item;
|
||||
});
|
||||
}
|
||||
|
||||
function anyMatches(regexes, item) {
|
||||
for(var i=0; i<regexes.length; ++i) {
|
||||
if ( item.match(regexes[i]) ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
127
docs/typescript-package/processors/readTypeScriptModules.spec.js
Normal file
127
docs/typescript-package/processors/readTypeScriptModules.spec.js
Normal file
@ -0,0 +1,127 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('readTypeScriptModules', function() {
|
||||
var dgeni, injector, processor;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
processor = injector.get('readTypeScriptModules');
|
||||
processor.basePath = path.resolve(__dirname, '../mocks/readTypeScriptModules');
|
||||
});
|
||||
|
||||
|
||||
describe('exportDocs', function() {
|
||||
it('should provide the original module if the export is re-exported', function() {
|
||||
processor.sourceFiles = [ 'publicModule.ts' ];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var exportedDoc = docs[1];
|
||||
expect(exportedDoc.originalModule).toEqual('privateModule');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ignoreExportsMatching', function() {
|
||||
it('should ignore exports that match items in the `ignoreExportsMatching` property', function() {
|
||||
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
|
||||
processor.ignoreExportsMatching = [/^_/];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
expect(moduleDoc.docType).toEqual('module');
|
||||
expect(moduleDoc.exports).toEqual([
|
||||
jasmine.objectContaining({ name: 'OKToExport' }),
|
||||
jasmine.objectContaining({ name: 'thisIsOK' })
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only ignore `___esModule` exports by default', function() {
|
||||
processor.sourceFiles = [ 'ignoreExportsMatching.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
expect(moduleDoc.docType).toEqual('module');
|
||||
expect(getNames(moduleDoc.exports)).toEqual([
|
||||
'OKToExport',
|
||||
'_thisIsPrivate',
|
||||
'thisIsOK'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('interfaces', function() {
|
||||
|
||||
it('should mark optional properties', function() {
|
||||
processor.sourceFiles = [ 'interfaces.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
var exportedInterface = moduleDoc.exports[0];
|
||||
var member = exportedInterface.members[0];
|
||||
expect(member.name).toEqual('optionalProperty');
|
||||
expect(member.optional).toEqual(true);
|
||||
});
|
||||
|
||||
|
||||
it('should handle "call" type interfaces', function() {
|
||||
processor.sourceFiles = [ 'interfaces.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
|
||||
var moduleDoc = docs[0];
|
||||
var exportedInterface = moduleDoc.exports[0];
|
||||
|
||||
expect(exportedInterface.callMember).toBeDefined();
|
||||
expect(exportedInterface.callMember.parameters).toEqual(['param: T']);
|
||||
expect(exportedInterface.callMember.returnType).toEqual('U');
|
||||
expect(exportedInterface.callMember.typeParameters).toEqual(['T', 'U extends Findable<T>']);
|
||||
expect(exportedInterface.newMember).toBeDefined();
|
||||
expect(exportedInterface.newMember.parameters).toEqual(['param: number']);
|
||||
expect(exportedInterface.newMember.returnType).toEqual('MyInterface');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ordering of members', function() {
|
||||
it('should order class members in order of appearance (by default)', function() {
|
||||
processor.sourceFiles = ['orderingOfMembers.ts'];
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
var classDoc = _.find(docs, { docType: 'class' });
|
||||
expect(classDoc.docType).toEqual('class');
|
||||
expect(getNames(classDoc.members)).toEqual([
|
||||
'firstItem',
|
||||
'otherMethod',
|
||||
'doStuff',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should not order class members if not sortClassMembers is false', function() {
|
||||
processor.sourceFiles = ['orderingOfMembers.ts'];
|
||||
processor.sortClassMembers = false;
|
||||
var docs = [];
|
||||
processor.$process(docs);
|
||||
var classDoc = _.find(docs, { docType: 'class' });
|
||||
expect(classDoc.docType).toEqual('class');
|
||||
expect(getNames(classDoc.members)).toEqual([
|
||||
'firstItem',
|
||||
'otherMethod',
|
||||
'doStuff'
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getNames(collection) {
|
||||
return collection.map(function(item) { return item.name; });
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function convertPrivateClassesToInterfaces() {
|
||||
return function(exportDocs, addInjectableReference) {
|
||||
_.forEach(exportDocs, function(exportDoc) {
|
||||
|
||||
// Search for classes with a constructor marked as `@internal`
|
||||
if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.internal) {
|
||||
|
||||
// Convert this class to an interface with no constructor
|
||||
exportDoc.docType = 'interface';
|
||||
exportDoc.constructorDoc = null;
|
||||
|
||||
if (exportDoc.heritage) {
|
||||
// convert the heritage since interfaces use `extends` not `implements`
|
||||
exportDoc.heritage = exportDoc.heritage.replace('implements', 'extends');
|
||||
}
|
||||
|
||||
if (addInjectableReference) {
|
||||
// Add the `declare var SomeClass extends InjectableReference` construct
|
||||
exportDocs.push({
|
||||
docType: 'var',
|
||||
name: exportDoc.name,
|
||||
id: exportDoc.id,
|
||||
returnType: 'InjectableReference'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
@ -0,0 +1,76 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var _ = require('lodash');
|
||||
|
||||
describe('readTypeScriptModules', function() {
|
||||
var dgeni, injector, convertPrivateClassesToInterfaces;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
convertPrivateClassesToInterfaces = injector.get('convertPrivateClassesToInterfaces');
|
||||
});
|
||||
|
||||
it('should convert @internal class docs to interface docs', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true }
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].docType).toEqual('interface');
|
||||
});
|
||||
|
||||
|
||||
it('should not touch non-internal class docs', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { }
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].docType).toEqual('class');
|
||||
});
|
||||
|
||||
|
||||
it('should convert the heritage since interfaces use `extends` not `implements`', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true },
|
||||
heritage: 'implements parentInterface'
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, false);
|
||||
expect(docs[0].heritage).toEqual('extends parentInterface');
|
||||
});
|
||||
|
||||
|
||||
it('should add new injectable reference types, if specified, to the passed in collection', function() {
|
||||
var docs = [
|
||||
{
|
||||
docType: 'class',
|
||||
name: 'privateClass',
|
||||
id: 'privateClass',
|
||||
constructorDoc: { internal: true },
|
||||
heritage: 'implements parentInterface'
|
||||
}
|
||||
];
|
||||
convertPrivateClassesToInterfaces(docs, true);
|
||||
expect(docs[1]).toEqual({
|
||||
docType : 'var',
|
||||
name : 'privateClass',
|
||||
id : 'privateClass',
|
||||
returnType : 'InjectableReference'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
3
docs/typescript-package/services/modules.js
Normal file
3
docs/typescript-package/services/modules.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function modules() {
|
||||
return {};
|
||||
};
|
@ -0,0 +1,75 @@
|
||||
var ts = require('typescript');
|
||||
var fs = require('fs');
|
||||
var path = require('canonical-path');
|
||||
|
||||
// We need to provide our own version of CompilerHost because we want to set the
|
||||
// base directory and specify what extensions to consider when trying to load a source
|
||||
// file
|
||||
module.exports = function createCompilerHost(log) {
|
||||
|
||||
return function createCompilerHost(options, baseDir, extensions) {
|
||||
|
||||
return {
|
||||
getSourceFile: function(fileName, languageVersion, onError) {
|
||||
var text, resolvedPath, resolvedPathWithExt;
|
||||
|
||||
// Strip off the extension and resolve relative to the baseDir
|
||||
baseFilePath = fileName.replace(/\.[^.]+$/, '');
|
||||
resolvedPath = path.resolve(baseDir, baseFilePath);
|
||||
|
||||
// Iterate through each possible extension and return the first source file that is actually found
|
||||
for(var i=0; i<extensions.length; i++) {
|
||||
|
||||
// Try reading the content from files using each of the given extensions
|
||||
try {
|
||||
resolvedPathWithExt = resolvedPath + extensions[i];
|
||||
log.silly('getSourceFile:', resolvedPathWithExt);
|
||||
text = fs.readFileSync(resolvedPathWithExt, { encoding: options.charset });
|
||||
log.debug('found source file:', fileName, resolvedPathWithExt);
|
||||
return ts.createSourceFile(baseFilePath + extensions[i], text, languageVersion);
|
||||
}
|
||||
catch(e) {
|
||||
// Try again if the file simply did not exist, otherwise report the error as a warning
|
||||
if(e.code !== 'ENOENT') {
|
||||
if (onError) onError(e.message);
|
||||
log.warn('Error reading ' + resolvedPathWithExt + ' : ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getDefaultLibFileName: function(options) {
|
||||
return path.resolve(path.dirname(ts.sys.getExecutingFilePath()), ts.getDefaultLibFileName(options));
|
||||
},
|
||||
writeFile: function(fileName, data, writeByteOrderMark, onError) {
|
||||
// no-op
|
||||
},
|
||||
getCurrentDirectory: function() {
|
||||
return baseDir;
|
||||
},
|
||||
useCaseSensitiveFileNames: function() {
|
||||
return ts.sys.useCaseSensitiveFileNames;
|
||||
},
|
||||
getCanonicalFileName: function(fileName) {
|
||||
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
|
||||
// otherwise use toLowerCase as a canonical form.
|
||||
return ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
},
|
||||
getNewLine: function() {
|
||||
return ts.sys.newLine;
|
||||
},
|
||||
fileExists: function(fileName) {
|
||||
var resolvedPath = path.resolve(baseDir, fileName);
|
||||
try {
|
||||
fs.statSync(resolvedPath);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
readFile: function(fileName) {
|
||||
var resolvedPath = path.resolve(baseDir, fileName);
|
||||
return fs.readFileSync(resolvedPath, { encoding: options.charset });
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,80 @@
|
||||
var mockPackage = require('../../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
var ts = require('typescript');
|
||||
|
||||
describe('createCompilerHost', function() {
|
||||
var dgeni, injector, options, host, baseDir, extensions;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
var createCompilerHost = injector.get('createCompilerHost');
|
||||
|
||||
options = { charset: 'utf8' };
|
||||
baseDir = path.resolve(__dirname, '../../mocks/tsParser');
|
||||
extensions = ['.ts', '.js'];
|
||||
|
||||
host = createCompilerHost(options, baseDir, extensions);
|
||||
});
|
||||
|
||||
describe('getSourceFile', function() {
|
||||
it('should return a SourceFile object for a given path, with fileName relative to baseDir', function() {
|
||||
var sourceFile = host.getSourceFile('testSrc.ts');
|
||||
expect(sourceFile.fileName).toEqual('testSrc.ts');
|
||||
expect(sourceFile.pos).toEqual(0);
|
||||
expect(sourceFile.text).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
it('should try each of the configured extensions and update the filename to the correct extension', function() {
|
||||
var sourceFile = host.getSourceFile('testSrc.js');
|
||||
expect(sourceFile.fileName).toEqual('testSrc.ts');
|
||||
|
||||
sourceFile = host.getSourceFile('../mockPackage.ts');
|
||||
expect(sourceFile.fileName).toEqual('../mockPackage.js');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getDefaultLibFileName', function() {
|
||||
it('should return a path to the default library', function() {
|
||||
expect(host.getDefaultLibFileName(options)).toContain('typescript/lib/lib.d.ts');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('writeFile', function() {
|
||||
it('should do nothing', function() {
|
||||
host.writeFile();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getCurrentDirectory', function() {
|
||||
it('should return the baseDir', function() {
|
||||
expect(host.getCurrentDirectory()).toEqual(baseDir);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('useCaseSensitiveFileNames', function() {
|
||||
it('should return true if the OS is case sensitive', function() {
|
||||
expect(host.useCaseSensitiveFileNames()).toBe(ts.sys.useCaseSensitiveFileNames);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getCanonicalFileName', function() {
|
||||
it('should lower case the filename', function() {
|
||||
var expectedFilePath = host.useCaseSensitiveFileNames() ? 'SomeFile.ts' : 'somefile.ts';
|
||||
expect(host.getCanonicalFileName('SomeFile.ts')).toEqual(expectedFilePath);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getNewLine', function() {
|
||||
it('should return the newline character for the OS', function() {
|
||||
expect(host.getNewLine()).toEqual(require('os').EOL);
|
||||
});
|
||||
});
|
||||
});
|
49
docs/typescript-package/services/tsParser/getContent.js
Normal file
49
docs/typescript-package/services/tsParser/getContent.js
Normal file
@ -0,0 +1,49 @@
|
||||
var ts = require('typescript');
|
||||
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
|
||||
|
||||
module.exports = function getContent() {
|
||||
return function(symbol) {
|
||||
|
||||
var content = "";
|
||||
|
||||
if (!symbol.declarations) return content;
|
||||
|
||||
symbol.declarations.forEach(function(declaration) {
|
||||
|
||||
// If this is left side of dotted module declaration, there is no doc comment associated with this declaration
|
||||
if (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.body.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// If this is dotted module name, get the doc comments from the parent
|
||||
while (declaration.kind === ts.SyntaxKind.ModuleDeclaration && declaration.parent.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
declaration = declaration.parent;
|
||||
}
|
||||
|
||||
// If this is a variable declaration then we get the doc comments from the grand parent
|
||||
if (declaration.kind === ts.SyntaxKind.VariableDeclaration) {
|
||||
declaration = declaration.parent.parent;
|
||||
}
|
||||
|
||||
// Get the source file of this declaration
|
||||
var sourceFile = ts.getSourceFileOfNode(declaration);
|
||||
var commentRanges = ts.getJsDocComments(declaration, sourceFile);
|
||||
|
||||
if (commentRanges) {
|
||||
commentRanges.forEach(function(commentRange) {
|
||||
content += sourceFile.text
|
||||
.substring(commentRange.pos+ '/**'.length, commentRange.end - '*/'.length)
|
||||
.replace(LEADING_STAR, '')
|
||||
.trim();
|
||||
if (commentRange.hasTrailingNewLine) {
|
||||
content += '\n';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
content += '\n';
|
||||
});
|
||||
|
||||
return content;
|
||||
};
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function getExportDocType(log) {
|
||||
|
||||
return function(symbol) {
|
||||
if(symbol.flags & ts.SymbolFlags.Function) {
|
||||
return 'function';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Class) {
|
||||
return 'class';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Interface) {
|
||||
return 'interface';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.ConstEnum) {
|
||||
return 'enum';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.RegularEnum) {
|
||||
return 'enum';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.Property) {
|
||||
return 'module-property';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.TypeAlias) {
|
||||
return 'type-alias';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.FunctionScopedVariable) {
|
||||
return 'var';
|
||||
}
|
||||
if(symbol.flags & ts.SymbolFlags.BlockScopedVariable) {
|
||||
return getBlockScopedVariableDocType(symbol);
|
||||
}
|
||||
|
||||
log.warn('getExportDocType(): Unknown symbol type', {
|
||||
symbolName: symbol.name,
|
||||
symbolType: symbol.flags,
|
||||
symbolTarget: symbol.target,
|
||||
file: ts.getSourceFileOfNode(symbol.declarations[0]).fileName
|
||||
});
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
function getBlockScopedVariableDocType(symbol) {
|
||||
|
||||
var node = symbol.valueDeclaration;
|
||||
while(node) {
|
||||
if ( node.flags & 0x2000 /* const */) {
|
||||
return 'const';
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return 'let';
|
||||
}
|
||||
};
|
20
docs/typescript-package/services/tsParser/getFileInfo.js
Normal file
20
docs/typescript-package/services/tsParser/getFileInfo.js
Normal file
@ -0,0 +1,20 @@
|
||||
var path = require('canonical-path');
|
||||
var ts = require('typescript');
|
||||
|
||||
module.exports = function getFileInfo(log) {
|
||||
|
||||
return function (symbol, basePath) {
|
||||
var fileName = ts.getSourceFileOfNode(symbol.declarations[0]).fileName;
|
||||
|
||||
var file = path.resolve(basePath, fileName);
|
||||
var fileInfo = {
|
||||
filePath: file,
|
||||
baseName: path.basename(file, path.extname(file)),
|
||||
extension: path.extname(file).replace(/^\./, ''),
|
||||
basePath: basePath,
|
||||
relativePath: fileName,
|
||||
projectRelativePath: fileName
|
||||
};
|
||||
return fileInfo;
|
||||
};
|
||||
};
|
74
docs/typescript-package/services/tsParser/index.js
Normal file
74
docs/typescript-package/services/tsParser/index.js
Normal file
@ -0,0 +1,74 @@
|
||||
var ts = require('typescript');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = function tsParser(createCompilerHost, log) {
|
||||
|
||||
return {
|
||||
|
||||
// These are the extension that we should consider when trying to load a module
|
||||
// During migration from Traceur, there is a mix of `.ts`, `.es6` and `.js` (atScript)
|
||||
// files in the project and the TypeScript compiler only looks for `.ts` files when trying
|
||||
// to load imports.
|
||||
extensions: ['.ts', '.js'],
|
||||
|
||||
// The options for the TS compiler
|
||||
options: {
|
||||
allowNonTsExtensions: true,
|
||||
charset: 'utf8'
|
||||
},
|
||||
|
||||
parse: function(fileNames, baseDir) {
|
||||
|
||||
// "Compile" a program from the given module filenames, to get hold of a
|
||||
// typeChecker that can be used to interrogate the modules, exports and so on.
|
||||
var host = createCompilerHost(this.options, baseDir, this.extensions);
|
||||
var program = ts.createProgram(fileNames, this.options, host);
|
||||
var typeChecker = program.getTypeChecker();
|
||||
|
||||
// Create an array of module symbols for each file we were given
|
||||
var moduleSymbols = [];
|
||||
fileNames.forEach(function(fileName) {
|
||||
var sourceFile = program.getSourceFile(fileName);
|
||||
|
||||
if (!sourceFile) {
|
||||
throw new Error('Invalid source file: ' + fileName);
|
||||
} else if (!sourceFile.symbol) {
|
||||
// Some files contain only a comment and no actual module code
|
||||
log.warn('No module code found in ' + fileName);
|
||||
} else {
|
||||
moduleSymbols.push(sourceFile.symbol);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
moduleSymbols.forEach(function(tsModule) {
|
||||
|
||||
// The type checker has a nice helper function that returns an array of Symbols
|
||||
// representing the exports for a given module
|
||||
tsModule.exportArray = typeChecker.getExportsOfModule(tsModule);
|
||||
|
||||
// Although 'star' imports (e.g. `export * from 'some/module';) get resolved automatically
|
||||
// by the compiler/binder, it seems that explicit imports (e.g. `export {SomeClass} from 'some/module'`)
|
||||
// do not so we have to do a little work.
|
||||
tsModule.exportArray.forEach(function(moduleExport) {
|
||||
if (moduleExport.flags & ts.SymbolFlags.Alias) {
|
||||
// To maintain the alias information (particularly the alias name)
|
||||
// we just attach the original "resolved" symbol to the alias symbol
|
||||
moduleExport.resolvedSymbol = typeChecker.getAliasedSymbol(moduleExport);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
moduleSymbols.typeChecker = typeChecker;
|
||||
|
||||
return {
|
||||
moduleSymbols: moduleSymbols,
|
||||
typeChecker: typeChecker,
|
||||
program: program,
|
||||
host: host
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
21
docs/typescript-package/services/tsParser/index.spec.js
Normal file
21
docs/typescript-package/services/tsParser/index.spec.js
Normal file
@ -0,0 +1,21 @@
|
||||
var mockPackage = require('../../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
var path = require('canonical-path');
|
||||
|
||||
describe('tsParser', function() {
|
||||
var dgeni, injector, parser;
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
parser = injector.get('tsParser');
|
||||
});
|
||||
|
||||
it("should parse a TS file", function() {
|
||||
var parseInfo = parser.parse(['testSrc.ts'], path.resolve(__dirname, '../../mocks/tsParser'));
|
||||
var tsModules = parseInfo.moduleSymbols;
|
||||
expect(tsModules.length).toEqual(1);
|
||||
expect(tsModules[0].exportArray.length).toEqual(3);
|
||||
expect(tsModules[0].exportArray.map(function(i) { return i.name; })).toEqual(['MyClass', 'myFn', 'x']);
|
||||
});
|
||||
});
|
1671
gulpfile.js
1671
gulpfile.js
File diff suppressed because it is too large
Load Diff
86
karma-dart-evalcache.js
Normal file
86
karma-dart-evalcache.js
Normal file
@ -0,0 +1,86 @@
|
||||
// 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;
|
||||
}
|
82
karma-dart.conf.js
Normal file
82
karma-dart.conf.js
Normal file
@ -0,0 +1,82 @@
|
||||
var sauceConf = require('./sauce.conf');
|
||||
|
||||
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: sauceConf.customLaunchers,
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877,
|
||||
|
||||
plugins: [
|
||||
require('karma-dart'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-sauce-launcher'),
|
||||
require('./karma-dart-evalcache')(packageSources)
|
||||
]
|
||||
});
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
var internalAngularReporter = require('./tools/karma/reporter.js');
|
||||
var sauceConf = require('./sauce.conf');
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
@ -17,14 +16,15 @@ module.exports = function(config) {
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
// zone-microtask must be included first as it contains a Promise monkey patch
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'modules/angular2/src/testing/shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
{pattern: 'node_modules/@reactivex/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
@ -33,64 +33,33 @@ module.exports = function(config) {
|
||||
|
||||
exclude: ['dist/js/dev/es5/**/e2e_test/**', 'dist/js/dev/es5/angular2/examples/**', 'dist/angular1_router.js'],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
customLaunchers: sauceConf.customLaunchers,
|
||||
|
||||
plugins: [
|
||||
'karma-jasmine',
|
||||
'karma-browserstack-launcher',
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-dart',
|
||||
internalAngularReporter
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'**/*.js': ['sourcemap']
|
||||
},
|
||||
|
||||
reporters: ['internal-angular'],
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
options: {
|
||||
'selenium-version': '2.48.2',
|
||||
'selenium-version': '2.47.1',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
}
|
||||
},
|
||||
|
||||
browserStack: {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 1,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
},
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
if (process.env.TRAVIS && process.env.MODE === 'saucelabs') {
|
||||
config.sauceLabs.build = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
// TODO(mlaval): remove once SauceLabs supports websockets.
|
||||
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||
console.log('>>>> setting socket.io transport to polling <<<<');
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
|
||||
if (process.env.MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
// TODO(mlaval): remove once SauceLabs supports websockets.
|
||||
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||
console.log('>>>> setting socket.io transport to polling <<<<');
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
};
|
||||
|
83
modules/angular1_router/build.js
vendored
83
modules/angular1_router/build.js
vendored
@ -4,47 +4,34 @@ var fs = require('fs');
|
||||
var ts = require('typescript');
|
||||
|
||||
var files = [
|
||||
'utils.ts',
|
||||
'lifecycle_annotations_impl.ts',
|
||||
'url_parser.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',
|
||||
'path_recognizer.ts',
|
||||
'route_config_impl.ts',
|
||||
'async_route_handler.ts',
|
||||
'sync_route_handler.ts',
|
||||
'route_recognizer.ts',
|
||||
'instruction.ts',
|
||||
'route_config_nomalizer.ts',
|
||||
'route_lifecycle_reflector.ts',
|
||||
'route_registry.ts',
|
||||
'router.ts'
|
||||
];
|
||||
|
||||
var PRELUDE = '(function(){\n';
|
||||
var POSTLUDE = '\n}());\n';
|
||||
var FACADES = fs.readFileSync(__dirname + '/lib/facades.es5', 'utf8');
|
||||
var DIRECTIVES = fs.readFileSync(__dirname + '/src/ng_outlet.ts', 'utf8');
|
||||
var moduleTemplate = fs.readFileSync(__dirname + '/src/module_template.js', 'utf8');
|
||||
|
||||
function main(modulesDirectory) {
|
||||
var angular1RouterModuleDirectory = modulesDirectory + '/angular1_router';
|
||||
|
||||
var facades = fs.readFileSync(
|
||||
angular1RouterModuleDirectory + '/lib/facades.es5', 'utf8');
|
||||
var directives = fs.readFileSync(
|
||||
angular1RouterModuleDirectory + '/src/ng_outlet.ts', 'utf8');
|
||||
var moduleTemplate = fs.readFileSync(
|
||||
angular1RouterModuleDirectory + '/src/module_template.js', 'utf8');
|
||||
|
||||
var dir = modulesDirectory + '/angular2/src/router/';
|
||||
function main() {
|
||||
var dir = __dirname + '/../angular2/src/router/';
|
||||
var sharedCode = files.reduce(function (prev, file) {
|
||||
return prev + transform(fs.readFileSync(dir + file, 'utf8'));
|
||||
}, '');
|
||||
|
||||
// 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;
|
||||
var out = moduleTemplate.replace('//{{FACADES}}', FACADES).replace('//{{SHARED_CODE}}', sharedCode);
|
||||
return PRELUDE + transform(DIRECTIVES) + out + POSTLUDE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -52,11 +39,7 @@ function main(modulesDirectory) {
|
||||
* sourcemap, and exported variable identifier name for the content.
|
||||
*/
|
||||
var IMPORT_RE = new RegExp("import \\{?([\\w\\n_, ]+)\\}? from '(.+)';?", 'g');
|
||||
var INJECT_RE = new RegExp("@Inject\\(ROUTER_PRIMARY_COMPONENT\\)", 'g');
|
||||
var INJECTABLE_RE = new RegExp("@Injectable\\(\\)", 'g');
|
||||
var REQUIRE_RE = new RegExp("require\\('(.*?)'\\);", 'g');
|
||||
function transform(contents) {
|
||||
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') {
|
||||
@ -64,15 +47,10 @@ function transform(contents) {
|
||||
}
|
||||
return match;
|
||||
});
|
||||
contents = ts.transpile(contents, {
|
||||
return 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) {
|
||||
@ -80,29 +58,10 @@ function isFacadeModule(modulePath) {
|
||||
modulePath === 'angular2/src/core/reflection/reflection';
|
||||
}
|
||||
|
||||
module.exports = function(modulesDirectory, outputDirectory) {
|
||||
if (!fs.existsSync(outputDirectory)) {
|
||||
fs.mkdirSync(outputDirectory);
|
||||
module.exports = function () {
|
||||
var dist = __dirname + '/../../dist';
|
||||
if (!fs.existsSync(dist)) {
|
||||
fs.mkdirSync(dist);
|
||||
}
|
||||
fs.writeFileSync(
|
||||
outputDirectory + '/angular_1_router.js', main(modulesDirectory));
|
||||
fs.writeFileSync(dist + '/angular_1_router.js', main());
|
||||
};
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
try {
|
||||
var args = process.argv;
|
||||
args.shift(); // node
|
||||
args.shift(); // scriptfile.js
|
||||
if (args.length < 2) {
|
||||
console.log("usage: $0 outFile path/to/modules");
|
||||
process.exit(1);
|
||||
}
|
||||
var outfile = args.shift();
|
||||
var directory = args.shift();
|
||||
fs.writeFileSync(outfile, main(directory));
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
<script src="../../dist/angular_1_router.js"></script>
|
||||
<script>
|
||||
angular.module('myApp', ['ngComponentRouter'])
|
||||
.controller('MyCtrl', ['$rootRouter', function ($rootRouter) {
|
||||
console.log($rootRouter);
|
||||
$rootRouter.navigateByUrl('/')
|
||||
.controller('MyCtrl', ['$router', function ($router) {
|
||||
console.log($router);
|
||||
$router.navigateByUrl('/')
|
||||
.then(console.log.bind(console, 'resolve'), console.log.bind(console, 'reject'));
|
||||
}]);
|
||||
</script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var browserProvidersConf = require('../../browser-providers.conf.js');
|
||||
var sauceConf = require('../../sauce.conf');
|
||||
|
||||
// This runs the tests for the router in Angular 1.x
|
||||
|
||||
@ -9,7 +9,6 @@ module.exports = function (config) {
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
files: [
|
||||
'../../node_modules/es6-shim/es6-shim.js',
|
||||
'../../node_modules/angular/angular.js',
|
||||
'../../node_modules/angular-animate/angular-animate.js',
|
||||
'../../node_modules/angular-mocks/angular-mocks.js',
|
||||
@ -21,9 +20,9 @@ module.exports = function (config) {
|
||||
'test/**/*_spec.js'
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
customLaunchers: sauceConf.customLaunchers,
|
||||
|
||||
browsers: ['Chrome']
|
||||
browsers: ['ChromeCanary']
|
||||
};
|
||||
|
||||
config.set(options);
|
||||
|
@ -173,14 +173,6 @@ var StringMapWrapper = {
|
||||
|
||||
var List = Array;
|
||||
var ListWrapper = {
|
||||
toJSON: function(l) {
|
||||
return JSON.stringify(l);
|
||||
},
|
||||
|
||||
clear: function (l) {
|
||||
l.length = 0;
|
||||
},
|
||||
|
||||
create: function () {
|
||||
return [];
|
||||
},
|
||||
@ -199,10 +191,6 @@ var ListWrapper = {
|
||||
return array[0];
|
||||
},
|
||||
|
||||
last: function(array) {
|
||||
return (array && array.length) > 0 ? array[array.length - 1] : null;
|
||||
},
|
||||
|
||||
map: function (l, fn) {
|
||||
return l.map(fn);
|
||||
},
|
||||
@ -251,10 +239,6 @@ var ListWrapper = {
|
||||
};
|
||||
|
||||
var StringWrapper = {
|
||||
charCodeAt: function(s, i) {
|
||||
return s.charCodeAt(i);
|
||||
},
|
||||
|
||||
equals: function (s1, s2) {
|
||||
return s1 === s2;
|
||||
},
|
||||
@ -263,10 +247,18 @@ var StringWrapper = {
|
||||
return s.split(re);
|
||||
},
|
||||
|
||||
substring: function(s, start, end) {
|
||||
return s.substr(start, end);
|
||||
},
|
||||
|
||||
replaceAll: function(s, from, replace) {
|
||||
return s.replace(from, replace);
|
||||
},
|
||||
|
||||
startsWith: function(s, start) {
|
||||
return s.substr(0, start.length) === start;
|
||||
},
|
||||
|
||||
replaceAllMapped: function(s, from, cb) {
|
||||
return s.replace(from, function(matches) {
|
||||
// Remove offset & string from the result array
|
||||
@ -292,9 +284,6 @@ var ObservableWrapper = {
|
||||
callNext: function(ob, val) {
|
||||
ob.fn(val);
|
||||
},
|
||||
callEmit: function(ob, val) {
|
||||
ob.fn(val);
|
||||
},
|
||||
|
||||
subscribe: function(ob, fn) {
|
||||
ob.fn = fn;
|
||||
@ -311,14 +300,8 @@ Location.prototype.subscribe = function () {
|
||||
//TODO: implement
|
||||
};
|
||||
Location.prototype.path = function () {
|
||||
return $location.url();
|
||||
return $location.path();
|
||||
};
|
||||
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;
|
||||
Location.prototype.go = function (url) {
|
||||
return $location.path(url);
|
||||
};
|
||||
|
103
modules/angular1_router/src/module_template.js
vendored
103
modules/angular1_router/src/module_template.js
vendored
@ -1,70 +1,26 @@
|
||||
|
||||
angular.module('ngComponentRouter').
|
||||
value('$route', null). // can be overloaded with ngRouteShim
|
||||
// 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', routerFactory]);
|
||||
|
||||
// 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) {
|
||||
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector) {
|
||||
|
||||
// When this file is processed, the line below is replaced with
|
||||
// the contents of `../lib/facades.es5`.
|
||||
//{{FACADES}}
|
||||
|
||||
var exports = {
|
||||
Injectable: function () {},
|
||||
OpaqueToken: function () {},
|
||||
Inject: function () {}
|
||||
};
|
||||
var routerRequire = function () {return exports;};
|
||||
var exports = {Injectable: function () {}};
|
||||
var require = 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 controller = getComponentConstructor(directiveName);
|
||||
return controller.$canActivate && function (next, prev) {
|
||||
return $injector.invoke(controller.$canActivate, null, {
|
||||
var factory = $$directiveIntrospector.getTypeByName(directiveName);
|
||||
return factory && factory.$canActivate && function (next, prev) {
|
||||
return $injector.invoke(factory.$canActivate, null, {
|
||||
$nextInstruction: next,
|
||||
$prevInstruction: prev
|
||||
});
|
||||
@ -75,41 +31,28 @@ function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRo
|
||||
// property in a route config
|
||||
exports.assertComponentExists = function () {};
|
||||
|
||||
angular.stringifyInstruction = function (instruction) {
|
||||
return instruction.toRootUrl();
|
||||
};
|
||||
angular.stringifyInstruction = exports.stringifyInstruction;
|
||||
|
||||
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 registry = new RouteRegistry();
|
||||
var location = new Location();
|
||||
|
||||
var router = new RootRouter(registry, location, $routerRootComponent);
|
||||
$rootScope.$watch(function () { return $location.url(); }, function (path) {
|
||||
$$directiveIntrospector(function (name, factory) {
|
||||
if (angular.isArray(factory.$routeConfig)) {
|
||||
factory.$routeConfig.forEach(function (config) {
|
||||
registry.config(name, config);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Because Angular 1 has no notion of a root component, we use an object with unique identity
|
||||
// to represent this.
|
||||
var ROOT_COMPONENT_OBJECT = new Object();
|
||||
|
||||
var router = new RootRouter(registry, location, ROOT_COMPONENT_OBJECT);
|
||||
$rootScope.$watch(function () { return $location.path(); }, function (path) {
|
||||
if (router.lastNavigationAttempt !== path) {
|
||||
router.navigateByUrl(path);
|
||||
}
|
||||
|
@ -1,6 +1,51 @@
|
||||
///<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
|
||||
@ -16,8 +61,8 @@
|
||||
*
|
||||
* The value for the `ngOutlet` attribute is optional.
|
||||
*/
|
||||
function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
let rootRouter = $rootRouter;
|
||||
function ngOutletDirective($animate, $q: ng.IQService, $router) {
|
||||
let rootRouter = $router;
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
@ -61,21 +106,21 @@ function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
let next = $q.when(true);
|
||||
let previousInstruction = this.currentInstruction;
|
||||
this.currentInstruction = instruction;
|
||||
if (this.currentController && this.currentController.$routerOnReuse) {
|
||||
if (this.currentController && this.currentController.$onReuse) {
|
||||
next = $q.when(
|
||||
this.currentController.$routerOnReuse(this.currentInstruction, previousInstruction));
|
||||
this.currentController.$onReuse(this.currentInstruction, previousInstruction));
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
routerCanReuse(nextInstruction) {
|
||||
canReuse(nextInstruction) {
|
||||
let result;
|
||||
if (!this.currentInstruction ||
|
||||
this.currentInstruction.componentType !== nextInstruction.componentType) {
|
||||
result = false;
|
||||
} else if (this.currentController && this.currentController.$routerCanReuse) {
|
||||
result = this.currentController.$routerCanReuse(nextInstruction, this.currentInstruction);
|
||||
} else if (this.currentController && this.currentController.$canReuse) {
|
||||
result = this.currentController.$canReuse(nextInstruction, this.currentInstruction);
|
||||
} else {
|
||||
result = nextInstruction === this.currentInstruction ||
|
||||
angular.equals(nextInstruction.params, this.currentInstruction.params);
|
||||
@ -83,24 +128,24 @@ function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
return $q.when(result);
|
||||
}
|
||||
|
||||
routerCanDeactivate(instruction) {
|
||||
if (this.currentController && this.currentController.$routerCanDeactivate) {
|
||||
canDeactivate(instruction) {
|
||||
if (this.currentController && this.currentController.$canDeactivate) {
|
||||
return $q.when(
|
||||
this.currentController.$routerCanDeactivate(instruction, this.currentInstruction));
|
||||
this.currentController.$canDeactivate(instruction, this.currentInstruction));
|
||||
}
|
||||
return $q.when(true);
|
||||
}
|
||||
|
||||
deactivate(instruction) {
|
||||
if (this.currentController && this.currentController.$routerOnDeactivate) {
|
||||
if (this.currentController && this.currentController.$onDeactivate) {
|
||||
return $q.when(
|
||||
this.currentController.$routerOnDeactivate(instruction, this.currentInstruction));
|
||||
this.currentController.$onDeactivate(instruction, this.currentInstruction));
|
||||
}
|
||||
return $q.when();
|
||||
}
|
||||
|
||||
activate(instruction) {
|
||||
this.previousInstruction = this.currentInstruction;
|
||||
let previousInstruction = this.currentInstruction;
|
||||
this.currentInstruction = instruction;
|
||||
|
||||
let componentName = this.controller.$$componentName = instruction.componentType;
|
||||
@ -109,14 +154,11 @@ function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
throw new Error('Component is not a string for ' + instruction.urlPath);
|
||||
}
|
||||
|
||||
this.controller.$$template = '<' + dashCase(componentName) + ' $router="::$$router"></' +
|
||||
dashCase(componentName) + '>';
|
||||
this.controller.$$routeParams = instruction.params;
|
||||
this.controller.$$template = '<div ' + dashCase(componentName) + '></div>';
|
||||
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);
|
||||
@ -125,7 +167,15 @@ function ngOutletDirective($animate, $q: ng.IQService, $rootRouter) {
|
||||
|
||||
this.currentElement = clone;
|
||||
this.currentScope = newScope;
|
||||
return this.deferredActivation.promise;
|
||||
|
||||
// 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.$onActivate) {
|
||||
return this.currentController.$onActivate(instruction, previousInstruction);
|
||||
}
|
||||
return $q.when();
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,32 +198,21 @@ function ngOutletFillContentDirective($compile) {
|
||||
link: (scope, element, attrs, ctrl) => {
|
||||
let template = ctrl.$$template;
|
||||
element.html(template);
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
let link = $compile(element.contents());
|
||||
link(scope);
|
||||
|
||||
// TODO: move to primary directive
|
||||
let componentInstance = scope[ctrl.$$componentName];
|
||||
if (componentInstance) {
|
||||
ctrl.$$currentComponent = componentInstance;
|
||||
|
||||
|
||||
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));
|
||||
componentInstance.$router = ctrl.$$router;
|
||||
componentInstance.$routeParams = ctrl.$$routeParams;
|
||||
}
|
||||
promise.then(outlet.deferredActivation.resolve, outlet.deferredActivation.reject);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name ngLink
|
||||
* @description
|
||||
@ -186,8 +225,8 @@ function routerTriggerDirective($q) {
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngComponentRouter'])
|
||||
* .controller('AppController', ['$rootRouter', function($rootRouter) {
|
||||
* $rootRouter.config({ path: '/user/:id', component: 'user' });
|
||||
* .controller('AppController', ['$router', function($router) {
|
||||
* $router.config({ path: '/user/:id', component: 'user' });
|
||||
* this.user = { name: 'Brian', id: 123 };
|
||||
* });
|
||||
* ```
|
||||
@ -198,32 +237,23 @@ function routerTriggerDirective($q) {
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
function ngLinkDirective($rootRouter, $parse) {
|
||||
function ngLinkDirective($router, $parse) {
|
||||
let rootRouter = $router;
|
||||
|
||||
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 navigationInstruction = null;
|
||||
let instruction = null;
|
||||
let link = attrs.ngLink || '';
|
||||
|
||||
function getLink(params) {
|
||||
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);
|
||||
instruction = router.generate(params);
|
||||
return './' + angular.stringifyInstruction(instruction);
|
||||
}
|
||||
|
||||
let routeParamsGetter = $parse(link);
|
||||
@ -237,11 +267,11 @@ function ngLinkDirective($rootRouter, $parse) {
|
||||
}
|
||||
|
||||
element.on('click', event => {
|
||||
if (event.which !== 1 || !navigationInstruction) {
|
||||
if (event.which !== 1 || !instruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootRouter.navigateByInstruction(navigationInstruction);
|
||||
$router.navigateByInstruction(instruction);
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
@ -255,7 +285,13 @@ function dashCase(str: string): string {
|
||||
* A module for adding new a routing system Angular 1.
|
||||
*/
|
||||
angular.module('ngComponentRouter', [])
|
||||
.directive('ngOutlet', ['$animate', '$q', '$rootRouter', ngOutletDirective])
|
||||
.directive('ngOutlet', ['$compile', ngOutletFillContentDirective])
|
||||
.directive('ngLink', ['$rootRouter', '$parse', ngLinkDirective])
|
||||
.directive('$router', ['$q', routerTriggerDirective]);
|
||||
.directive('ngOutlet', ngOutletDirective)
|
||||
.directive('ngOutlet', ngOutletFillContentDirective)
|
||||
.directive('ngLink', ngLinkDirective);
|
||||
|
||||
/*
|
||||
* A module for inspecting controller constructors
|
||||
*/
|
||||
angular.module('ng')
|
||||
.provider('$$directiveIntrospector', DirectiveIntrospectorProvider)
|
||||
.config(compilerProviderDecorator);
|
||||
|
56
modules/angular1_router/src/ng_route_shim.js
vendored
56
modules/angular1_router/src/ng_route_shim.js
vendored
@ -1,4 +1,4 @@
|
||||
/** @license Copyright 2014-2016 Google, Inc. http://github.com/angular/angular/LICENSE */
|
||||
/** @license Copyright 2014-2015 Google, Inc. http://github.com/angular/angular/LICENSE */
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
@ -24,12 +24,12 @@
|
||||
.directive('a', anchorLinkDirective)
|
||||
|
||||
// Connects the legacy $routeProvider config shim to Component Router's config.
|
||||
.run(['$route', '$rootRouter', function ($route, $rootRouter) {
|
||||
.run(['$route', '$router', function ($route, $router) {
|
||||
$route.$$subscribe(function (routeDefinition) {
|
||||
if (!angular.isArray(routeDefinition)) {
|
||||
routeDefinition = [routeDefinition];
|
||||
}
|
||||
$rootRouter.config(routeDefinition);
|
||||
$router.config(routeDefinition);
|
||||
});
|
||||
}]);
|
||||
|
||||
@ -110,47 +110,59 @@
|
||||
routeMap[path] = routeCopy;
|
||||
|
||||
if (route.redirectTo) {
|
||||
routeDefinition.redirectTo = [routeMap[route.redirectTo].name];
|
||||
routeDefinition.redirectTo = route.redirectTo;
|
||||
} else {
|
||||
if (routeCopy.controller && !routeCopy.controllerAs) {
|
||||
console.warn('Route for "' + path + '" should use "controllerAs".');
|
||||
}
|
||||
|
||||
var componentName = routeObjToRouteName(routeCopy, path);
|
||||
var directiveName = routeObjToRouteName(routeCopy, path);
|
||||
|
||||
if (!componentName) {
|
||||
if (!directiveName) {
|
||||
throw new Error('Could not determine a name for route "' + path + '".');
|
||||
}
|
||||
|
||||
routeDefinition.component = componentName;
|
||||
routeDefinition.name = route.name || upperCase(componentName);
|
||||
routeDefinition.component = directiveName;
|
||||
routeDefinition.as = upperCase(directiveName);
|
||||
|
||||
var directiveController = routeCopy.controller;
|
||||
|
||||
var componentDefinition = {
|
||||
var directiveDefinition = {
|
||||
scope: false,
|
||||
controller: directiveController,
|
||||
controllerAs: routeCopy.controllerAs
|
||||
|
||||
controllerAs: routeCopy.controllerAs,
|
||||
templateUrl: routeCopy.templateUrl,
|
||||
template: routeCopy.template
|
||||
};
|
||||
if (routeCopy.templateUrl) componentDefinition.templateUrl = routeCopy.templateUrl;
|
||||
if (routeCopy.template) componentDefinition.template = routeCopy.template;
|
||||
|
||||
var directiveFactory = function () {
|
||||
return directiveDefinition;
|
||||
};
|
||||
|
||||
// if we have route resolve options, prepare a wrapper controller
|
||||
if (directiveController && routeCopy.resolve) {
|
||||
var originalController = directiveController;
|
||||
var resolvedLocals = {};
|
||||
|
||||
componentDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
|
||||
directiveDefinition.controller = ['$injector', '$scope', function ($injector, $scope) {
|
||||
var locals = angular.extend({
|
||||
$scope: $scope
|
||||
}, resolvedLocals);
|
||||
|
||||
return $injector.instantiate(originalController, locals);
|
||||
var ctrl = $injector.instantiate(originalController, locals);
|
||||
|
||||
if (routeCopy.controllerAs) {
|
||||
locals.$scope[routeCopy.controllerAs] = ctrl;
|
||||
}
|
||||
|
||||
return ctrl;
|
||||
}];
|
||||
|
||||
// we take care of controllerAs in the directive controller wrapper
|
||||
delete directiveDefinition.controllerAs;
|
||||
|
||||
// we resolve the locals in a canActivate block
|
||||
componentDefinition.controller.$canActivate = function() {
|
||||
directiveFactory.$canActivate = function() {
|
||||
var locals = angular.extend({}, routeCopy.resolve);
|
||||
|
||||
angular.forEach(locals, function(value, key) {
|
||||
@ -167,7 +179,7 @@
|
||||
}
|
||||
|
||||
// register the dynamically created directive
|
||||
$compileProvider.component(componentName, componentDefinition);
|
||||
$compileProvider.directive(directiveName, directiveFactory);
|
||||
}
|
||||
if (subscriptionFn) {
|
||||
subscriptionFn(routeDefinition);
|
||||
@ -241,12 +253,12 @@
|
||||
|
||||
}
|
||||
|
||||
function $routeParamsFactory($rootRouter, $rootScope) {
|
||||
function $routeParamsFactory($router, $rootScope) {
|
||||
// the identity of this object cannot change
|
||||
var paramsObj = {};
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function () {
|
||||
var newParams = $rootRouter.currentInstruction && $rootRouter.currentInstruction.component.params;
|
||||
var newParams = $router._currentInstruction && $router._currentInstruction.component.params;
|
||||
|
||||
angular.forEach(paramsObj, function (val, name) {
|
||||
delete paramsObj[name];
|
||||
@ -262,7 +274,7 @@
|
||||
/**
|
||||
* Allows normal anchor links to kick off routing.
|
||||
*/
|
||||
function anchorLinkDirective($rootRouter) {
|
||||
function anchorLinkDirective($router) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function (scope, element) {
|
||||
@ -281,8 +293,8 @@
|
||||
}
|
||||
|
||||
var href = element.attr(hrefAttrName);
|
||||
if (href && $rootRouter.recognize(href)) {
|
||||
$rootRouter.navigateByUrl(href);
|
||||
if (href && $router.recognize(href)) {
|
||||
$router.navigateByUrl(href);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
38
modules/angular1_router/test/directive_introspector_spec.js
vendored
Normal file
38
modules/angular1_router/test/directive_introspector_spec.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
'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,
|
||||
$rootRouter,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -17,18 +17,15 @@ describe('ngOutlet animations', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$rootRouter_) {
|
||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
|
||||
$animate = _$animate_;
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
|
||||
$routerOnActivate: function(next) {
|
||||
this.$routeParams = next.params;
|
||||
}
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
});
|
||||
});
|
||||
|
||||
@ -41,11 +38,11 @@ describe('ngOutlet animations', function () {
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
@ -54,7 +51,7 @@ describe('ngOutlet animations', function () {
|
||||
expect(item.event).toBe('enter');
|
||||
|
||||
// navigate to pete
|
||||
$rootRouter.navigateByUrl('/user/pete');
|
||||
$router.navigateByUrl('/user/pete');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello pete');
|
||||
|
||||
@ -73,7 +70,7 @@ describe('ngOutlet animations', function () {
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$routerOnActivate', '$routerOnDeactivate', '$routerOnReuse', '$routerCanReuse', '$routerCanDeactivate'].forEach(function (hookName) {
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ describe('Navigation lifecycle', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$rootRouter,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -14,10 +14,10 @@ describe('Navigation lifecycle', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerComponent('oneCmp', {
|
||||
@ -35,15 +35,15 @@ describe('Navigation lifecycle', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
template: '<p>hello</p>',
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -53,15 +53,15 @@ describe('Navigation lifecycle', function () {
|
||||
it('should pass instruction into the activate hook of a controller', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('userCmp', {
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
|
||||
@ -72,18 +72,18 @@ describe('Navigation lifecycle', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
var activate = registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'oneCmp' },
|
||||
{ path: '/post/:id', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
|
||||
instructionFor('oneCmp'));
|
||||
@ -98,12 +98,12 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user');
|
||||
$router.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(injectedScope).toBeDefined();
|
||||
@ -113,18 +113,18 @@ describe('Navigation lifecycle', function () {
|
||||
it('should run the deactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('deactivate');
|
||||
registerComponent('deactivateCmp', {
|
||||
$routerOnDeactivate: spy
|
||||
$onDeactivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
@ -133,18 +133,18 @@ describe('Navigation lifecycle', function () {
|
||||
it('should pass instructions into the deactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('deactivate');
|
||||
registerComponent('deactivateCmp', {
|
||||
$routerOnDeactivate: spy
|
||||
$onDeactivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
@ -155,32 +155,32 @@ describe('Navigation lifecycle', function () {
|
||||
var log = [];
|
||||
|
||||
registerComponent('activateCmp', {
|
||||
$routerOnActivate: function () {
|
||||
$onActivate: function () {
|
||||
log.push('activate');
|
||||
}
|
||||
});
|
||||
|
||||
registerComponent('deactivateCmp', {
|
||||
$routerOnDeactivate: function () {
|
||||
$onDeactivate: function () {
|
||||
log.push('deactivate');
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'deactivateCmp' },
|
||||
{ path: '/b', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(log).toEqual(['deactivate', 'activate']);
|
||||
});
|
||||
|
||||
it('should reuse a component when the routerCanReuse hook returns true', function () {
|
||||
it('should reuse a component when the canReuse hook returns true', function () {
|
||||
var log = [];
|
||||
var cmpInstanceCount = 0;
|
||||
|
||||
@ -195,27 +195,27 @@ describe('Navigation lifecycle', function () {
|
||||
{path: '/b', component: 'twoCmp'}
|
||||
],
|
||||
controller: ReuseCmp,
|
||||
$routerCanReuse: function () {
|
||||
$canReuse: function () {
|
||||
return true;
|
||||
},
|
||||
$routerOnReuse: function (next, prev) {
|
||||
$onReuse: function (next, prev) {
|
||||
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/on-reuse/1/a');
|
||||
$router.navigateByUrl('/on-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$rootRouter.navigateByUrl('/on-reuse/2/b');
|
||||
$router.navigateByUrl('/on-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
@ -223,7 +223,7 @@ describe('Navigation lifecycle', function () {
|
||||
});
|
||||
|
||||
|
||||
it('should not reuse a component when the routerCanReuse hook returns false', function () {
|
||||
it('should not reuse a component when the canReuse hook returns false', function () {
|
||||
var log = [];
|
||||
var cmpInstanceCount = 0;
|
||||
|
||||
@ -237,27 +237,27 @@ describe('Navigation lifecycle', function () {
|
||||
{path: '/b', component: 'twoCmp'}
|
||||
],
|
||||
controller: NeverReuseCmp,
|
||||
$routerCanReuse: function () {
|
||||
$canReuse: function () {
|
||||
return false;
|
||||
},
|
||||
$routerOnReuse: function (next, prev) {
|
||||
$onReuse: function (next, prev) {
|
||||
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
|
||||
{ path: '/two', component: 'twoCmp', name: 'Two'}
|
||||
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/never-reuse/1/a');
|
||||
$router.navigateByUrl('/never-reuse/1/a');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('outer { reuse {one} }');
|
||||
|
||||
$rootRouter.navigateByUrl('/never-reuse/2/b');
|
||||
$router.navigateByUrl('/never-reuse/2/b');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual([]);
|
||||
expect(cmpInstanceCount).toBe(2);
|
||||
@ -271,15 +271,15 @@ describe('Navigation lifecycle', function () {
|
||||
var spy = jasmine.createSpy('activate');
|
||||
registerComponent('activateCmp', {
|
||||
$canActivate: canActivateSpy,
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
@ -293,15 +293,15 @@ describe('Navigation lifecycle', function () {
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$canActivate: canActivateSpy,
|
||||
$routerOnActivate: activateSpy
|
||||
$onActivate: activateSpy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(canActivateSpy).toHaveBeenCalled();
|
||||
@ -317,15 +317,15 @@ describe('Navigation lifecycle', function () {
|
||||
$canActivate: function () {
|
||||
return $q.when(true);
|
||||
},
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -341,64 +341,64 @@ describe('Navigation lifecycle', function () {
|
||||
|
||||
spy.$inject = ['$nextInstruction', '$http'];
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
var args = spy.calls.mostRecent().args;
|
||||
expect(args[0].params).toEqual(jasmine.objectContaining({name: 'brian'}));
|
||||
expect(args[0].params).toEqual({name: 'brian'});
|
||||
expect(args[1]).toBe($http);
|
||||
}));
|
||||
|
||||
|
||||
it('should not navigate when routerCanDeactivate returns false', function () {
|
||||
it('should not navigate when canDeactivate returns false', function () {
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$routerCanDeactivate: function () {
|
||||
$canDeactivate: function () {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
});
|
||||
|
||||
|
||||
it('should navigate when routerCanDeactivate returns true', function () {
|
||||
it('should navigate when canDeactivate returns true', function () {
|
||||
registerComponent('activateCmp', {
|
||||
template: 'hi',
|
||||
$routerCanDeactivate: function () {
|
||||
$canDeactivate: function () {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' },
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { hi }');
|
||||
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('outer { one }');
|
||||
});
|
||||
@ -411,15 +411,15 @@ describe('Navigation lifecycle', function () {
|
||||
$canActivate: function () {
|
||||
return true;
|
||||
},
|
||||
$routerOnActivate: spy
|
||||
$onActivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/a', component: 'activateCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a');
|
||||
$router.navigateByUrl('/a');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -427,21 +427,21 @@ describe('Navigation lifecycle', function () {
|
||||
});
|
||||
|
||||
|
||||
it('should pass instructions into the routerCanDeactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('routerCanDeactivate').and.returnValue(true);
|
||||
it('should pass instructions into the canDeactivate hook of controllers', function () {
|
||||
var spy = jasmine.createSpy('canDeactivate').and.returnValue(true);
|
||||
registerComponent('deactivateCmp', {
|
||||
$routerCanDeactivate: spy
|
||||
$canDeactivate: spy
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||
{ path: '/post/:id', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
$rootRouter.navigateByUrl('/post/123');
|
||||
$router.navigateByUrl('/post/123');
|
||||
$rootScope.$digest();
|
||||
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||
instructionFor('deactivateCmp'));
|
||||
@ -451,7 +451,7 @@ describe('Navigation lifecycle', function () {
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$routerOnActivate', '$routerOnDeactivate', '$routerOnReuse', '$routerCanReuse', '$routerCanDeactivate'].forEach(function (hookName) {
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
@ -466,10 +466,10 @@ describe('Navigation lifecycle', function () {
|
||||
}
|
||||
|
||||
if (options.$canActivate) {
|
||||
controller.$canActivate = options.$canActivate;
|
||||
factory.$canActivate = options.$canActivate;
|
||||
}
|
||||
if (options.$routeConfig) {
|
||||
controller.$routeConfig = options.$routeConfig;
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
|
@ -5,7 +5,7 @@ describe('navigation', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$rootRouter,
|
||||
$router,
|
||||
$compileProvider;
|
||||
|
||||
beforeEach(function () {
|
||||
@ -15,79 +15,49 @@ describe('navigation', function () {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
registerDirective('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>',
|
||||
$routerOnActivate: function(next) {
|
||||
this.$routeParams = next.params;
|
||||
}
|
||||
registerComponent('userCmp', {
|
||||
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||
});
|
||||
registerDirective('oneCmp', {
|
||||
registerComponent('oneCmp', {
|
||||
template: '<div>{{oneCmp.number}}</div>',
|
||||
controller: function () {this.number = 'one'}
|
||||
});
|
||||
registerDirective('twoCmp', {
|
||||
registerComponent('twoCmp', {
|
||||
template: '<div>{{twoCmp.number}}</div>',
|
||||
controller: function () {this.number = 'two'}
|
||||
});
|
||||
registerComponent('threeCmp', {
|
||||
template: '<div>{{$ctrl.number}}</div>',
|
||||
controller: function () {this.number = 'three'}
|
||||
});
|
||||
registerComponent('getParams', {
|
||||
template: '<div>{{$ctrl.params.x}}</div>',
|
||||
controller: function () {
|
||||
this.$routerOnActivate = function(next) {
|
||||
this.params = next.params;
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('should work in a simple case', function () {
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
});
|
||||
|
||||
|
||||
it('should work with components created by the `mod.component()` helper', function () {
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.config([
|
||||
{ path: '/', component: 'threeCmp' }
|
||||
]);
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('three');
|
||||
});
|
||||
|
||||
|
||||
it('should navigate between components with different parameters', function () {
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/igor');
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
@ -98,7 +68,7 @@ describe('navigation', function () {
|
||||
function ParentController() {
|
||||
instanceCount += 1;
|
||||
}
|
||||
registerDirective('parentCmp', {
|
||||
registerComponent('parentCmp', {
|
||||
template: 'parent { <ng-outlet></ng-outlet> }',
|
||||
$routeConfig: [
|
||||
{ path: '/user/:name', component: 'userCmp' }
|
||||
@ -106,17 +76,17 @@ describe('navigation', function () {
|
||||
controller: ParentController
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/parent/...', component: 'parentCmp' }
|
||||
]);
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/parent/user/brian');
|
||||
$router.navigateByUrl('/parent/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello brian }');
|
||||
|
||||
$rootRouter.navigateByUrl('/parent/user/igor');
|
||||
$router.navigateByUrl('/parent/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(instanceCount).toBe(1);
|
||||
expect(elt.text()).toBe('parent { hello igor }');
|
||||
@ -124,25 +94,6 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should work with nested outlets', function () {
|
||||
registerDirective('childCmp', {
|
||||
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/b', component: 'oneCmp' }
|
||||
]
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
{ path: '/a/...', component: 'childCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/a/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('outer { inner { one } }');
|
||||
});
|
||||
|
||||
it('should work when parent route has empty path', inject(function ($location) {
|
||||
registerComponent('childCmp', {
|
||||
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
@ -150,77 +101,62 @@ describe('navigation', function () {
|
||||
]
|
||||
});
|
||||
|
||||
$rootRouter.config([
|
||||
{ path: '/...', component: 'childCmp' }
|
||||
$router.config([
|
||||
{ path: '/a/...', component: 'childCmp' }
|
||||
]);
|
||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/b');
|
||||
$router.navigateByUrl('/a/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('outer { inner { one } }');
|
||||
expect($location.path()).toBe('/b');
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should work with recursive nested outlets', function () {
|
||||
registerDirective('recurCmp', {
|
||||
// TODO: fix this
|
||||
xit('should work with recursive nested outlets', function () {
|
||||
registerComponent('recurCmp', {
|
||||
template: '<div>recur { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/recur', component: 'recurCmp' },
|
||||
{ path: '/end', component: 'oneCmp' }
|
||||
]});
|
||||
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/recur', component: 'recurCmp' },
|
||||
{ path: '/', component: 'oneCmp' }
|
||||
]);
|
||||
|
||||
compile('<div>root { <div ng-outlet></div> }</div>');
|
||||
$rootRouter.navigateByUrl('/recur/recur/end');
|
||||
$router.navigateByUrl('/recur/recur/end');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root { one }');
|
||||
});
|
||||
|
||||
|
||||
it('should change location path', inject(function ($location) {
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user');
|
||||
$router.navigateByUrl('/user');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
}));
|
||||
|
||||
|
||||
it('should pass through query terms to the location', inject(function ($location) {
|
||||
$rootRouter.config([
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user?x=y');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
expect($location.search()).toEqual({ x: 'y'});
|
||||
}));
|
||||
|
||||
|
||||
it('should change location to the canonical route', inject(function ($location) {
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.config([
|
||||
{ path: '/', redirectTo: ['/User'] },
|
||||
{ path: '/user', component: 'userCmp', name: 'User' }
|
||||
$router.config([
|
||||
{ path: '/', redirectTo: '/user' },
|
||||
{ path: '/user', component: 'userCmp' }
|
||||
]);
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/user');
|
||||
@ -228,29 +164,30 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should change location to the canonical route with nested components', inject(function ($location) {
|
||||
registerDirective('childRouter', {
|
||||
registerComponent('childRouter', {
|
||||
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||
$routeConfig: [
|
||||
{ path: '/new-child', component: 'oneCmp', name: 'NewChild'},
|
||||
{ path: '/new-child-two', component: 'twoCmp', name: 'NewChildTwo'}
|
||||
{ path: '/old-child', redirectTo: '/new-child' },
|
||||
{ path: '/new-child', component: 'oneCmp'},
|
||||
{ path: '/old-child-two', redirectTo: '/new-child-two' },
|
||||
{ path: '/new-child-two', component: 'twoCmp'}
|
||||
]
|
||||
});
|
||||
|
||||
$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' }
|
||||
$router.config([
|
||||
{ path: '/old-parent', redirectTo: '/new-parent' },
|
||||
{ path: '/new-parent/...', component: 'childRouter' }
|
||||
]);
|
||||
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/old-parent/old-child');
|
||||
$router.navigateByUrl('/old-parent/old-child');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child');
|
||||
expect(elt.text()).toBe('inner { one }');
|
||||
|
||||
$rootRouter.navigateByUrl('/old-parent/old-child-two');
|
||||
$router.navigateByUrl('/old-parent/old-child-two');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($location.path()).toBe('/new-parent/new-child-two');
|
||||
@ -259,7 +196,7 @@ describe('navigation', function () {
|
||||
|
||||
|
||||
it('should navigate when the location path changes', inject(function ($location) {
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/one', component: 'oneCmp' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
@ -271,42 +208,36 @@ describe('navigation', function () {
|
||||
}));
|
||||
|
||||
|
||||
it('should navigate when the location query changes', inject(function ($location) {
|
||||
$rootRouter.config([
|
||||
{ path: '/get/params', component: 'getParams' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$location.url('/get/params?x=y');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('y');
|
||||
}));
|
||||
|
||||
|
||||
it('should expose a "navigating" property on $rootRouter', inject(function ($q) {
|
||||
it('should expose a "navigating" property on $router', inject(function ($q) {
|
||||
var defer;
|
||||
registerDirective('pendingActivate', {
|
||||
registerComponent('pendingActivate', {
|
||||
$canActivate: function () {
|
||||
defer = $q.defer();
|
||||
return defer.promise;
|
||||
}
|
||||
});
|
||||
$rootRouter.config([
|
||||
$router.config([
|
||||
{ path: '/pending-activate', component: 'pendingActivate' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$rootRouter.navigateByUrl('/pending-activate');
|
||||
$router.navigateByUrl('/pending-activate');
|
||||
$rootScope.$digest();
|
||||
expect($rootRouter.navigating).toBe(true);
|
||||
expect($router.navigating).toBe(true);
|
||||
defer.resolve();
|
||||
$rootScope.$digest();
|
||||
expect($rootRouter.navigating).toBe(false);
|
||||
expect($router.navigating).toBe(false);
|
||||
}));
|
||||
|
||||
function registerDirective(name, options) {
|
||||
var controller = getController(options);
|
||||
function registerComponent(name, options) {
|
||||
var controller = options.controller || function () {};
|
||||
|
||||
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
template: options.template || '',
|
||||
@ -314,18 +245,15 @@ describe('navigation', function () {
|
||||
controller: controller
|
||||
};
|
||||
}
|
||||
applyStaticProperties(controller, options);
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function registerComponent(name, options) {
|
||||
|
||||
var definition = {
|
||||
template: options.template || '',
|
||||
controller: getController(options),
|
||||
if (options.$canActivate) {
|
||||
factory.$canActivate = options.$canActivate;
|
||||
}
|
||||
applyStaticProperties(definition.controller, options);
|
||||
$compileProvider.component(name, definition);
|
||||
if (options.$routeConfig) {
|
||||
factory.$routeConfig = options.$routeConfig;
|
||||
}
|
||||
|
||||
$compileProvider.directive(name, factory);
|
||||
}
|
||||
|
||||
function compile(template) {
|
||||
@ -333,27 +261,4 @@ describe('navigation', function () {
|
||||
$rootScope.$digest();
|
||||
return elt;
|
||||
}
|
||||
|
||||
function getController(options) {
|
||||
var controller = options.controller || function () {};
|
||||
[
|
||||
'$routerOnActivate', '$routerOnDeactivate',
|
||||
'$routerOnReuse', '$routerCanReuse',
|
||||
'$routerCanDeactivate'
|
||||
].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
function applyStaticProperties(target, options) {
|
||||
['$canActivate', '$routeConfig'].forEach(function(property) {
|
||||
if (options[property]) {
|
||||
target[property] = options[property];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,217 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('router', function () {
|
||||
|
||||
var elt, testMod;
|
||||
beforeEach(function () {
|
||||
testMod = angular.module('testMod', ['ngComponentRouter'])
|
||||
.value('$routerRootComponent', 'app');
|
||||
});
|
||||
|
||||
it('should work with a provided root component', function() {
|
||||
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home'
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', component: 'homeCmp' }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
inject(function($location, $rootScope) {
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home');
|
||||
});
|
||||
});
|
||||
|
||||
it('should bind the component to the current router', function() {
|
||||
var router;
|
||||
registerComponent('homeCmp', {
|
||||
bindings: { $router: '=' },
|
||||
controller: function($scope, $element) {
|
||||
this.$routerOnActivate = function() {
|
||||
router = this.$router;
|
||||
};
|
||||
},
|
||||
template: 'Home'
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', component: 'homeCmp' }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
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', function() {
|
||||
registerComponent('homeCmp', {
|
||||
template: 'Home ({{$ctrl.isAdmin}})',
|
||||
$routerOnActivate: function(next, prev) {
|
||||
this.isAdmin = next.routeData.data.isAdmin;
|
||||
}
|
||||
});
|
||||
|
||||
registerComponent('app', {
|
||||
template: '<div ng-outlet></div>',
|
||||
$routeConfig: [
|
||||
{ path: '/', loader: function($q) { return $q.when('homeCmp'); }, data: { isAdmin: true } }
|
||||
]
|
||||
});
|
||||
|
||||
module('testMod');
|
||||
compileApp();
|
||||
|
||||
inject(function($location, $rootScope) {
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('Home (true)');
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
controller: getController(options),
|
||||
};
|
||||
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 compileApp() {
|
||||
inject(function($compile, $rootScope) {
|
||||
elt = $compile('<div><app></app</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
return elt;
|
||||
}
|
||||
|
||||
function getController(options) {
|
||||
var controller = options.controller || function () {};
|
||||
[
|
||||
'$routerOnActivate', '$routerOnDeactivate',
|
||||
'$routerOnReuse', '$routerCanReuse',
|
||||
'$routerCanDeactivate'
|
||||
].forEach(function (hookName) {
|
||||
if (options[hookName]) {
|
||||
controller.prototype[hookName] = options[hookName];
|
||||
}
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
function applyStaticProperties(target, options) {
|
||||
['$canActivate', '$routeConfig'].forEach(function(property) {
|
||||
if (options[property]) {
|
||||
target[property] = options[property];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -5,7 +5,7 @@ describe('ngRoute shim', function () {
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$rootRouter,
|
||||
$router,
|
||||
$compileProvider,
|
||||
$routeProvider;
|
||||
|
||||
@ -18,10 +18,10 @@ describe('ngRoute shim', function () {
|
||||
$routeProvider = _$routeProvider_;
|
||||
});
|
||||
|
||||
inject(function (_$compile_, _$rootScope_, _$rootRouter_) {
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$router = _$router_;
|
||||
});
|
||||
});
|
||||
|
||||
@ -36,7 +36,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('one');
|
||||
@ -55,7 +55,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {one}');
|
||||
}));
|
||||
@ -76,7 +76,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.text()).toBe('value: 42');
|
||||
@ -94,11 +94,11 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/brian');
|
||||
$router.navigateByUrl('/user/brian');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello brian');
|
||||
|
||||
$rootRouter.navigateByUrl('/user/igor');
|
||||
$router.navigateByUrl('/user/igor');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('hello igor');
|
||||
});
|
||||
@ -115,7 +115,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('<ng-outlet></ng-outlet>');
|
||||
|
||||
$rootRouter.navigateByUrl('/home/foo/bar/123');
|
||||
$router.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>}');
|
||||
|
||||
$rootRouter.navigateByUrl('/home/test');
|
||||
$router.navigateByUrl('/home/test');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {}');
|
||||
expect(console.warn)
|
||||
@ -139,18 +139,17 @@ describe('ngRoute shim', function () {
|
||||
|
||||
it('should adapt routes with redirects', inject(function ($location) {
|
||||
$routeProvider
|
||||
.when('/home', {
|
||||
template: 'welcome home!',
|
||||
name: 'Home'
|
||||
})
|
||||
.when('/', {
|
||||
redirectTo: '/home'
|
||||
})
|
||||
.when('/home', {
|
||||
template: 'welcome home!'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$rootRouter.navigateByUrl('/');
|
||||
$router.navigateByUrl('/');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
@ -169,7 +168,7 @@ describe('ngRoute shim', function () {
|
||||
|
||||
compile('root {<ng-outlet></ng-outlet>}');
|
||||
|
||||
$rootRouter.navigateByUrl('/somewhere');
|
||||
$router.navigateByUrl('/somewhere');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('root {welcome home!}');
|
||||
expect($location.path()).toBe('/home');
|
||||
|
332
modules/angular1_router/test/ng_link_spec.js
vendored
332
modules/angular1_router/test/ng_link_spec.js
vendored
@ -2,191 +2,169 @@
|
||||
|
||||
describe('ngLink', function () {
|
||||
|
||||
describe('html5Mode enabled', function () {
|
||||
runHrefTestsAndExpectPrefix('/', true);
|
||||
});
|
||||
var elt,
|
||||
$compile,
|
||||
$rootScope,
|
||||
$router,
|
||||
$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');
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
|
||||
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 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' }
|
||||
]);
|
||||
|
||||
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 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'}
|
||||
]);
|
||||
|
||||
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');
|
||||
|
||||
elt.find('a')[0].click();
|
||||
inject(function($rootScope) { $rootScope.$digest(); });
|
||||
expect(elt.text()).toBe('link | two');
|
||||
});
|
||||
|
||||
|
||||
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'}
|
||||
]);
|
||||
|
||||
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,
|
||||
controller: controller
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setup(config) {
|
||||
module(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate) {
|
||||
$delegate.baseHref = function() { return config.baseHref; };
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
beforeEach(function () {
|
||||
module('ng');
|
||||
module('ngComponentRouter');
|
||||
module(function($locationProvider) {
|
||||
$locationProvider.html5Mode(config.html5Mode);
|
||||
$locationProvider.hashPrefix(config.hashPrefix);
|
||||
module(function (_$compileProvider_) {
|
||||
$compileProvider = _$compileProvider_;
|
||||
});
|
||||
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);
|
||||
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$router = _$router_;
|
||||
});
|
||||
|
||||
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 linking from the parent to the child', function () {
|
||||
$router.config([
|
||||
{ path: '/a', component: 'oneCmp' },
|
||||
{ path: '/b', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
||||
|
||||
$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', as: 'Two' }
|
||||
]);
|
||||
compile('outer { <div ng-outlet></div> }');
|
||||
|
||||
$router.navigateByUrl('/b');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elt.find('a').attr('href')).toBe('./b');
|
||||
});
|
||||
|
||||
|
||||
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'});
|
||||
|
||||
$router.config([
|
||||
{ path: '/a', component: 'twoLinkCmp' },
|
||||
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
|
||||
]);
|
||||
compile('<div ng-outlet></div>');
|
||||
|
||||
$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', as: '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', as: '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', as: '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', as: 'Two'}
|
||||
]);
|
||||
expect(function () {
|
||||
compile('<a>link</a>');
|
||||
$rootScope.$digest();
|
||||
expect(elt.text()).toBe('link');
|
||||
elt.find('a')[0].click();
|
||||
$rootScope.$digest();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
|
||||
function registerComponent(name, template, config) {
|
||||
var controller = function () {};
|
||||
|
||||
function factory() {
|
||||
return {
|
||||
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 compile(template) {
|
||||
var elt;
|
||||
inject(function($compile, $rootScope) {
|
||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
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,
|
||||
$rootRouter,
|
||||
$router,
|
||||
$templateCache,
|
||||
$controllerProvider;
|
||||
|
||||
@ -32,10 +32,10 @@ function provideHelpers(fn, preInject) {
|
||||
$controllerProvider = _$controllerProvider_;
|
||||
});
|
||||
|
||||
inject(function(_$compile_, _$rootScope_, _$rootRouter_, _$templateCache_) {
|
||||
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootRouter = _$rootRouter_;
|
||||
$router = _$router_;
|
||||
$templateCache = _$templateCache_;
|
||||
});
|
||||
|
||||
@ -72,7 +72,7 @@ function provideHelpers(fn, preInject) {
|
||||
|
||||
fn({
|
||||
registerComponent: registerComponent,
|
||||
$rootRouter: $rootRouter,
|
||||
$router: $router,
|
||||
put: put,
|
||||
compile: compile
|
||||
})
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": "v4",
|
||||
"repo": "DefinitelyTyped/DefinitelyTyped",
|
||||
"repo": "angular/DefinitelyTyped",
|
||||
"ref": "master",
|
||||
"path": "typings",
|
||||
"bundle": "typings/tsd.d.ts",
|
||||
"installed": {
|
||||
"angularjs/angular.d.ts": {
|
||||
"commit": "6eebd5e90a1cbd6b47b0705ba72dbcd5baf846f3"
|
||||
"commit": "746b9a892629060bc853e792afff536e0ec4655e"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,7 @@ This package contains different sources for different users:
|
||||
* `prod/`: a production version that does not include runtime type assertions
|
||||
3. The files under `/ts` are the TypeScript source files.
|
||||
|
||||
As a convenience, we provide you with `/es6/{dev|prod}/es5build.js`, a script to transpile the es6 sources into es5
|
||||
using [Google Traceur](https://github.com/google/traceur-compiler/).
|
||||
|
||||
License: Apache MIT 2.0
|
||||
|
@ -5,13 +5,10 @@ library angular2;
|
||||
*
|
||||
* This library does not include `bootstrap`. Import `bootstrap.dart` instead.
|
||||
*/
|
||||
export 'package:angular2/core.dart';
|
||||
export 'package:angular2/common.dart';
|
||||
export 'package:angular2/instrumentation.dart';
|
||||
export 'package:angular2/src/core/angular_entrypoint.dart' show AngularEntrypoint;
|
||||
export 'package:angular2/core.dart'
|
||||
hide forwardRef, resolveForwardRef, ForwardRefFn;
|
||||
export 'package:angular2/profile.dart';
|
||||
export 'package:angular2/lifecycle_hooks.dart';
|
||||
export 'package:angular2/src/core/application_tokens.dart'
|
||||
hide APP_COMPONENT_REF_PROMISE, APP_ID_RANDOM_PROVIDER;
|
||||
export 'package:angular2/src/platform/dom/dom_tokens.dart';
|
||||
export 'package:angular2/src/platform/dom/dom_adapter.dart';
|
||||
export 'package:angular2/src/platform/dom/events/event_manager.dart';
|
||||
export 'package:angular2/src/compiler/url_resolver.dart';
|
||||
export 'package:angular2/src/core/render/dom/dom_tokens.dart';
|
||||
|
4
modules/angular2/angular2.ts
Normal file
4
modules/angular2/angular2.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './core';
|
||||
export * from './profile';
|
||||
export * from './lifecycle_hooks';
|
||||
export * from './bootstrap';
|
3
modules/angular2/angular2_sfx.dart
Normal file
3
modules/angular2/angular2_sfx.dart
Normal file
@ -0,0 +1,3 @@
|
||||
library angular2.sfx;
|
||||
|
||||
// empty as we don't have a version for Dart
|
22
modules/angular2/angular2_sfx.ts
Normal file
22
modules/angular2/angular2_sfx.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import * as ng from './angular2';
|
||||
// the router and http should have their own SFX bundle
|
||||
// But currently the module arithmetic 'angular2/router_sfx - angular2/angular2',
|
||||
// is not support by system builder.
|
||||
import * as router from './router';
|
||||
import * as http from './http';
|
||||
|
||||
var _prevNg = (<any>window).ng;
|
||||
|
||||
(<any>window).ng = ng;
|
||||
|
||||
|
||||
(<any>window).ngRouter = router;
|
||||
(<any>window).ngHttp = http;
|
||||
/**
|
||||
* Calling noConflict will restore window.angular to its pre-angular loading state
|
||||
* and return the angular module object.
|
||||
*/
|
||||
(<any>ng).noConflict = function() {
|
||||
(<any>window).ng = _prevNg;
|
||||
return ng;
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from 'angular2/src/mock/animation_builder_mock';
|
@ -1,6 +1,5 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
* This file is only used for dart applications and for internal examples
|
||||
* that compile with both JavaScript and Dart.
|
||||
*/
|
||||
export {bootstrap} from 'angular2/platform/browser';
|
||||
export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
|
||||
export {bootstrap} from 'angular2/src/core/bootstrap';
|
||||
|
3
modules/angular2/bootstrap_static.dart
Normal file
3
modules/angular2/bootstrap_static.dart
Normal file
@ -0,0 +1,3 @@
|
||||
library angular2.bootstrap_static;
|
||||
|
||||
export 'src/core/application_static.dart';
|
@ -1,6 +0,0 @@
|
||||
/**
|
||||
* See {@link bootstrap} for more information.
|
||||
* @deprecated
|
||||
*/
|
||||
export {bootstrapStatic} from 'angular2/platform/browser_static';
|
||||
export {AngularEntrypoint} from 'angular2/src/core/angular_entrypoint';
|
@ -1,4 +0,0 @@
|
||||
export * from './src/common/pipes';
|
||||
export * from './src/common/directives';
|
||||
export * from './src/common/forms';
|
||||
export * from './src/common/common_directives';
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Starting point to import all compiler APIs.
|
||||
*/
|
||||
export * from './src/compiler/url_resolver';
|
||||
export * from './src/compiler/xhr';
|
||||
export * from './src/compiler/compiler';
|
@ -1,26 +1,21 @@
|
||||
library angular2.core;
|
||||
|
||||
export './src/core/angular_entrypoint.dart' show AngularEntrypoint;
|
||||
export './src/core/metadata.dart';
|
||||
export './src/core/util.dart';
|
||||
export 'package:angular2/src/facade/lang.dart' show enableProdMode;
|
||||
export './src/core/di.dart' hide ForwardRefFn, resolveForwardRef, forwardRef;
|
||||
export './src/facade/facade.dart';
|
||||
export './src/core/application_ref.dart' show platform, createNgZone, PlatformRef, ApplicationRef;
|
||||
export './src/core/application_tokens.dart' show APP_ID,
|
||||
APP_COMPONENT,
|
||||
APP_INITIALIZER,
|
||||
PACKAGE_ROOT_URL,
|
||||
PLATFORM_INITIALIZER;
|
||||
export './src/core/zone.dart';
|
||||
export './src/core/render.dart';
|
||||
export './src/core/linker.dart';
|
||||
export './src/core/debug/debug_node.dart' show DebugElement,
|
||||
DebugNode,
|
||||
asNativeElements;
|
||||
export './src/core/testability/testability.dart';
|
||||
export './src/core/change_detection.dart';
|
||||
export './src/core/platform_directives_and_pipes.dart';
|
||||
export './src/core/platform_common_providers.dart';
|
||||
export './src/core/application_common_providers.dart';
|
||||
export './src/core/reflection/reflection.dart';
|
||||
// Public Core API
|
||||
export 'package:angular2/src/core/metadata.dart';
|
||||
export 'package:angular2/src/core/util.dart';
|
||||
export 'package:angular2/src/core/di.dart';
|
||||
export 'package:angular2/src/core/pipes.dart';
|
||||
export 'package:angular2/src/core/facade.dart';
|
||||
// Do not export application for dart. Must import from angular2/bootstrap
|
||||
//export 'package:angular2/src/core/application.dart';
|
||||
export 'package:angular2/src/core/application_ref.dart'
|
||||
hide ApplicationRef_, PlatformRef_;
|
||||
export 'package:angular2/src/core/services.dart';
|
||||
export 'package:angular2/src/core/linker.dart';
|
||||
export 'package:angular2/src/core/lifecycle.dart';
|
||||
export 'package:angular2/src/core/zone.dart';
|
||||
export 'package:angular2/src/core/render.dart';
|
||||
export 'package:angular2/src/core/directives.dart';
|
||||
export 'package:angular2/src/core/forms.dart';
|
||||
export 'package:angular2/src/core/debug.dart';
|
||||
export 'package:angular2/src/core/change_detection.dart';
|
||||
|
@ -5,25 +5,17 @@
|
||||
*/
|
||||
export * from './src/core/metadata';
|
||||
export * from './src/core/util';
|
||||
export * from './src/core/prod_mode';
|
||||
export * from './src/core/di';
|
||||
export * from './src/facade/facade';
|
||||
export {enableProdMode} from 'angular2/src/facade/lang';
|
||||
export {platform, createNgZone, PlatformRef, ApplicationRef} from './src/core/application_ref';
|
||||
export {
|
||||
APP_ID,
|
||||
APP_COMPONENT,
|
||||
APP_INITIALIZER,
|
||||
PACKAGE_ROOT_URL,
|
||||
PLATFORM_INITIALIZER
|
||||
} from './src/core/application_tokens';
|
||||
export * from './src/core/pipes';
|
||||
export * from './src/core/facade';
|
||||
export * from './src/core/application';
|
||||
export * from './src/core/bootstrap';
|
||||
export * from './src/core/services';
|
||||
export * from './src/core/linker';
|
||||
export * from './src/core/lifecycle';
|
||||
export * from './src/core/zone';
|
||||
export * from './src/core/render';
|
||||
export * from './src/core/linker';
|
||||
export {DebugElement, DebugNode, asNativeElements} from './src/core/debug/debug_node';
|
||||
export * from './src/core/testability/testability';
|
||||
export * from './src/core/directives';
|
||||
export * from './src/core/forms';
|
||||
export * from './src/core/debug';
|
||||
export * from './src/core/change_detection';
|
||||
export * from './src/core/platform_directives_and_pipes';
|
||||
export * from './src/core/platform_common_providers';
|
||||
export * from './src/core/application_common_providers';
|
||||
export * from './src/core/reflection/reflection';
|
||||
|
@ -1,92 +0,0 @@
|
||||
# TL;DR;
|
||||
|
||||
* If you write ES5 use _one_ of the `UMD` bundles.
|
||||
* If you experiment with Angular2 using online prototyping tools like [plnkr](http://plnkr.co/) or similar use `System.register` bundles with SystemJS loader.
|
||||
* If you use build tools like Browserify or WebPack - bundle Angular2 as part of your build.
|
||||
* For all the above cases you must use `angular2-polyfills.js` in a `script` tag to easily include polyfills and external dependencies.
|
||||
|
||||
# Modules, barrels and bundles
|
||||
|
||||
Angular2 source code is authored using the ES2015 standardized module format where one module corresponds to exactly one file. Multiple modules (files) can be logically grouped into so-called "barrels".
|
||||
A bundle is a file that contains all the code for one or more barrels.
|
||||
|
||||
Most bundles come in several flavors:
|
||||
* regular and minified (got `.min` in their name);
|
||||
* regular and "development" (have `.dev` in their name) - "development" bundles contain in-line source maps and don't have minified flavor (minification removes in-lined source maps).
|
||||
|
||||
# Bundles, their content and usage scenarios
|
||||
|
||||
Angular 2 distributes several types of bundles targeted at specific usages:
|
||||
* users writing ES5 code without any transpilation steps
|
||||
* users experimenting with Angular 2 and TypeScript/ES2015 using online tools like plunker, jsbin or similar
|
||||
|
||||
Since each identified scenario has slightly different requirements and constraints there are specific bundles for each use-case.
|
||||
|
||||
## ES5 and ngUpgrade users
|
||||
|
||||
ES5 users and AngularJS 1.x users interested in the `ngUpgrade` path can take advantage of the bundles in the [UMD format](https://github.com/umdjs/umd).
|
||||
Those are coarse-grained bundles that combine many barrels in one final file.
|
||||
|
||||
filename | list of barrels | dev/prod | minified?
|
||||
------------|-------------------|----------|-------------|--------------|-------------
|
||||
`angular2-all.umd.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/http`, `angular2/router`, `angular2/instrumentation`, `angular2/upgrade`| prod | no
|
||||
`angular2-all.umd.min.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/http`, `angular2/router`, `angular2/instrumentation`, `angular2/upgrade` | prod | yes
|
||||
`angular2-all.umd.dev.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/http`, `angular2/router`, `angular2/instrumentation`, `angular2/upgrade` | dev | no
|
||||
`angular2-all-testing.umd.dev.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/http`, `angular2/router`, `angular2/instrumentation`, `angular2/upgrade`, `angular2/testing`, `angular2/http/testing`, `angular2/router/testing`, `angular2/platform/testing/browser` | dev | no
|
||||
|
||||
**Warning**: bundles in the `UMD` format are _not_ "additive". A single application should use only one bundle from the above list.
|
||||
|
||||
## SystemJS loader users
|
||||
|
||||
[SystemJS loader](https://github.com/systemjs/systemjs) with on-the-fly (in a browser) transpilations support is very useful for quick experiments using tools like plunker, jsbin or similar.
|
||||
For this scenario Angular 2 is distributed with bundles in the [System.register format](https://github.com/ModuleLoader/es6-module-loader/wiki/System.register-Explained):
|
||||
|
||||
filename | list of barrels | dev/prod | minified?
|
||||
------------|-------------------|----------|-------------|--------------|-------------
|
||||
`angular2.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/instrumentation`| prod | no
|
||||
`angular2.min.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/instrumentation`| prod | yes
|
||||
`angular2.dev.js` | `angular2/core`, `angular2/common`, `angular2/compiler`, `angular2/platform/browser`, `angular2/platform/common_dom`, `angular2/instrumentation`| dev | no
|
||||
`http.js` | `angular2/http` | prod | no
|
||||
`http.min.js` | `angular2/http` | prod | yes
|
||||
`http.dev.js` | `angular2/http` | dev | no
|
||||
`router.js` | `angular2/router` | prod | no
|
||||
`router.min.js` | `angular2/router` | prod | yes
|
||||
`router.dev.js` | `angular2/router` | dev | no
|
||||
`upgrade.js` | `angular2/upgrade` | prod | no
|
||||
`upgrade.min.js` | `angular2/upgrade` | prod | yes
|
||||
`upgrade.dev.js` | `angular2/upgrade` | dev | no
|
||||
`testing.dev.js` | `angular2/testing`, `angular2/http/testing`, `angular2/router/testing`, `angular2/platform/testing/browser` | dev | no
|
||||
|
||||
**Note**: bundles in the `System.register` format are "additive" - it is quite common to include several bundles in one application.
|
||||
For example people using Angular 2 with `http` and `router` would include: `angular2.js`, `http.js` and `router.js`.
|
||||
|
||||
## Browserify / JSPM / Rollup / WebPack users
|
||||
|
||||
Angular 2 doesn't provide any bundles for use with packaging tools Browserify or WebPack. Those tools are sophisticated enough to build optimal bundles for production use from individual Angular 2 files distributed in the npm package.
|
||||
An example of an Angular 2 project built with WebPack can be found in the [angular2-seed](https://github.com/angular/angular2-seed) repository.
|
||||
|
||||
|
||||
# Polyfills and external dependencies
|
||||
|
||||
## Required Polyfills
|
||||
|
||||
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 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.
|
||||
|
||||
|
||||
## RxJS
|
||||
|
||||
[RxJS](https://github.com/ReactiveX/RxJS) is a required dependency of Angular 2.
|
||||
|
||||
You should include RxJS in your project by declaring a dependency on the [`rxjs` npm package](https://www.npmjs.com/package/rxjs).
|
||||
|
||||
Depending on if you are using Angular bundles or not you can either use RxJS bundles from `node_modules/rxjs/bundles/` or configure your bundler to pull in the individual files from the npm package.
|
||||
|
||||
|
||||
## ES6 shims (optional)
|
||||
|
||||
Users of pre-ES6 browsers might need to add an ES6 shim (e.g. [es6-shim](https://github.com/paulmillr/es6-shim))
|
@ -1,18 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Bootstrapping
|
||||
@cheatsheetIndex 0
|
||||
@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/platform/browser.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`bootstrap(MyAppComponent, [MyService, provide(...)]);`|`provide`
|
||||
syntax(js):
|
||||
`document.addEventListener('DOMContentLoaded', function () {
|
||||
ng.platform.browser.bootstrap(MyAppComponent,
|
||||
[MyService, ng.core.provide(...)]);
|
||||
});`|`provide`
|
||||
description:
|
||||
Bootstraps an application with MyAppComponent as the root component and configures the DI providers. {@target js}Must be wrapped in the event listener to fire when the page loads.{@endtarget}
|
@ -1,35 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Built-in directives
|
||||
@cheatsheetIndex 2
|
||||
@description
|
||||
{@target ts}`import {NgIf, ...} from 'angular2/common';`{@endtarget}
|
||||
{@target js}Available from the `ng.common` namespace{@endtarget}
|
||||
{@target dart}Available using `platform_directives` in pubspec{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<section *ngIf="showSection">`|`*ngIf`
|
||||
description:
|
||||
Removes or recreates a portion of the DOM tree based on the showSection expression.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<li *ngFor="#item of list">`|`*ngFor`
|
||||
description:
|
||||
Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [ngSwitch]="conditionExpression">
|
||||
<template [ngSwitchWhen]="case1Exp">...</template>
|
||||
<template ngSwitchWhen="case2LiteralString">...</template>
|
||||
<template ngSwitchDefault>...</template>
|
||||
</div>`|`[ngSwitch]`|`[ngSwitchWhen]`|`ngSwitchWhen`|`ngSwitchDefault`
|
||||
description:
|
||||
Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of conditionExpression.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [ngClass]="{active: isActive, disabled: isDisabled}">`|`[ngClass]`
|
||||
description:
|
||||
Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand side expression should return {class-name: true/false} map.
|
@ -1,48 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Class decorators
|
||||
@cheatsheetIndex 4
|
||||
@description
|
||||
{@target ts}`import {Directive, ...} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Component({...})
|
||||
class MyComponent() {}`|`@Component({...})`
|
||||
syntax(js):
|
||||
`var MyComponent = ng.core.Component({...}).Class({...})`|`ng.core.Component({...})`
|
||||
syntax(dart):
|
||||
`@Component(...)
|
||||
class MyComponent() {}`|`@Component(...)`
|
||||
description:
|
||||
Declares that a class is a component and provides metadata about the component.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Pipe({...})
|
||||
class MyPipe() {}`|`@Pipe({...})`
|
||||
syntax(js):
|
||||
`var MyPipe = ng.core.Pipe({...}).Class({...})`|`ng.core.Pipe({...})`
|
||||
syntax(dart):
|
||||
`@Pipe(...)
|
||||
class MyPipe() {}`|`@Pipe(...)`
|
||||
description:
|
||||
Declares that a class is a pipe and provides metadata about the pipe.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Injectable()
|
||||
class MyService() {}`|`@Injectable()`
|
||||
syntax(js):
|
||||
`var OtherService = ng.core.Class({constructor: function() { }});
|
||||
var MyService = ng.core.Class({constructor: [OtherService, function(otherService) { }]});`|`var MyService = ng.core.Class({constructor: [OtherService, function(otherService) { }]});`
|
||||
syntax(dart):
|
||||
`@Injectable()
|
||||
class MyService() {}`|`@Injectable()`
|
||||
description:
|
||||
{@target ts dart}Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||
{@endtarget}
|
||||
{@target js}
|
||||
Declares a service to inject into a class by providing an array with the services with the final item being the function which will receive the injected services.
|
||||
{@endtarget}
|
@ -1,46 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Component configuration
|
||||
@cheatsheetIndex 6
|
||||
@description
|
||||
{@target js}`ng.core.Component` extends `ng.core.Directive`,
|
||||
so the `ng.core.Directive` configuration applies to components as well{@endtarget}
|
||||
{@target ts dart}`@Component` extends `@Directive`,
|
||||
so the `@Directive` configuration applies to components as well{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`viewProviders: [MyService, provide(...)]`|`viewProviders:`
|
||||
syntax(js):
|
||||
`viewProviders: [MyService, ng.core.provide(...)]`|`viewProviders:`
|
||||
description:
|
||||
Array of dependency injection providers scoped to this component's view.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`template: 'Hello {{name}}'
|
||||
templateUrl: 'my-component.html'`|`template:`|`templateUrl:`
|
||||
description:
|
||||
Inline template / external template URL of the component's view.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`styles: ['.primary {color: red}']
|
||||
styleUrls: ['my-component.css']`|`styles:`|`styleUrls:`
|
||||
description:
|
||||
List of inline CSS styles / external stylesheet URLs for styling component’s view.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`directives: [MyDirective, MyComponent]`|`directives:`
|
||||
description:
|
||||
List of directives used in the the component’s template.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`pipes: [MyPipe, OtherPipe]`|`pipes:`
|
||||
description:
|
||||
List of pipes used in the component's template.
|
@ -1,33 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Dependency injection configuration
|
||||
@cheatsheetIndex 9
|
||||
@description
|
||||
{@target ts}`import {provide} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`provide(MyService, {useClass: MyMockService})`|`provide`|`useClass`
|
||||
syntax(js):
|
||||
`ng.core.provide(MyService, {useClass: MyMockService})`|`provide`|`useClass`
|
||||
description:
|
||||
Sets or overrides the provider for MyService to the MyMockService class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`provide(MyService, {useFactory: myFactory})`|`provide`|`useFactory`
|
||||
syntax(js):
|
||||
`ng.core.provide(MyService, {useFactory: myFactory})`|`provide`|`useFactory`
|
||||
description:
|
||||
Sets or overrides the provider for MyService to the myFactory factory function.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`provide(MyValue, {useValue: 41})`|`provide`|`useValue`
|
||||
syntax(js):
|
||||
`provide(MyValue, {useValue: 41})`|`provide`|`useValue`
|
||||
description:
|
||||
Sets or overrides the provider for MyValue to the value 41.
|
@ -1,80 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Class field decorators for directives and components
|
||||
@cheatsheetIndex 7
|
||||
@description
|
||||
{@target ts}`import {Input, ...} from 'angular2/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@Input() myProperty;`|`@Input()`
|
||||
syntax(js):
|
||||
`ng.core.Input(myProperty, myComponent);`|`ng.core.Input(`|`);`
|
||||
description:
|
||||
Declares an input property that we can update via property binding (e.g.
|
||||
`<my-cmp [my-property]="someExpression">`).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@Output() myEvent = new EventEmitter();`|`@Output()`
|
||||
syntax(js):
|
||||
`myEvent = new ng.core.EventEmitter(); ng.core.Output(myEvent, myComponent);`|`ng.core.Output(`|`);`
|
||||
description:
|
||||
Declares an output property that fires events to which we can subscribe with an event binding (e.g. `<my-cmp (my-event)="doSomething()">`).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@HostBinding('[class.valid]') isValid;`|`@HostBinding('[class.valid]')`
|
||||
syntax(js):
|
||||
`ng.core.HostBinding('[class.valid]', 'isValid', myComponent);`|`ng.core.HostBinding('[class.valid]', 'isValid'`|`);`
|
||||
description:
|
||||
Binds a host element property (e.g. CSS class valid) to directive/component property (e.g. isValid).
|
||||
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@HostListener('click', ['$event']) onClick(e) {...}`|`@HostListener('click', ['$event'])`
|
||||
syntax(js):
|
||||
`ng.core.HostListener('click', ['$event'], onClick(e) {...}, myComponent);`|`ng.core.HostListener('click', ['$event'], onClick(e)`|`);`
|
||||
description:
|
||||
Subscribes to a host element event (e.g. click) with a directive/component method (e.g. onClick), optionally passing an argument ($event).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@ContentChild(myPredicate) myChildComponent;`|`@ContentChild(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ContentChild(myPredicate, 'myChildComponent', myComponent);`|`ng.core.ContentChild(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the first result of the component content query (myPredicate) to the myChildComponent property of the class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@ContentChildren(myPredicate) myChildComponents;`|`@ContentChildren(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ContentChildren(myPredicate, 'myChildComponents', myComponent);`|`ng.core.ContentChildren(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the results of the component content query (myPredicate) to the myChildComponents property of the class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@ViewChild(myPredicate) myChildComponent;`|`@ViewChild(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ViewChild(myPredicate, 'myChildComponent', myComponent);`|`ng.core.ViewChild(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the first result of the component view query (myPredicate) to the myChildComponent property of the class. Not available for directives.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`@ViewChildren(myPredicate) myChildComponents;`|`@ViewChildren(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ViewChildren(myPredicate, 'myChildComponents', myComponent);`|`ng.core.ViewChildren(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the results of the component view query (myPredicate) to the myChildComponents property of the class. Not available for directives.
|
@ -1,24 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Directive configuration
|
||||
@cheatsheetIndex 5
|
||||
@description
|
||||
{@target ts}`@Directive({ property1: value1, ... })`{@endtarget}
|
||||
{@target js}`ng.core.Directive({ property1: value1, ... }).Class({...})`{@endtarget}
|
||||
{@target dart}`@Directive(property1: value1, ...)`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`selector: '.cool-button:not(a)'`|`selector:`
|
||||
description:
|
||||
Specifies a CSS selector that identifies this directive within a template. Supported selectors include `element`,
|
||||
`[attribute]`, `.class`, and `:not()`.
|
||||
|
||||
Does not support parent-child relationship selectors.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`providers: [MyService, provide(...)]`|`providers:`
|
||||
syntax(js):
|
||||
`providers: [MyService, ng.core.provide(...)]`|`providers:`
|
||||
description:
|
||||
Array of dependency injection providers for this directive and its children.
|
@ -1,13 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Forms
|
||||
@cheatsheetIndex 3
|
||||
@description
|
||||
{@target ts}`import {FORM_DIRECTIVES} from 'angular2/common';`{@endtarget}
|
||||
{@target js}Available from `ng.common.FORM_DIRECTIVES`{@endtarget}
|
||||
{@target dart}Available using `platform_directives` in pubspec{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<input [(ngModel)]="userName">`|`[(ngModel)]`
|
||||
description:
|
||||
Provides two-way data-binding, parsing and validation for form controls.
|
@ -1,88 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Directive and component change detection and lifecycle hooks
|
||||
@cheatsheetIndex 8
|
||||
@description
|
||||
{@target ts dart}(implemented as class methods){@endtarget}
|
||||
{@target js}(implemented as component properties){@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`constructor(myService: MyService, ...) { ... }`|`constructor(myService: MyService, ...)`
|
||||
syntax(js):
|
||||
`constructor: function(MyService, ...) { ... }`|`constructor: function(MyService, ...)`
|
||||
syntax(dart):
|
||||
`MyAppComponent(MyService myService, ...) { ... }`|`MyAppComponent(MyService myService, ...)`
|
||||
description:
|
||||
The class constructor is called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngOnChanges(changeRecord) { ... }`|`ngOnChanges(changeRecord)`
|
||||
syntax(js):
|
||||
`ngOnChanges: function(changeRecord) { ... }`|`ngOnChanges: function(changeRecord)`
|
||||
description:
|
||||
Called after every change to input properties and before processing content or child views.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngOnInit() { ... }`|`ngOnInit()`
|
||||
syntax(js):
|
||||
`ngOnInit: function() { ... }`|`ngOnInit: function()`
|
||||
description:
|
||||
Called after the constructor, initializing input properties, and the first call to ngOnChanges.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngDoCheck() { ... }`|`ngDoCheck()`
|
||||
syntax(js):
|
||||
`ngDoCheck: function() { ... }`|`ngDoCheck: function()`
|
||||
description:
|
||||
Called every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngAfterContentInit() { ... }`|`ngAfterContentInit()`
|
||||
syntax(js):
|
||||
`ngAfterContentInit: function() { ... }`|`ngAfterContentInit: function()`
|
||||
description:
|
||||
Called after ngOnInit when the component's or directive's content has been initialized.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngAfterContentChecked() { ... }`|`ngAfterContentChecked()`
|
||||
syntax(js):
|
||||
`ngAfterContentChecked: function() { ... }`|`ngAfterContentChecked: function()`
|
||||
description:
|
||||
Called after every check of the component's or directive's content.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngAfterViewInit() { ... }`|`ngAfterViewInit()`
|
||||
syntax(js):
|
||||
`ngAfterViewInit: function() { ... }`|`ngAfterViewInit: function()`
|
||||
description:
|
||||
Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngAfterViewChecked() { ... }`|`ngAfterViewChecked()`
|
||||
syntax(js):
|
||||
`ngAfterViewChecked: function() { ... }`|`ngAfterViewChecked: function()`
|
||||
description:
|
||||
Called after every check of the component's view. Applies to components only.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`ngOnDestroy() { ... }`|`ngOnDestroy()`
|
||||
syntax(js):
|
||||
`ngOnDestroy: function() { ... }`|`ngOnDestroy: function()`
|
||||
description:
|
||||
Called once, before the instance is destroyed.
|
@ -1,101 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Routing and navigation
|
||||
@cheatsheetIndex 10
|
||||
@description
|
||||
{@target ts}`import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, ...} from 'angular2/router';`{@endtarget}
|
||||
{@target js}Available from the `ng.router` namespace{@endtarget}
|
||||
{@target dart}`import 'package:angular2/angular2.dart';`{@endtarget}
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@RouteConfig([
|
||||
{ path: '/:myParam', component: MyComponent, name: 'MyCmp' },
|
||||
{ path: '/staticPath', component: ..., name: ...},
|
||||
{ path: '/*wildCardParam', component: ..., name: ...}
|
||||
])
|
||||
class MyComponent() {}`|`@RouteConfig`
|
||||
syntax(js):
|
||||
`var MyComponent = ng.router.RouteConfig([
|
||||
{ path: '/:myParam', component: MyComponent, name: 'MyCmp' },
|
||||
{ path: '/staticPath', component: ..., name: ...},
|
||||
{ path: '/*wildCardParam', component: ..., name: ...}
|
||||
]).Class({
|
||||
constructor: function() {}
|
||||
});`|`ng.router.RouteConfig`
|
||||
syntax(dart):
|
||||
`@RouteConfig(const [
|
||||
const Route(path: '/:myParam', component: MyComponent, name: 'MyCmp' ),
|
||||
])`|`@RouteConfig`
|
||||
description:
|
||||
Configures routes for the decorated component. Supports static, parameterized, and wildcard routes.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<router-outlet></router-outlet>`|`router-outlet`
|
||||
description:
|
||||
Marks the location to load the component of the active route.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<a [routerLink]="[ '/MyCmp', {myParam: 'value' } ]">`|`[routerLink]`
|
||||
description:
|
||||
Creates a link to a different view based on a route instruction consisting of a route name and optional parameters. The route name matches the as property of a configured route. Add the '/' prefix to navigate to a root route; add the './' prefix for a child route.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@CanActivate(() => { ... })class MyComponent() {}`|`@CanActivate`
|
||||
syntax(js):
|
||||
`var MyComponent = ng.router.CanActivate(function() { ... }).Component({...}).Class({constructor: ...});`|`ng.router.CanActivate(function() { ... })`
|
||||
syntax(dart):
|
||||
`@CanActivate(() => ...)class MyComponent() {}`|`@CanActivate`
|
||||
description:
|
||||
A component decorator defining a function that the router should call first to determine if it should activate this component. Should return a boolean or a {@target js ts}promise{@endtarget}{@target dart}future{@endtarget}.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`routerOnActivate(nextInstruction, prevInstruction) { ... }`|`routerOnActivate`
|
||||
syntax(js):
|
||||
`routerOnActivate: function(nextInstruction, prevInstruction) { ... }`|`routerOnActivate`
|
||||
description:
|
||||
After navigating to a component, the router calls the component's routerOnActivate method (if defined).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`routerCanReuse(nextInstruction, prevInstruction) { ... }`|`routerCanReuse`
|
||||
syntax(js):
|
||||
`routerCanReuse: function(nextInstruction, prevInstruction) { ... }`|`routerCanReuse`
|
||||
description:
|
||||
The router calls a component's routerCanReuse method (if defined) to determine whether to reuse the instance or destroy it and create a new instance. Should return a boolean or a {@target js ts}promise{@endtarget}{@target dart}future{@endtarget}.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`routerOnReuse(nextInstruction, prevInstruction) { ... }`|`routerOnReuse`
|
||||
syntax(js):
|
||||
`routerOnReuse: function(nextInstruction, prevInstruction) { ... }`|`routerOnReuse`
|
||||
description:
|
||||
The router calls the component's routerOnReuse method (if defined) when it re-uses a component instance.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`routerCanDeactivate(nextInstruction, prevInstruction) { ... }`|`routerCanDeactivate`
|
||||
syntax(js):
|
||||
`routerCanDeactivate: function(nextInstruction, prevInstruction) { ... }`|`routerCanDeactivate`
|
||||
description:
|
||||
The router calls the routerCanDeactivate methods (if defined) of every component that would be removed after a navigation. The navigation proceeds if and only if all such methods return true or a {@target js ts}promise that is resolved{@endtarget}{@target dart}future that completes successfully{@endtarget}.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts dart):
|
||||
`routerOnDeactivate(nextInstruction, prevInstruction) { ... }`|`routerOnDeactivate`
|
||||
syntax(js):
|
||||
`routerOnDeactivate: function(nextInstruction, prevInstruction) { ... }`|`routerOnDeactivate`
|
||||
description:
|
||||
Called before the directive is removed as the result of a route change. May return a {@target js ts}promise{@endtarget}{@target dart}future{@endtarget} that pauses removing the directive until the {@target js ts}promise resolves{@endtarget}{@target dart}future completes{@endtarget}.
|
@ -1,80 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Template syntax
|
||||
@cheatsheetIndex 1
|
||||
@description
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<input [value]="firstName">`|`[value]`
|
||||
description:
|
||||
Binds property `value` to the result of expression `firstName`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [attr.role]="myAriaRole">`|`[attr.role]`
|
||||
description:
|
||||
Binds attribute `role` to the result of expression `myAriaRole`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [class.extra-sparkle]="isDelightful">`|`[class.extra-sparkle]`
|
||||
description:
|
||||
Binds the presence of the CSS class `extra-sparkle` on the element to the truthiness of the expression `isDelightful`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [style.width.px]="mySize">`|`[style.width.px]`
|
||||
description:
|
||||
Binds style property `width` to the result of expression `mySize` in pixels. Units are optional.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<button (click)="readRainbow($event)">`|`(click)`
|
||||
description:
|
||||
Calls method `readRainbow` when a click event is triggered on this button element (or its children) and passes in the event object.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div title="Hello {{ponyName}}">`|`{{ponyName}}`
|
||||
description:
|
||||
Binds a property to an interpolated string, e.g. "Hello Seabiscuit". Equivalent to:
|
||||
`<div [title]="'Hello' + ponyName">`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Hello {{ponyName}}</p>`|`{{ponyName}}`
|
||||
description:
|
||||
Binds text content to an interpolated string, e.g. "Hello Seabiscuit".
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<my-cmp [(title)]="name">`|`[(title)]`
|
||||
description:
|
||||
Sets up two-way data binding. Equivalent to: `<my-cmp [title]="name" (titleChange)="name=$event">`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<video #movieplayer ...>
|
||||
<button (click)="movieplayer.play()">
|
||||
</video>`|`#movieplayer`|`(click)`
|
||||
description:
|
||||
Creates a local variable `movieplayer` that provides access to the `video` element instance in data-binding and event-binding expressions in the current template.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p *myUnless="myExpression">...</p>`|`*myUnless`
|
||||
description:
|
||||
The `*` symbol means that the current element will be turned into an embedded template. Equivalent to:
|
||||
`<template [myUnless]="myExpression"><p>...</p></template>`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Card No.: {{cardNumber | myCreditCardNumberFormatter}}</p>`|`{{cardNumber | myCreditCardNumberFormatter}}`
|
||||
description:
|
||||
Transforms the current value of expression `cardNumber` via the pipe called `myCreditCardNumberFormatter`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Employer: {{employer?.companyName}}</p>`|`{{employer?.companyName}}`
|
||||
description:
|
||||
The Elvis operator (`?`) means that the `employer` field is optional and if `undefined`, the rest of the expression should be ignored.
|
@ -359,20 +359,20 @@ Example of conditionally included template:
|
||||
|
||||
```
|
||||
Hello {{user}}!
|
||||
<div template="ngIf: isAdministrator">
|
||||
<div template="ng-if: isAdministrator">
|
||||
...administrator menu here...
|
||||
</div>
|
||||
```
|
||||
|
||||
In the above example the `ngIf` directive determines whether the child view (an instance of the child template) should be
|
||||
inserted into the root view. The `ngIf` makes this decision based on if the `isAdministrator` binding is true.
|
||||
In the above example the `ng-if` directive determines whether the child view (an instance of the child template) should be
|
||||
inserted into the root view. The `ng-if` makes this decision based on if the `isAdministrator` binding is true.
|
||||
|
||||
The above example is in the short form, for better clarity let's rewrite it in the canonical form, which is functionally
|
||||
identical.
|
||||
|
||||
```
|
||||
Hello {{user}}!
|
||||
<template [ngIf]="isAdministrator">
|
||||
<template [ng-if]="isAdministrator">
|
||||
<div>
|
||||
...administrator menu here...
|
||||
</div>
|
||||
@ -383,22 +383,22 @@ Hello {{user}}!
|
||||
### Template Microsyntax
|
||||
|
||||
Often times it is necessary to encode a lot of different bindings into a template to control how the instantiation
|
||||
of the templates occurs. One such example is `ngFor`.
|
||||
of the templates occurs. One such example is `ng-for`.
|
||||
|
||||
```
|
||||
<form #foo=form>
|
||||
</form>
|
||||
<ul>
|
||||
<template [ngFor] #person [ngForOf]="people" #i="index">
|
||||
<template [ng-for] #person [ng-for-of]="people" #i="index">
|
||||
<li>{{i}}. {{person}}<li>
|
||||
</template>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Where:
|
||||
* `[ngFor]` triggers the for directive.
|
||||
* `#person` exports the implicit `ngFor` item.
|
||||
* `[ngForOf]="people"` binds an iterable object to the `ngFor` controller.
|
||||
* `[ng-for]` triggers the for directive.
|
||||
* `#person` exports the implicit `ng-for` item.
|
||||
* `[ng-for-of]="people"` binds an iterable object to the `ng-for` controller.
|
||||
* `#i=index` exports item index as `i`.
|
||||
|
||||
The above example is explicit but quite wordy. For this reason in most situations a short hand version of the
|
||||
@ -406,7 +406,7 @@ syntax is preferable.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor; #person; of=people; #i=index;">{{i}}. {{person}}<li>
|
||||
<li template="ng-for; #person; of=people; #i=index;">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -416,24 +416,24 @@ which allows us to further shorten the text.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor #person of people #i=index">{{i}}. {{person}}<li>
|
||||
<li template="ng-for #person of people #i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
We can also optionally use `var` instead of `#` and add `:` to `for` which creates the following recommended
|
||||
microsyntax for `ngFor`.
|
||||
microsyntax for `ng-for`.
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor: var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
<li template="ng-for: var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Finally, we can move the `ngFor` keyword to the left hand side and prefix it with `*` as so:
|
||||
Finally, we can move the `ng-for` keyword to the left hand side and prefix it with `*` as so:
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li *ngFor="var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
<li *ng-for="var person of people; var i=index">{{i}}. {{person}}<li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
@ -456,7 +456,7 @@ Where
|
||||
* `local` is a local identifier for local variables.
|
||||
* `internal` is an internal variable which the directive exports for binding.
|
||||
* `key` is an attribute name usually only used to trigger a specific directive.
|
||||
* `keyExpression` is a property name to which the expression will be bound to.
|
||||
* `keyExpression` is an property name to which the expression will be bound to.
|
||||
* `varExport` allows exporting of directive internal state as variables for further binding. If no `internal` name
|
||||
is specified, the exporting is to an implicit variable.
|
||||
* `microsyntax` allows you to build a simple microsyntax which can still clearly identify which expressions bind to
|
||||
@ -544,7 +544,7 @@ Angular are:
|
||||
<div title="{{expression}}">{{expression}}</div>
|
||||
<div [title]="expression">...</div>
|
||||
<div bind-title="expression">...</div>
|
||||
<div template="ngIf: expression">...</div>
|
||||
<div template="ng-if: expression">...</div>
|
||||
```
|
||||
|
||||
Expressions are simplified version of expression in the language in which you are writing your application. (i.e.
|
||||
|
@ -112,7 +112,9 @@ Example of a component:
|
||||
'title', | - title mapped to component title
|
||||
'open' | - open attribute mapped to component's open property
|
||||
], |
|
||||
templateUrl: 'pane.html' | URL of template HTML
|
||||
}) |
|
||||
@View({ | View annotation
|
||||
templateUrl: 'pane.html' | - URL of template HTML
|
||||
}) |
|
||||
class Pane { | Component controller class
|
||||
title:string; | - title property
|
||||
@ -159,7 +161,7 @@ Example of usage:
|
||||
|
||||
## Directives that use a ViewContainer
|
||||
|
||||
Directives that use a ViewContainer can control instantiation of child views which are then inserted into the DOM. (Examples are `ngIf` and `ngFor`.)
|
||||
Directives that use a ViewContainer can control instantiation of child views which are then inserted into the DOM. (Examples are `ng-if` and `ng-for`.)
|
||||
|
||||
* Every `template` element creates a `ProtoView` which can be used to create Views via the ViewContainer.
|
||||
* The child views show up as siblings of the directive in the DOM.
|
||||
@ -225,9 +227,11 @@ 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], |
|
||||
templateUrl: 'my_app.html', | Assume we have a template that needs to be
|
||||
directives: [House] | configured with directives 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] |
|
||||
}) |
|
||||
class MyApp {} |
|
||||
|
|
||||
@ -269,6 +273,8 @@ 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] |
|
||||
@ -323,6 +329,8 @@ Shadow DOM provides an encapsulation for components, so as a general rule it doe
|
||||
```
|
||||
@Component({
|
||||
selector: '[kid]'
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'kid.html',
|
||||
directives: []
|
||||
})
|
||||
@ -339,6 +347,8 @@ class Kid {
|
||||
|
||||
@Component({
|
||||
selector: '[dad]'
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'dad.html',
|
||||
directives: [Kid]
|
||||
})
|
||||
@ -351,7 +361,9 @@ class Dad {
|
||||
|
||||
@Component({
|
||||
selector: '[grandpa]',
|
||||
viewBindings: [],
|
||||
viewBindings: []
|
||||
})
|
||||
@View({
|
||||
templateUrl: 'grandpa.html',
|
||||
directives: [Dad]
|
||||
})
|
||||
|
@ -94,7 +94,7 @@ Let's start with a View such as:
|
||||
|
||||
```
|
||||
<ul>
|
||||
<li template="ngFor: #person of people">{{person}}</li>
|
||||
<li template="ng-for: #person of people">{{person}}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
@ -179,7 +179,9 @@ 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,239 +0,0 @@
|
||||
Angular2 templates are now case-sensitive and use camelCase in many places where dash-case was previously used ([design doc] (https://docs.google.com/document/d/1UMxTIUBTIUZNfOqwMhkLg0ANFSBtLIK9JsIu77EZaBA/edit?ts=564f7dd4)).
|
||||
|
||||
## Overview
|
||||
|
||||
Where you used to write:
|
||||
|
||||
```
|
||||
<my-cmp (some-event)="someAction()" [some-property]="expression" #some-var>
|
||||
```
|
||||
|
||||
in order to:
|
||||
- bind to the `someEvent` event,
|
||||
- bind to the `someProperty` property,
|
||||
- create a `someVar` local variable
|
||||
|
||||
You should now write:
|
||||
|
||||
```
|
||||
<my-cmp (someEvent)="someAction()" [someProperty]="expression" #someVar>
|
||||
```
|
||||
|
||||
Notes:
|
||||
- while tag name are case sensitive, the best practice is to use dash case for component elements so that the browser
|
||||
interpret them as custom elements,
|
||||
- `(some-event)` would now bind to the `some-event` event (i.e. there is no implicit dash to camel case conversion),
|
||||
- `[some-property]` would now bind to the `some-property` property (i.e. there is no implicit dash to camel case conversion),
|
||||
- `#some-var` is not allowed any more ("-" can not be used in variable names).
|
||||
|
||||
## Migration
|
||||
|
||||
#### Templates
|
||||
|
||||
1. Directives selectors, property bindings, event bindings, template variables and template element attributes should be changed to camel case:
|
||||
|
||||
Examples:
|
||||
- `<p *ng-if="cond">` should be changed to `<p *ngIf="cond">`,
|
||||
- `<my-cmp [my-prop]="exp">` should be changed to `<my-cmp [myProp]="exp">`,
|
||||
- `<my-cmp (my-event)="action()">` should be changed to `<my-cmp (myEvent)="action()">`,
|
||||
- `<my-cmp [(my-prop)]="prop">` should be changed to `<my-cmp [(myProp)]="prop">`,
|
||||
- `<input #my-input>` should be changed to `<input #myInput>`,
|
||||
- `<template ng-for #my-item [ng-for-of]=items #my-index="index">` should be changed to `<template ngFor #myItem [ngForOf]=items #myIndex="index">`,
|
||||
|
||||
Note: while the tag names are now case-sensitive the best practice is to keep them lower-dash-cased so that the browser
|
||||
treat them as custom elements. Using dashes in custom element names is required by the [Custom Element HTML Spec](http://www.w3.org/TR/custom-elements/#concepts).
|
||||
This explains why the `<router-outlet>` component is left unchanged.
|
||||
|
||||
`on-`, `bindon-`, `bind-` and `var-` prefixes are still part of the canonical syntax and should remain unchanged (lower cased):
|
||||
- `on-some-event` should be changed to `on-someEvent`,
|
||||
- `bind-my-prop` should be changed to `bind-myProp`,
|
||||
- `bindon-my-prop` should be changed to `bindon-myProp`,
|
||||
- `var-my-var` should be changed to `var-myVar`.
|
||||
|
||||
2. Update variable binding
|
||||
|
||||
- `<p #var1="a-b" var-var2="c-d">` should be changed to `<p #var1="aB" var-var2="cD">`
|
||||
|
||||
3. The `template` attribute values should also be updated in the same way
|
||||
|
||||
`<p template="ng-for #my-item of items #my-index = index">` should be changed to `<p template="ngFor #myItem of items #myIndex = index">`.
|
||||
|
||||
Note that both angular directives and your own directives must be updated in the same way.
|
||||
|
||||
#### Directives and Components
|
||||
|
||||
Take the following steps to upgrade your directives and components:
|
||||
|
||||
1. Update the selector:
|
||||
```
|
||||
@Directive({selector: 'tag[attr][name=value]'})
|
||||
@Component({selector: 'tag[attr][name=value]'})
|
||||
```
|
||||
|
||||
Tag and attributes names are case sensitive:
|
||||
- For tag names, the best practice is to keep them lower dash cased, do not update them,
|
||||
- For attribute names, the best practice is to convert them from lower dash case to camel case.
|
||||
|
||||
Examples:
|
||||
- `custom-tag` should stay `custom-tag` (do not update tag names),
|
||||
- `[attr-name]` should be updated to `[attrName]`,
|
||||
- `[attr-name=someValue]` should be updated to `[attrName=someValue]`,
|
||||
- `custom-tag[attr-name=someValue]` should be updated to `custom-tag[attrName=someValue]`
|
||||
|
||||
Note: attribute values and classes are still matched case insensitive.
|
||||
|
||||
2. Update the inputs
|
||||
```
|
||||
@Directive({inputs: ['myProp', 'myOtherProp: my-attr-name']})
|
||||
```
|
||||
|
||||
As attribute names are now case sensitive, they should be converted from dash to camel case where they are specified.
|
||||
The previous decorator becomes:
|
||||
|
||||
```
|
||||
@Directive({inputs: ['myProp', 'myOtherProp: myAttrName']})
|
||||
```
|
||||
|
||||
Notes:
|
||||
- only the long syntax (with ":") needs to be updated,
|
||||
- `properties` is the legacy name for `inputs` and should be updated in the same way - it is a good idea to replace
|
||||
`properties` with `inputs` at the same time as support for the former will be removed soon.
|
||||
|
||||
The same applies for the `@Input` decorator:
|
||||
|
||||
```
|
||||
@Input() myProp;
|
||||
@Input('my-attr-name') myProp;
|
||||
```
|
||||
|
||||
That is they only need to be updated when the attribute name is specified:
|
||||
|
||||
```
|
||||
@Input() myProp; // Nothing to update
|
||||
@Input('myAttrName') myProp; // Convert the attribute name to camel case
|
||||
```
|
||||
|
||||
3. Update the outputs
|
||||
|
||||
Update the outputs in the same way the inputs are updated:
|
||||
|
||||
```
|
||||
@Directive({outputs: ['myEvent', 'myOtherEvent: my-event-name']})
|
||||
```
|
||||
|
||||
should be updated to:
|
||||
|
||||
```
|
||||
@Directive({outputs: ['myEvent', 'myOtherEvent: myEventName']})
|
||||
```
|
||||
|
||||
If you use `events` instead of `outputs` you should update in the same way and switch to `outputs` as `events` is deprecated.
|
||||
|
||||
```
|
||||
@Output() myEvent;
|
||||
@Output('my-event-name') myEvent;
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Output() myEvent;
|
||||
@Output('myEventName') myEvent;
|
||||
```
|
||||
|
||||
4. Update the host bindings
|
||||
|
||||
```
|
||||
@Directive({
|
||||
host: {
|
||||
'[some-prop]': 'exp',
|
||||
'[style.background-color]': 'exp',
|
||||
'[class.some-class]': 'exp',
|
||||
'[attr.some-attr]': 'exp',
|
||||
'(some-event)': 'action()',
|
||||
'some-attr': 'value'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Directive({
|
||||
host: {
|
||||
'[someProp]': 'exp',
|
||||
'[style.background-color]': 'exp',
|
||||
'[class.some-class]': 'exp',
|
||||
'[attr.some-attr]': 'exp',
|
||||
'(someEvent)': 'action()',
|
||||
'some-attr': 'value'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The property bindings (`[...]`) and event bindings (`(...)`) must be updated in the same way as they are updated in a
|
||||
template - ie converted to camel case (reminder: `[attr.]`, `[class.]` and `[style.]` should not be converted to camel case).
|
||||
|
||||
5. Update export name
|
||||
|
||||
```
|
||||
@Directive({
|
||||
exportAs: 'some-name'
|
||||
})
|
||||
```
|
||||
|
||||
should be changed to:
|
||||
|
||||
```
|
||||
@Directive({
|
||||
exportAs: 'someName'
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
# CSS
|
||||
|
||||
As the attribute names from your templates have been updated from dash to camel case, you should also reflect the changes
|
||||
in your stylesheets.
|
||||
|
||||
The attributes that need to be updated are the ones used in the selector and the inputs of your directives.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
// Directive
|
||||
@Directive({
|
||||
selector: '[my-dir]',
|
||||
inputs: ['someProp: some-input'],
|
||||
})
|
||||
|
||||
<!-- template -->
|
||||
<div my-dir some-input="some value" not-an-input></div>
|
||||
|
||||
/* css */
|
||||
[my-dir] { ... }
|
||||
[some-input] { ... }
|
||||
[not-an-input] { ... }
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
// Directive
|
||||
@Directive({
|
||||
selector: '[myDir]',
|
||||
inputs: ['someProp: someInput'],
|
||||
})
|
||||
|
||||
<!-- template -->
|
||||
<div myDir someInput="some value" not-an-input></div>
|
||||
|
||||
/* css */
|
||||
[myDir] { ... }
|
||||
[someInput] { ... }
|
||||
[not-an-input] { ... }
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `not-an-input` is not used in a selector nor it is an input of a directive, it need not be camel cased,
|
||||
- CSS selectors are case insensitive you can use `[myDir]`, `[mydir]` or any other casing.
|
@ -19,10 +19,8 @@ better framerate and UX for applications.
|
||||
|
||||
## Bootstrapping a WebWorker Application
|
||||
Bootstrapping a WebWorker application is not much different than bootstrapping a normal application.
|
||||
The main difference is that you need to do the bootstrap process on both the worker and render thread.
|
||||
Unlike in a standard Angular2 application you don't bootstrap your main component on the render thread.
|
||||
Instead you initialize a new application injector with the WORKER_APP_PLATFORM providers and provide the name
|
||||
of your WebWorker script. See the example below for details:
|
||||
The primary difference is that you don't pass your root component directly to ```bootstrap```.
|
||||
Instead you pass the name of a background script that calls ```bootstrapWebWorker``` with your root component.
|
||||
|
||||
### Example
|
||||
To bootstrap Hello World in a WebWorker we do the following in TypeScript
|
||||
@ -41,11 +39,8 @@ To bootstrap Hello World in a WebWorker we do the following in TypeScript
|
||||
```
|
||||
```TypeScript
|
||||
// index.js
|
||||
import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT} from "angular2/platform/worker_render";
|
||||
import {platform} from "angular2/core";
|
||||
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
|
||||
import {bootstrap} from "angular2/web_worker/ui";
|
||||
bootstrap("loader.js");
|
||||
```
|
||||
```JavaScript
|
||||
// loader.js
|
||||
@ -54,9 +49,7 @@ System.import("app");
|
||||
```
|
||||
```TypeScript
|
||||
// app.ts
|
||||
import {Component, View, platform} from "angular2/core";
|
||||
import {WORKER_APP_PLATFORM, WORKER_APP_APPLICATION} from "angular2/platform/worker_app";
|
||||
|
||||
import {Component, View, bootstrapWebWorker} from "angular2/web_worker/worker";
|
||||
@Component({
|
||||
selector: "hello-world"
|
||||
})
|
||||
@ -67,19 +60,21 @@ export class HelloWorld {
|
||||
name: string = "Jane";
|
||||
}
|
||||
|
||||
platform([WORKER_APP_PLATFORM])
|
||||
.application([WORKER_APP_APPLICATION])
|
||||
.then((ref) => ref.bootstrap(RootComponent));
|
||||
bootstrapWebWorker(HelloWorld);
|
||||
```
|
||||
There's a few important things to note here:
|
||||
* On the UI side we import all angular types from `angular2/web_worker/ui` and on the worker side we import from
|
||||
`angular2/web_worker/worker`. These modules include all the typings in the WebWorker bundle. By importing from
|
||||
these URLs instead of `angular2/angular2` we can statically ensure that our app does not reference a type that
|
||||
doesn't exist in the context it's mean to execute in. For example, if we tried to import DomRenderer in the Worker
|
||||
or NgFor on the UI we would get a compiler error.
|
||||
* The UI loads angular from the file `angular2/web_worker/ui.js` and the Worker loads angular from
|
||||
`angular2/web_worker/worker.js`. These bundles are created specifically for using WebWorkers and should be used
|
||||
instead of the normal angular2.js file. Both files contain subsets of the angular2 codebase that is designed to
|
||||
run specifically on the UI or Worker. Additionally, they contain the core messaging infrastructure used to
|
||||
communicate between the Worker and the UI. This messaging code is not in the standard angular2.js file.
|
||||
* We pass `loader.js` to our application injector using the WORKER_SCRIPT symbol. This tells angular that our WebWorkers's init script is located at `loader.js`.
|
||||
You can think of `loader.js` as the index.html file for the WebWorker.
|
||||
Since WebWorkers share no memory with the UI we need to reload the angular2 dependencies before
|
||||
* We pass `loader.js` to bootstrap and not `app.ts`. You can think of `loader.js` as the `index.html` of the
|
||||
Worker. Since WebWorkers share no memory with the UI we need to reload the angular2 dependencies before
|
||||
bootstrapping our application. We do this with `importScripts`. Additionally, we need to do this in a different
|
||||
file than `app.ts` because our module loader (System.js in this example) has not been loaded yet, and `app.ts`
|
||||
will be compiled with a `System.define` call at the top.
|
||||
@ -98,18 +93,17 @@ For reference, here's the same HelloWorld example in Dart.
|
||||
```
|
||||
```Dart
|
||||
// index.dart
|
||||
import "angular2/core.dart";
|
||||
import "angular2/platform/worker_render.dart";
|
||||
import "package:angular2/web_worker/ui.dart";
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
|
||||
main() {
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.asyncApplication(initIsolate("my_worker.dart"));
|
||||
reflector.reflectionCapabilities = new ReflectionCabilities();
|
||||
bootstrap("app.dart");
|
||||
}
|
||||
```
|
||||
```Dart
|
||||
// background_index.dart
|
||||
import "angular2/core.dart";
|
||||
import "angular2/platform/worker_app.dart";
|
||||
import "package:angular2/web_worker/worker.dart";
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
|
||||
@ -125,27 +119,23 @@ class HelloWorld {
|
||||
|
||||
main(List<String> args, SendPort replyTo) {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
platform([WORKER_APP_PLATFORM, new Provider(RENDER_SEND_PORT, useValue: replyTo)])
|
||||
.application([WORKER_APP_APPLICATION])
|
||||
.bootstrap(RootComponent);
|
||||
bootstrapWebWorker(replyTo, HelloWorld);
|
||||
}
|
||||
|
||||
```
|
||||
This code is nearly the same as the TypeScript version with just a couple key differences:
|
||||
* We don't have a `loader.js` file. Dart applications don't need this file because you don't need a module loader.
|
||||
* We provide a `SendPort` through DI using the token `RENDER_SEND_PORT`. Dart applications use the Isolate API, which communicates via
|
||||
Dart's Port abstraction. When you call `setupIsolate` from the UI thread, angular starts a new Isolate to run
|
||||
* We pass a `SendPort` to `bootstrapWebWorker`. Dart applications use the Isolate API, which communicates via
|
||||
Dart's Port abstraction. When you call `bootstrap` from the UI thread, angular starts a new Isolate to run
|
||||
your application logic. When Dart starts a new Isolate it passes a `SendPort` to that Isolate so that it
|
||||
can communicate with the Isolate that spawned it. You need to provide this `SendPort` through DI
|
||||
can communicate with the Isolate that spawned it. You need to pass this `SendPort` to `bootstrapWebWorker`
|
||||
so that Angular can communicate with the UI.
|
||||
* You need to set up `ReflectionCapabilities` on both the UI and Worker. Just like writing non-concurrent
|
||||
Angular2 Dart applications you need to set up the reflector. You should not use Reflection in production,
|
||||
but should use the angular 2 transformer to remove it in your final JS code. Note there's currently a bug
|
||||
with running the transformer on your UI code (#3971). You can (and should) pass the file where you call
|
||||
`bootstrap` as an entry point to the transformer, but you should not pass your UI index file
|
||||
`bootstrapWebWorker` as an entry point to the transformer, but you should not pass your UI index file
|
||||
to the transformer until that bug is fixed.
|
||||
* In dart we call `asyncApplication` instead of `application` from the render thread because starting an isolate in Dart is asyncronous
|
||||
whereas starting a new WebWorker in JavaScript is a synchronous operation.
|
||||
|
||||
## Writing WebWorker Compatible Components
|
||||
You can do almost everything in a WebWorker component that you can do in a typical Angular 2 Component.
|
||||
@ -212,22 +202,17 @@ can make it easy to catch tricky browser compatibility issues.
|
||||
[MessageBroker](#using-the-messagebroker-in-your-application). However, if you want to control the messaging
|
||||
protocol yourself you can use the MessageBus directly.
|
||||
|
||||
You can obtain the MessageBus on both the render and worker thread through DI.
|
||||
To use the MessageBus you need to initialize a new channel on both the UI and WebWorker.
|
||||
In TypeScript that would look like this:
|
||||
```TypeScript
|
||||
// index.ts, which is running on the UI.
|
||||
import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, MessageBus} from "angular2/platform/worker_render";
|
||||
import {platform} from "angular2/core";
|
||||
|
||||
let appRef = platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
|
||||
let bus = appRef.injector.get(MessageBus);
|
||||
var instance = bootstrap("loader.js");
|
||||
var bus = instance.bus;
|
||||
bus.initChannel("My Custom Channel");
|
||||
```
|
||||
```TypeScript
|
||||
// background_index.ts, which is running on the WebWorker
|
||||
import {MessageBus} from 'angular2/platform/worker_app';
|
||||
import {MessageBus} from 'angular2/web_worker/worker';
|
||||
@Component({...})
|
||||
@View({...})
|
||||
export class MyComponent {
|
||||
@ -241,19 +226,15 @@ Once the channel has been initialized either side can use the `from` and `to` me
|
||||
and receive messages. Both methods return EventEmitter. Expanding on the example from earlier:
|
||||
```TypeScript
|
||||
// index.ts, which is running on the UI.
|
||||
import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, MessageBus} from "angular2/platform/worker_render";
|
||||
import {platform} from "angular2/core";
|
||||
|
||||
let appRef = platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
|
||||
let bus = appRef.injector.get(MessageBus);
|
||||
import {bootstrap} from 'angukar2/web_worker/ui';
|
||||
var instance = bootstrap("loader.js");
|
||||
var bus = instance.bus;
|
||||
bus.initChannel("My Custom Channel");
|
||||
bus.to("My Custom Channel").emit("Hello from the UI");
|
||||
bus.to("My Custom Channel").next("hello from the UI");
|
||||
```
|
||||
```TypeScript
|
||||
// background_index.ts, which is running on the WebWorker
|
||||
import {Component, View} from 'angular2/core';
|
||||
import {MessageBus} from 'angular2/platform/worker_app';
|
||||
import {MessageBus, Component, View} from 'angular2/web_worker/worker';
|
||||
@Component({...})
|
||||
@View({...})
|
||||
export class MyComponent {
|
||||
@ -272,21 +253,16 @@ This example is nearly identical in Dart, and is included below for reference:
|
||||
import 'package:angular2/web_workers/ui.dart';
|
||||
|
||||
main() {
|
||||
import "angular2/core.dart";
|
||||
import "angular2/platform/worker_render.dart";
|
||||
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.asyncApplication(initIsolate("my_worker.dart")).then((ref) {
|
||||
var bus = ref.injector.get(MessageBus);
|
||||
bus.initChannel("My Custom Channel");
|
||||
bus.to("My Custom Channel").add("hello from the UI");
|
||||
});
|
||||
var instance = bootstrap("background_index.dart");
|
||||
var bus = instance.bus;
|
||||
bus.initChannel("My Custom Channel");
|
||||
bus.to("My Custom Channel").add("hello from the UI");
|
||||
}
|
||||
|
||||
```
|
||||
```Dart
|
||||
// background_index.dart, which is running on the WebWorker
|
||||
import 'package:angular2/platform/worker_app.dart';
|
||||
import 'package:angular2/web_worker/worker.dart';
|
||||
@Component(...)
|
||||
@View(...)
|
||||
class MyComponent {
|
||||
@ -322,7 +298,7 @@ change detection. Generally, you want your channels to run inside the zone unles
|
||||
they need to run outside the zone.
|
||||
|
||||
### Implementing and Using a Custom MessageBus
|
||||
**Note:** Implementing and using a Custom MessageBus is experimental and the APIs may change.
|
||||
**Note:** Implementing and using a Custom MessageBus is experimental and requires importing from private APIs.
|
||||
|
||||
If you want to drive your application from something other than a WebWorker you can implement a custom message
|
||||
bus. Implementing a custom message bus just means creating a class that fulfills the API specified by the
|
||||
@ -362,104 +338,54 @@ class JsonMessageBusSource extends GenericMessageBuSource {
|
||||
}
|
||||
```
|
||||
|
||||
Once you've implemented your custom MessageBus in either TypeScript or Dart, you must provide it through DI
|
||||
during bootstrap like so:
|
||||
|
||||
Once you've implemented your custom MessageBus in either TypeScript or Dart, you can tell angular to use it like
|
||||
so:
|
||||
In TypeScript:
|
||||
```TypeScript
|
||||
// index.ts, running on the UI side
|
||||
import {platform, Provider, APP_INITIALIZER, Injector} from 'angular2/core';
|
||||
import {
|
||||
WORKER_RENDER_PLATFORM,
|
||||
WORKER_RENDER_APPLICATION_COMMON,
|
||||
initializeGenericWorkerRenderer,
|
||||
MessageBus
|
||||
} from 'angular2/platform/worker_render';
|
||||
|
||||
import {bootstrapUICommon} from 'angular2/src/web_workers/ui/impl';
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION_COMMON, new Provider(MessageBus, {useValue: bus}),
|
||||
new Provider(APP_INITIALIZER, {
|
||||
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
|
||||
deps: [Injector],
|
||||
multi: true
|
||||
})
|
||||
]);
|
||||
bootstrapUICommon(bus);
|
||||
```
|
||||
```TypeScript
|
||||
// background_index.ts, running on the application side
|
||||
import {WORKER_APP_PLATFORM, genericWorkerAppProviders} from "angular2/platform/worker_app";
|
||||
import {NgZone, platform, Provider} from "angular/core";
|
||||
import {bootstrapWebWorkerCommon} from 'angular2/src/web_workers/worker/application_common';
|
||||
import {MyApp} from './app';
|
||||
|
||||
/**
|
||||
* Do initialization work here to set up the app thread and MessageBus;
|
||||
* Once you have a working MessageBus you should bootstrap your app.
|
||||
*/
|
||||
|
||||
platform([WORKER_APP_PLATFORM_PROVIDERS])
|
||||
.application([WORKER_APP_APPLICATION_COMMON, new Provider(MessageBus, {useValue: bus}),
|
||||
new Provider(APP_INITIALIZER, {useFactory: (zone, bus) => () => initAppThread(zone, bus), multi: true, deps: [NgZone, MessageBus]})])
|
||||
.bootstrap(MyApp);
|
||||
|
||||
function initAppThread(zone: NgZone, bus: MyAwesomeMessageBus): void{
|
||||
/**
|
||||
* Here you can do any initilization work that requires the app providers to be initialized.
|
||||
* At a minimum, you must attach your bus to the zone and setup a DOM adapter.
|
||||
* Depending on your environment you may choose to do more work here.
|
||||
*/
|
||||
}
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
bootstrapWebWorkerCommon(MyApp, bus);
|
||||
```
|
||||
In Dart:
|
||||
```Dart
|
||||
// index.dart, running on the UI side
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:angular2/platform/worker_render.dart';
|
||||
import 'package:angular2/src/web_workers/ui/impl.dart' show bootstrapUICommon;
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
|
||||
main() {
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION_COMMON, new Provider(MessageBus, useValue: bus),
|
||||
new Provider(APP_INITIALIZER,
|
||||
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
|
||||
deps: [Injector],
|
||||
multi: true
|
||||
)
|
||||
]);
|
||||
bootstrapUiCommon(bus);
|
||||
}
|
||||
|
||||
```
|
||||
```Dart
|
||||
// background_index.dart, running on the application side
|
||||
import "package:angular2/platform/worker_app.dart";
|
||||
import "package:angular2/core.dart";
|
||||
import "package:angular2/src/web_workers/worker/application_common.dart" show bootstrapWebWorkerCommon;
|
||||
import "package:angular2/src/core/reflection/reflection.dart";
|
||||
import "package:angular2/src/core/reflection/reflection_capabilities.dart";
|
||||
import "./app.dart" show MyApp;
|
||||
|
||||
main() {
|
||||
/**
|
||||
* Do initialization work here to set up the app thread and MessageBus;
|
||||
* Once you have a working MessageBus you should bootstrap your app.
|
||||
*/
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
platform([WORKER_APP_PLATFORM_PROVIDERS])
|
||||
.application([WORKER_APP_APPLICATION_COMMON, new Provider(MessageBus, useValue: bus),
|
||||
new Provider(APP_INITIALIZER, useFactory: (zone, bus) => () => initAppThread(zone, bus), multi: true, deps: [NgZone, MessageBus])])
|
||||
.bootstrap(MyApp);
|
||||
}
|
||||
|
||||
|
||||
void initAppThread(NgZone zone) {
|
||||
/**
|
||||
* Here you can do any initilization work that requires the app providers to be initialized.
|
||||
* At a minimum, you must attach your bus to the zone and setup a DOM adapter.
|
||||
* Depending on your environment you may choose to do more work here.
|
||||
*/
|
||||
reflector.reflectionCapabilities = new ReflectionCapabilities();
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
bootstrapWebWorkerCommon(MyApp, bus);
|
||||
}
|
||||
```
|
||||
Notice how we use the `WORKER_RENDER_APPLICTION_COMMON` providers instead of the `WORKER_RENDER_APPLICATION` providers on the render thread.
|
||||
This is because the `WORKER_RENDER_APPLICATION` providers include an application initializer that starts a new WebWorker/Isolate.
|
||||
The `WORKER_RENDER_APPLICATION_COMMON` providers make no assumption about where your application code lives.
|
||||
However, we now need to provide our own app initializer. At the very least this initializer needs to call `initializeGenericWorkerRenderer`.
|
||||
Notice how we call `bootstrapUICommon` instead of `bootstrap` from the UI side. `bootstrap` spans a new WebWorker
|
||||
/ Isolate and attaches the default angular MessageBus to it. If you're using a custom MessageBus you are
|
||||
responsible for setting up the application side and initiating communication with it. `bootstrapUICommon` assumes
|
||||
that the given MessageBus is already set up and can communicate with the application.
|
||||
Similarly, we call `bootstrapWebWorkerCommon` instead of `boostrapWebWorker` from the application side. This is
|
||||
because `bootstrapWebWorker` assumes you're using the default angular MessageBus and initializes a new one for you.
|
||||
|
||||
## MessageBroker
|
||||
The MessageBroker is a higher level messaging abstraction that sits on top of the MessageBus. It is used when you
|
||||
@ -481,21 +407,18 @@ MessageBrokers in an application. For a more complete example, check out the `We
|
||||
#### Using the MessageBroker in TypeScript
|
||||
```TypeScript
|
||||
// index.ts, which is running on the UI with a method that we want to expose to a WebWorker
|
||||
import {WORKER_RENDER_PLATFORM, WORKER_RENDER_APPLICATION, WORKER_SCRIPT, ServiceMessageBrokerFactory} from "angular2/platform/worker_render";
|
||||
import {platform} from "angular2/core";
|
||||
import {bootstrap} from 'angular2/web_worker/ui';
|
||||
|
||||
let appRef = platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APPLICATION, new Provider(WORKER_SCRIPT, {useValue: "loader.js"});
|
||||
let injector = instance.injector;
|
||||
var broker = injector.get(ServiceMessageBrokerFactory).createMessageBroker("My Broker Channel");
|
||||
var instance = bootstrap("loader.js");
|
||||
var broker = instance.app.createServiceMessageBroker("My Broker Channel");
|
||||
|
||||
// assume we have some function doCoolThings that takes a string argument and returns a Promise<string>
|
||||
broker.registerMethod("awesomeMethod", [PRIMITIVE], (arg1: string) => doCoolThing(arg1), PRIMITIVE);
|
||||
```
|
||||
```TypeScript
|
||||
// background.ts, which is running on a WebWorker and wants to execute a method on the UI
|
||||
import {Component, View} from 'angular2/core';
|
||||
import {ClientMessageBrokerFactory, PRIMITIVE, UiArguments, FnArgs} from 'angular2/platform/worker_app';
|
||||
import {Component, View, ClientMessageBrokerFactory, PRIMITIVE, UiArguments, FnArgs}
|
||||
from 'angular2/web_worker/worker';
|
||||
|
||||
@Component(...)
|
||||
@View(...)
|
||||
@ -514,24 +437,20 @@ export class MyComponent {
|
||||
#### Using the MessageBroker in Dart
|
||||
```Dart
|
||||
// index.dart, which is running on the UI with a method that we want to expose to a WebWorker
|
||||
import "angular2/core.dart";
|
||||
import "angular2/platform/worker_render.dart";
|
||||
import 'package:angular2/web_worker/ui.dart';
|
||||
|
||||
main() {
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.asyncApplication(initIsolate("my_worker.dart")).then((ref) {
|
||||
var broker = ref.injector.get(ServiceMessageBrokerFactory).createMessageBroker("My Broker Channel");
|
||||
var instance = bootstrap("background.dart");
|
||||
var broker = instance.app.createServiceMessageBroker("My Broker Channel");
|
||||
|
||||
// assume we have some function doCoolThings that takes a String argument and returns a Future<String>
|
||||
broker.registerMethod("awesomeMethod", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);
|
||||
});
|
||||
// assume we have some function doCoolThings that takes a String argument and returns a Future<String>
|
||||
broker.registerMethod("awesomeMethod", [PRIMITIVE], (String arg1) => doCoolThing(arg1), PRIMITIVE);
|
||||
}
|
||||
|
||||
```
|
||||
```Dart
|
||||
// background.dart, which is running on a WebWorker and wants to execute a method on the UI
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:angular2/platform/worker_app.dart';
|
||||
import 'package:angular2/web_worker/worker.dart';
|
||||
|
||||
@Component(...)
|
||||
@View(...)
|
||||
|
@ -1,6 +0,0 @@
|
||||
# API Examples
|
||||
|
||||
This folder contains small example apps that get in-lined into our API docs.
|
||||
These examples are written with idiomatic TypeScript, and are not transpiled to Dart.
|
||||
Each example contains tests for application behavior (as opposed to testing Angular's
|
||||
behavior) just like an Angular application developer would write.
|
@ -1,30 +0,0 @@
|
||||
import {Component} from 'angular2/core';
|
||||
import {MinLengthValidator, MaxLengthValidator} from 'angular2/common';
|
||||
|
||||
// #docregion min
|
||||
@Component({
|
||||
selector: 'min-cmp',
|
||||
directives: [MinLengthValidator],
|
||||
template: `
|
||||
<form>
|
||||
<p>Year: <input ngControl="year" minlength="2"></p>
|
||||
</form>
|
||||
`
|
||||
})
|
||||
class MinLengthTestComponent {
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
// #docregion max
|
||||
@Component({
|
||||
selector: 'max-cmp',
|
||||
directives: [MaxLengthValidator],
|
||||
template: `
|
||||
<form>
|
||||
<p>Year: <input ngControl="year" maxlength="4"></p>
|
||||
</form>
|
||||
`
|
||||
})
|
||||
class MaxLengthTestComponent {
|
||||
}
|
||||
// #enddocregion
|
@ -1,19 +0,0 @@
|
||||
import {provide} from 'angular2/core';
|
||||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {UrlResolver} from 'angular2/compiler';
|
||||
|
||||
var MyApp: any;
|
||||
|
||||
// #docregion url_resolver
|
||||
class MyUrlResolver extends UrlResolver {
|
||||
resolve(baseUrl: string, url: string): string {
|
||||
// Serve CSS files from a special CDN.
|
||||
if (url.substr(-4) === '.css') {
|
||||
return super.resolve('http://cdn.myapp.com/css/', url);
|
||||
}
|
||||
return super.resolve(baseUrl, url);
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap(MyApp, [provide(UrlResolver, {useClass: MyUrlResolver})]);
|
||||
// #enddocregion
|
@ -1,8 +0,0 @@
|
||||
import {DebugElement} from 'angular2/core';
|
||||
|
||||
var debugElement: DebugElement;
|
||||
var predicate: any;
|
||||
|
||||
// #docregion scope_all
|
||||
debugElement.query(predicate);
|
||||
// #enddocregion
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user