Compare commits
15 Commits
2.0.0-rc.5
...
starting
Author | SHA1 | Date | |
---|---|---|---|
2800683e64 | |||
a1158a06bd | |||
35a376acdb | |||
b8f0209ba5 | |||
9386bb1cb1 | |||
01592ff773 | |||
2a5d0dd59a | |||
e07a2d10c7 | |||
7234d368e3 | |||
cdc83730df | |||
0f30ce6e92 | |||
fb6b8b22a1 | |||
853c24f4d4 | |||
e37f58a228 | |||
e569e0b1ee |
@ -1,3 +0,0 @@
|
||||
Language: JavaScript
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -3,6 +3,3 @@
|
||||
|
||||
# JS files must always use LF for tools to work
|
||||
*.js eol=lf
|
||||
|
||||
# Must keep Windows line ending to be parsed correctly
|
||||
scripts/windows/packages.txt eol=crlf
|
||||
|
33
.github/ISSUE_TEMPLATE.md
vendored
33
.github/ISSUE_TEMPLATE.md
vendored
@ -1,33 +0,0 @@
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
```
|
||||
[ ] bug report
|
||||
[ ] feature request
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
|
||||
**Current behavior**
|
||||
|
||||
|
||||
**Expected/desired behavior**
|
||||
|
||||
|
||||
**Reproduction of the problem**
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
|
||||
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
|
||||
|
||||
|
||||
**Please tell us about your environment:**
|
||||
|
||||
* **Angular version:** 2.0.0-rc.X
|
||||
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,36 +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?** (check one with "x")
|
||||
```
|
||||
[ ] Bugfix
|
||||
[ ] Feature
|
||||
[ ] Code style update (formatting, local variables)
|
||||
[ ] Refactoring (no functional changes, no api changes)
|
||||
[ ] Build related changes
|
||||
[ ] CI related changes
|
||||
[ ] Other... Please describe:
|
||||
```
|
||||
|
||||
**What is the current behavior?** (You can also link to an open issue here)
|
||||
|
||||
|
||||
|
||||
**What is the new behavior?**
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?** (check one with "x")
|
||||
```
|
||||
[ ] Yes
|
||||
[ ] No
|
||||
```
|
||||
|
||||
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
|
||||
|
||||
|
||||
**Other information**:
|
||||
|
29
.gitignore
vendored
29
.gitignore
vendored
@ -1,18 +1,11 @@
|
||||
.DS_STORE
|
||||
|
||||
# Don’t commit the following directories created by pub.
|
||||
packages
|
||||
pubspec.lock
|
||||
.pub
|
||||
.packages
|
||||
|
||||
/dist/
|
||||
packages/
|
||||
.buildlog
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
# Or broccoli working directory
|
||||
tmp
|
||||
.pub
|
||||
.DS_STORE
|
||||
|
||||
# Or the files created by dart2js.
|
||||
*.dart.js
|
||||
@ -25,25 +18,9 @@ tmp
|
||||
pubspec.lock
|
||||
.c9
|
||||
.idea/
|
||||
.settings/
|
||||
*.swo
|
||||
modules/.settings
|
||||
.vscode
|
||||
modules/.vscode
|
||||
|
||||
# Don't check in secret files
|
||||
*secret.js
|
||||
|
||||
# Ignore npm debug log
|
||||
npm-debug.log
|
||||
|
||||
/docs/bower_components/
|
||||
|
||||
# build-analytics
|
||||
.build-analytics
|
||||
|
||||
# built dart payload tests
|
||||
/modules_dart/payload/**/build
|
||||
|
||||
# rollup-test output
|
||||
/modules/rollup-test/dist/
|
||||
|
214
.travis.yml
214
.travis.yml
@ -1,196 +1,38 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '5.4.1'
|
||||
|
||||
addons:
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
# needed to install g++ that is used by npms's native modules
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ./node_modules
|
||||
- ./.chrome/chromium
|
||||
# - $HOME/.pub-cache
|
||||
|
||||
|
||||
#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
|
||||
|
||||
- '0.10'
|
||||
env:
|
||||
global:
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - ARCH=linux-x64
|
||||
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
- KARMA_BROWSERS=DartiumWithWebPlatform
|
||||
- E2E_BROWSERS=Dartium
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- ARCH=linux-x64
|
||||
matrix:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
- CI_MODE=js
|
||||
- CI_MODE=e2e
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "CI_MODE=saucelabs_optional"
|
||||
- env: "CI_MODE=browserstack_optional"
|
||||
|
||||
|
||||
install:
|
||||
- ./scripts/ci-lite/install.sh
|
||||
- MODE=js DART_CHANNEL=dev
|
||||
# Dissabled until Dart v1.9 hits stable
|
||||
# - MODE=dart DART_CHANNEL=stable
|
||||
- MODE=dart DART_CHANNEL=dev
|
||||
|
||||
before_install:
|
||||
- export DISPLAY=:99.0
|
||||
- export GIT_SHA=$(git rev-parse HEAD)
|
||||
- ./scripts/ci/init_android.sh
|
||||
- ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${ARCH}
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [[ -e SKIP_TRAVIS_TESTS ]]; then { cat SKIP_TRAVIS_TESTS ; exit 0; } fi
|
||||
before_script:
|
||||
|
||||
|
||||
- mkdir -p $LOGS_DIR
|
||||
script:
|
||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||
|
||||
- ./scripts/ci/build_and_test.sh ${MODE}
|
||||
after_script:
|
||||
- ./scripts/ci-lite/cleanup.sh
|
||||
|
||||
|
||||
#branches:
|
||||
# except:
|
||||
# - g3_v2_0
|
||||
#
|
||||
#cache:
|
||||
# directories:
|
||||
# - $HOME/.pub-cache
|
||||
# - $HOME/.chrome/chromium
|
||||
#
|
||||
#before_cache:
|
||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
||||
#
|
||||
#env:
|
||||
# global:
|
||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
# - CXX=g++-4.8
|
||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
||||
# - E2E_BROWSERS=ChromeOnTravis
|
||||
# - LOGS_DIR=/tmp/angular-build/logs
|
||||
# - SAUCE_USERNAME=angular-ci
|
||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
# - BROWSER_STACK_USERNAME=angularteam1
|
||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
||||
# - ARCH=linux-x64
|
||||
# - DART_DEV_VERSION=latest
|
||||
# - DART_STABLE_VERSION=latest
|
||||
# - DART_CHANNEL=stable
|
||||
# - DART_VERSION=$DART_STABLE_VERSION
|
||||
# # Token for tsd to increase github rate limit
|
||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
||||
# # because those are not visible for pull requests, and those should also be reliable.
|
||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
||||
# # (password is in Valentine)
|
||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
||||
# # GITHUB_TOKEN_ANGULAR
|
||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
# matrix:
|
||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
# - MODE=dart
|
||||
# - MODE=dart DART_CHANNEL=dev
|
||||
# - MODE=saucelabs_required
|
||||
# - MODE=browserstack_required
|
||||
# - MODE=saucelabs_optional
|
||||
# - MODE=browserstack_optional
|
||||
# - MODE=dart_ddc
|
||||
# - MODE=js
|
||||
# - MODE=router
|
||||
# - MODE=build_only
|
||||
# - MODE=typescript_next
|
||||
# - MODE=lint
|
||||
#
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - env: "MODE=saucelabs_optional"
|
||||
# - env: "MODE=browserstack_optional"
|
||||
#
|
||||
#addons:
|
||||
# firefox: "38.0"
|
||||
# apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
# packages:
|
||||
# - g++-4.8
|
||||
#
|
||||
#before_install:
|
||||
# - node tools/analytics/build-analytics start ci job
|
||||
# - node tools/analytics/build-analytics start ci before_install
|
||||
# - echo ${TSDRC} > .tsdrc
|
||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
||||
# - export DISPLAY=:99.0
|
||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
||||
# - ./scripts/ci/init_android.sh
|
||||
# - sh -e /etc/init.d/xvfb start
|
||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
||||
# - node tools/analytics/build-analytics success ci before_install
|
||||
#
|
||||
#install:
|
||||
# - node tools/analytics/build-analytics start ci install
|
||||
# # Install version of npm that we are locked against
|
||||
# - npm install -g npm@3.5.3
|
||||
# # Install version of Chromium that we are locked against
|
||||
# - ./scripts/ci/install_chromium.sh
|
||||
# # Install version of Dart based on the matrix build variables
|
||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
||||
# # Print the size of caches to ease debugging
|
||||
# - du -sh ./node_modules || true
|
||||
# # Install npm dependecies
|
||||
# # check-node-modules will exit(1) if we don't need to install
|
||||
# # we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
# - node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
# - node tools/analytics/build-analytics success ci install
|
||||
#
|
||||
#before_script:
|
||||
# - node tools/analytics/build-analytics start ci before_script
|
||||
# - mkdir -p $LOGS_DIR
|
||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
||||
# - node tools/analytics/build-analytics success ci before_script
|
||||
#
|
||||
#script:
|
||||
# - node tools/analytics/build-analytics start ci script
|
||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
||||
# - node tools/analytics/build-analytics success ci script
|
||||
#
|
||||
#after_script:
|
||||
# - node tools/analytics/build-analytics start ci after_script
|
||||
# - ./scripts/ci/print-logs.sh
|
||||
# - ./scripts/ci/after-script.sh
|
||||
# - ./scripts/publish/publish-build-artifacts.sh
|
||||
# - node tools/analytics/build-analytics success ci after_script
|
||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
||||
#
|
||||
#notifications:
|
||||
# webhooks:
|
||||
# urls:
|
||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
||||
# - https://buildtimetrend.herokuapp.com/travis
|
||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
||||
# on_success: always # options: [always|never|change] default: always
|
||||
# on_failure: always # options: [always|never|change] default: always
|
||||
# on_start: never # default: never
|
||||
# slack:
|
||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
- ./scripts/ci/print-logs.sh
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
3812
CHANGELOG.md
3812
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
34
COMMITTER.md
34
COMMITTER.md
@ -1,34 +0,0 @@
|
||||
# Pushing changes into the Angular 2 tree
|
||||
|
||||
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
|
||||
for details about how we maintain a linear commit history, and the rules for committing.
|
||||
|
||||
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
|
||||
Someone with committer access will do the rest.
|
||||
|
||||
## The `PR: merge` label and `presubmit-*` branches
|
||||
|
||||
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.
|
||||
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}`.
|
||||
|
||||
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
|
||||
|
||||
When a Travis build succeeds for a presubmit branch named following the convention,
|
||||
Travis will re-base the commits, merge to master, and close the PR automatically.
|
||||
|
||||
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.
|
||||
Edit the contents of the [CoreTeamMember Table](
|
||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
||||
## <a name="question"></a> Got a Question or Problem?
|
||||
|
||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||
discussion list or [StackOverflow][stackoverflow]. We are also available on [Gitter][gitter].
|
||||
|
||||
## <a name="issue"></a> Found an Issue?
|
||||
If you find a bug in the source code or a mistake in the documentation, you can help us by
|
||||
@ -27,16 +27,27 @@ If you find a bug in the source code or a mistake in the documentation, you can
|
||||
|
||||
## <a name="feature"></a> Want a Feature?
|
||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
||||
and we are not ready to accept major contributions ahead of the full release.
|
||||
Please consider what kind of change it is:
|
||||
Repository][github]. If you would like to *implement* a new feature then consider what kind of
|
||||
change it is:
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||
and help you to craft the change so that it is successfully accepted into the project.
|
||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="docs"></a> Want a Doc Fix?
|
||||
If you want to help improve the docs, then consider what kind of improvement it is:
|
||||
|
||||
* For **Major Changes**, it's a good idea to let others know what you're working on to
|
||||
minimize duplication of effort. Before starting, check out the issue queue for
|
||||
issues labeled [#docs](https://github.com/angular/angular/labels/%23docs).
|
||||
Comment on an issue to let others know what you're working on, or [create a new issue](#submit-issue)
|
||||
if your work doesn't fit within the scope of any of the existing doc issues.
|
||||
Please build and test the documentation before [submitting the Pull Request](#submit-pr), to be sure
|
||||
you haven't accidentally introduced any layout or formatting issues. Also ensure that your commit
|
||||
message is labeled "docs" and follows the [Commit Message Guidelines](#commit) given below.
|
||||
* For **Small Changes**, there is no need to file an issue first. Simply [submit a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
### <a name="submit-issue"></a> Submitting an Issue
|
||||
@ -48,22 +59,19 @@ 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
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **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:
|
||||
|
||||
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
* Search [GitHub](https://github.com/angular/angular.dart/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without this.
|
||||
@ -95,7 +103,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular 2 test suites to ensure tests are still passing.
|
||||
* Re-run the Angular 2 test suites for JS and Dart to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
@ -139,9 +147,9 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||
* All public API methods **must be documented**. (Details TBC).
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available, see
|
||||
[DEVELOPER.md](DEVELOPER.md#clang-format).
|
||||
* With the exceptions listed below, we follow the rules contained in
|
||||
[Google's JavaScript Style Guide][js-style-guide]:
|
||||
* Wrap all code at **100 characters**.
|
||||
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
|
||||
@ -161,27 +169,9 @@ format that includes a **type**, a **scope** and a **subject**:
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
|
||||
to read on GitHub as well as in various git tools.
|
||||
|
||||
Footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
|
||||
|
||||
Samples: (even more [samples](https://github.com/angular/angular/commits/master))
|
||||
|
||||
```
|
||||
docs(changelog): update change log to beta.5
|
||||
```
|
||||
```
|
||||
fix(release): need to depend on latest rxjs and zone.js
|
||||
|
||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||
```
|
||||
|
||||
### Revert
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
### Type
|
||||
Must be one of the following:
|
||||
|
||||
@ -190,15 +180,14 @@ Must be one of the following:
|
||||
* **docs**: Documentation only changes
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||
semi-colons, etc)
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **refactor**: A code change that neither fixes a bug or 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
|
||||
The scope could be anything specifying place of the commit change. For example
|
||||
`Compiler`, `ElementInjector`, etc.
|
||||
|
||||
### Subject
|
||||
@ -216,7 +205,6 @@ The body should include the motivation for the change and contrast this with pre
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to
|
||||
reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
@ -238,8 +226,8 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[github]: https://github.com/angular/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[js-style-guide]: https://google.github.io/styleguide/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[runnable]: http://runnable.com
|
||||
[runnable]: http://runnable.com/
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular
|
||||
|
229
DEVELOPER.md
229
DEVELOPER.md
@ -3,45 +3,56 @@
|
||||
This document describes how to set up your development environment to build and test Angular, both
|
||||
JS and Dart versions. It also explains the basic mechanics of using `git`, `node`, and `npm`.
|
||||
|
||||
* [Prerequisite Software](#prerequisite-software)
|
||||
* [Getting the Sources](#getting-the-sources)
|
||||
* [Installing NPM Modules](#installing-npm-modules)
|
||||
* [Building](#building)
|
||||
* [Running Tests Locally](#running-tests-locally)
|
||||
See the [contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
for how to contribute your own code to
|
||||
|
||||
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
|
||||
if you'd like to contribute to Angular.
|
||||
1. [Prerequisite Software](#prerequisite-software)
|
||||
2. [Getting the Sources](#getting-the-sources)
|
||||
3. [Environment Variable Setup](#environment-variable-setup)
|
||||
4. [Installing NPM Modules and Dart Packages](#installing-npm-modules-and-dart-packages)
|
||||
5. [Running Tests Locally](#running-tests-locally)
|
||||
6. [Project Information](#project-information)
|
||||
7. [CI using Travis](#ci-using-travis)
|
||||
8. [Debugging](#debugging)
|
||||
|
||||
## Prerequisite Software
|
||||
|
||||
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
|
||||
* [Dart](https://www.dartlang.org) (version `>=1.9.0-dev.8.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)
|
||||
download [page for instructions](https://www.dartlang.org/tools/download.html); note that you can
|
||||
download both **stable** and **dev** channel versions from the [download
|
||||
archive](https://www.dartlang.org/tools/download-archive).
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **Github app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)): the [Github 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.
|
||||
* [Node.js](http://nodejs.org) which is used to run a development web server, run tests, and
|
||||
generate distributable files. We also use Node's Package Manager (`npm`). 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).
|
||||
|
||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||
to execute the selenium standalone server for e2e testing.
|
||||
|
||||
## Getting the Sources
|
||||
|
||||
Fork and clone the Angular repository:
|
||||
Forking and cloning the Angular repository:
|
||||
|
||||
1. Login to your GitHub account or create one by following the instructions given
|
||||
1. Login to your Github account or create one by following the instructions given
|
||||
[here](https://github.com/signup/free).
|
||||
2. [Fork](http://help.github.com/forking) the [main Angular
|
||||
repository](https://github.com/angular/angular).
|
||||
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
|
||||
the Angular repository that you forked in the first place.
|
||||
the Angular repository that you forked in the first place:
|
||||
|
||||
```shell
|
||||
# Clone your GitHub repository:
|
||||
# Clone your Github repository:
|
||||
git clone git@github.com:<github username>/angular.git
|
||||
|
||||
# Go to the Angular directory:
|
||||
@ -50,91 +61,175 @@ cd angular
|
||||
# Add the main Angular repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.git
|
||||
```
|
||||
## Installing NPM Modules
|
||||
|
||||
Next, install the JavaScript modules and Dart packages needed to build and test Angular:
|
||||
## Environment Variable Setup
|
||||
|
||||
Define the environment variables listed below. These are mainly needed for the testing. The
|
||||
notation shown here is for [`bash`](http://www.gnu.org/software/bash); adapt as appropriate for
|
||||
your favorite shell.
|
||||
|
||||
Examples given below of possible values for initializing the environment variables assume **Mac OS
|
||||
X** and that you have installed the Dart Editor in the directory named by
|
||||
`DART_EDITOR_DIR=/Applications/dart`. This is only for illustrative purposes.
|
||||
|
||||
```shell
|
||||
# DARTIUM_BIN: path to a Dartium browser executable; used by Karma to run Dart tests
|
||||
export DARTIUM_BIN="$DART_EDITOR_DIR/chromium/Chromium.app/Contents/MacOS/Chromium"
|
||||
```
|
||||
|
||||
Add the Dart SDK `bin` directory to your path and/or define `DART_SDK` (this is also detailed
|
||||
[here](https://www.dartlang.org/tools/pub/installing.html)):
|
||||
|
||||
```shell
|
||||
# DART_SDK: path to a Dart SDK directory
|
||||
export DART_SDK="$DART_EDITOR_DIR/dart-sdk"
|
||||
|
||||
# Update PATH to include the Dart SDK bin directory
|
||||
PATH+=":$DART_SDK/bin"
|
||||
```
|
||||
|
||||
## Installing NPM Modules and Dart Packages
|
||||
|
||||
Next, install the modules and packages needed to build Angular and run tests:
|
||||
|
||||
```shell
|
||||
# Install Angular project dependencies (package.json)
|
||||
npm install
|
||||
|
||||
# Ensure protractor has the latest webdriver
|
||||
$(npm bin)/webdriver-manager update
|
||||
|
||||
# Install Dart packages
|
||||
pub get
|
||||
```
|
||||
|
||||
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
|
||||
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
|
||||
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
|
||||
|
||||
*Option 1*: globally installing these two packages as follows:
|
||||
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by
|
||||
globally installing these two packages as follows:
|
||||
|
||||
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
|
||||
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
|
||||
|
||||
Since global installs can become stale, and required versions can vary by project, we avoid their
|
||||
use in these instructions.
|
||||
Since global installs can become stale, we avoid their use in these instructions.
|
||||
|
||||
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
|
||||
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
|
||||
## Build commands
|
||||
|
||||
## Windows only
|
||||
To build Angular and prepare tests run
|
||||
|
||||
In order to create the right symlinks, run **as administrator**:
|
||||
```shell
|
||||
./scripts/windows/create-symlinks.sh
|
||||
$(npm bin)/gulp build
|
||||
```
|
||||
|
||||
Before submitting a PR, do not forget to remove them:
|
||||
Notes:
|
||||
* Results are put in the `dist` folder.
|
||||
* This will also run `pub get` for the subfolders in `modules` and run `dartanalyzer` for
|
||||
every file that matches `<module>/src/<module>.dart`, e.g. `di/src/di.dart`
|
||||
|
||||
To clean out the `dist` folder use:
|
||||
```shell
|
||||
./scripts/windows/remove-symlinks.sh
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build Angular run:
|
||||
|
||||
```shell
|
||||
./build.sh
|
||||
$(npm bin)/gulp clean
|
||||
```
|
||||
|
||||
* Results are put in the dist folder.
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
To run tests:
|
||||
### Basic tests
|
||||
|
||||
```shell
|
||||
$ ./test.sh node # Run all angular tests on node
|
||||
1. `$(npm bin)/gulp test.unit.js`: JS tests in a browser; runs in **watch mode** (i.e. karma
|
||||
watches the test files for changes and re-runs tests when files are updated).
|
||||
2. `$(npm bin)/gulp test.unit.cjs`: JS tests in NodeJS; runs in **watch mode**
|
||||
3. `$(npm bin)/gulp test.unit.dart`: Dart tests in Dartium; runs in **watch mode**.
|
||||
|
||||
$ ./test.sh browser # Run all angular tests in browser
|
||||
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
|
||||
If you prefer running tests in "single-run" mode rather than watch mode use
|
||||
|
||||
$ ./test.sh tools # Run angular tooling (not framework) tests
|
||||
```
|
||||
* `$(npm bin)/gulp test.unit.js/ci`
|
||||
* `$(npm bin)/gulp test.unit.dart/ci`
|
||||
|
||||
You should execute the 3 test suites before submitting a PR to github.
|
||||
**Note**: If you want to only run a single test you can alter the test you wish
|
||||
to run by changing `it` to `iit` or `describe` to `ddescribe`. This will only
|
||||
run that individual test and make it much easier to debug. `xit` and `xdescribe`
|
||||
can also be useful to exclude a test and a group of tests respectively.
|
||||
|
||||
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
|
||||
**Note** for transpiler tests: The karma preprocessor is setup in a way so that after every test
|
||||
run the transpiler is reloaded. With that it is possible to make changes to the preprocessor and
|
||||
run the tests without exiting karma (just touch a test file that you would like to run).
|
||||
|
||||
- CircleCI fails if your code is not formatted properly,
|
||||
- Travis CI fails if any of the test suite describe above fails.
|
||||
### E2e tests
|
||||
|
||||
## Update the public API tests
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder).
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver).
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js`: JS e2e tests.
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js`: Dart2JS e2e tests.
|
||||
|
||||
If you happen to modify the public API of Angular, API golden files must be updated using:
|
||||
Angular specific command line options when running protractor:
|
||||
- `$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
``` shell
|
||||
$ gulp public-api:update
|
||||
```
|
||||
### Performance tests
|
||||
|
||||
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
|
||||
1. `$(npm bin)/gulp build.js.cjs` (builds benchpress and tests into `dist/js/cjs` folder)
|
||||
2. `$(npm bin)/gulp serve.js.prod serve.js.dart2js` (runs local webserver)
|
||||
3. `$(npm bin)/protractor protractor-js.conf.js --benchmark`: JS performance tests
|
||||
4. `$(npm bin)/protractor protractor-dart2js.conf.js --benchmark`: Dart2JS performance tests
|
||||
|
||||
## Formatting your source code
|
||||
Angular specific command line options when running protractor (e.g. force gc, ...):
|
||||
`$(npm bin)/protractor protractor-{js|dart2js}-conf.js --ng-help`
|
||||
|
||||
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
|
||||
is not properly formatted, the CI will fail and the PR can not be merged.
|
||||
## Project Information
|
||||
|
||||
You can automatically format your code by running:
|
||||
### Folder structure
|
||||
|
||||
``` shell
|
||||
$ gulp format
|
||||
```
|
||||
* `modules/*`: modules that will be loaded in the browser
|
||||
* `tools/*`: tools that are needed to build Angular
|
||||
* `dist/*`: build files are placed here.
|
||||
|
||||
### File suffixes
|
||||
|
||||
* `*.js`: javascript files that get transpiled to Dart and EcmaScript 5
|
||||
* `*.es6`: javascript files that get transpiled only to EcmaScript 5
|
||||
* `*.es5`: javascript files that don't get transpiled
|
||||
* `*.dart`: dart files that don't get transpiled
|
||||
|
||||
## CI using Travis
|
||||
|
||||
For instructions on setting up Continuous Integration using Travis, see the instructions given
|
||||
[here](https://github.com/angular/angular.dart/blob/master/travis.md).
|
||||
|
||||
## Debugging
|
||||
|
||||
### Debug the transpiler
|
||||
|
||||
If you need to debug the transpiler:
|
||||
|
||||
- add a `debugger;` statement in the transpiler code,
|
||||
- from the root folder, execute `node debug $(npm bin)/gulp build` to enter the node
|
||||
debugger
|
||||
- press "c" to execute the program until you reach the `debugger;` statement,
|
||||
- you can then type "repl" to enter the REPL and inspect variables in the context.
|
||||
|
||||
See the [Node.js manual](http://nodejs.org/api/debugger.html) for more information.
|
||||
|
||||
Notes:
|
||||
- You can also execute `node $(npm bin)/karma start karma-dart.conf.js` depending on which
|
||||
code you want to debug (the former will process the "modules" folder while the later processes
|
||||
the transpiler specs).
|
||||
- You can also add `debugger;` statements in the specs (JavaScript). The execution will halt when
|
||||
the developer tools are opened in the browser running Karma.
|
||||
|
||||
### Debug the tests
|
||||
|
||||
If you need to debug the tests:
|
||||
|
||||
- add a `debugger;` statement to the test you want to debug (oe the source code),
|
||||
- execute karma `$(npm bin)/gulp test.js`,
|
||||
- press the top right "DEBUG" button,
|
||||
- open the dev tools and press F5,
|
||||
- the execution halt at the `debugger;` statement
|
||||
|
||||
**Note (WebStorm users)**:
|
||||
You can create a Karma run config from WebStorm.
|
||||
Then in the "Run" menu, press "Debug 'karma-js.conf.js'", WebStorm will stop in the generated code
|
||||
on the `debugger;` statement.
|
||||
You can then step into the code and add watches.
|
||||
The `debugger;` statement is needed because WebStorm will stop in a transpiled file. Breakpoints in
|
||||
the original source files are not supported at the moment.
|
||||
|
||||
|
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.
|
||||
|
32
NAMING.md
32
NAMING.md
@ -1,32 +0,0 @@
|
||||
Naming Conventions in Angular2
|
||||
---
|
||||
|
||||
In general Angular2 should follow TypeScript naming conventions.
|
||||
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
|
||||
|
||||
|
||||
Classes:
|
||||
- Example: `Compiler`, `ApplicationMetadata`
|
||||
- Camel case with first letter upper-case
|
||||
- In general prefer single words. (This is so that when appending `Proto` or `Factory` the class
|
||||
is still reasonable to work with.)
|
||||
- Should not end with `Impl` or any other word which describes a specific implementation of an
|
||||
interface.
|
||||
|
||||
|
||||
Interfaces:
|
||||
- Follow the same rules as Classes
|
||||
- Should not have `I` or `Interface` in the name or any other way of identifying it as an interface.
|
||||
|
||||
|
||||
Methods and functions:
|
||||
- Example: `bootstrap`, `someMethod`
|
||||
- Should be camel case with first lower case
|
||||
|
||||
|
||||
Constants
|
||||
- Example: `CORE_DIRECTIVES`
|
||||
- Should be all uppercase with SNAKE_CASE
|
||||
|
||||
|
||||
|
55
README.md
55
README.md
@ -1,39 +1,52 @@
|
||||
[](https://travis-ci.org/angular/angular)
|
||||
[](https://circleci.com/gh/angular/angular/tree/master)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](http://issuestats.com/github/angular/angular)
|
||||
[](https://badge.fury.io/js/%40angular%2Fcore)
|
||||
[](https://npmjs.org/package/angular2)
|
||||
|
||||
[](https://saucelabs.com/u/angular2-ci)
|
||||
|
||||
Angular
|
||||
Angular [](https://travis-ci.org/angular/angular) [](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
=========
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
||||
|
||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
|
||||
Angular 2 is currently in **Release Candidate**.
|
||||
Angular 2 is currently in **Alpha Preview**. We recommend using Angular 1.X for production
|
||||
applications:
|
||||
|
||||
## Quickstart
|
||||
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
|
||||
## Want to help?
|
||||
|
||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||
Want to file a bug, or contribute some code or improve documentation? Excellent! Read up on our
|
||||
guidelines for [contributing][contributing].
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
To see the examples, first build the project as described
|
||||
[here](http://github.com/angular/angular/blob/master/DEVELOPER.md).
|
||||
|
||||
### Hello World Example
|
||||
|
||||
This example consists of three basic pieces - a component, a decorator and a
|
||||
service. They are all constructed via injection. For more information see the
|
||||
comments in the source `modules/examples/src/hello_world/index.js`.
|
||||
|
||||
You can build this example as either JS or Dart app:
|
||||
|
||||
* JS:
|
||||
* `$(npm bin)/gulp serve.js.dev`, and
|
||||
* open `localhost:8000/examples/src/hello_world/` in Chrome.
|
||||
* Dart:
|
||||
* `$(npm bin)/gulp serve/examples.dart`, and
|
||||
* open `localhost:8080/src/hello_world` in Chrome (for dart2js) or
|
||||
[Dartium][dartium] (for Dart VM).
|
||||
|
||||
[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
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
[ng2dart]: https://github.com/dart-lang/angular2
|
||||
|
4
TOOLS.md
4
TOOLS.md
@ -1,4 +0,0 @@
|
||||
# Developer Tools for Angular 2
|
||||
|
||||
- [JavaScript](TOOLS_JS.md)
|
||||
- [Dart](TOOLS_DART.md)
|
140
TOOLS_JS.md
140
TOOLS_JS.md
@ -1,140 +0,0 @@
|
||||
# Developer Tools for JavaScript
|
||||
|
||||
Here you will find a collection of tools and tips for keeping your application
|
||||
perform well and contain fewer bugs.
|
||||
|
||||
## Angular debug tools in the dev console
|
||||
|
||||
Angular provides a set of debug tools that are accessible from any browser's
|
||||
developer console. In Chrome the dev console can be accessed by pressing
|
||||
Ctrl + Shift + j.
|
||||
|
||||
### Enabling debug tools
|
||||
|
||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||
|
||||
```typescript
|
||||
import {enableDebugTools} from '@angular/platform-browser';
|
||||
|
||||
bootstrap(Application).then((appRef) => {
|
||||
enableDebugTools(appRef);
|
||||
});
|
||||
```
|
||||
|
||||
### Using debug tools
|
||||
|
||||
In the browser open the developer console (Ctrl + Shift + j in Chrome). The
|
||||
top level object is called `ng` and contains more specific tools inside it.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Change detection profiler
|
||||
|
||||
If your application is janky (it misses frames) or is slow according to other
|
||||
metrics it is important to find the root cause of the issue. Change detection
|
||||
is a phase in Angular's lifecycle that detects changes in values that are
|
||||
bound to UI, and if it finds a change it performs the corresponding UI update.
|
||||
However, sometimes it is hard to tell if the slowness is due to the act of
|
||||
computing the changes being slow, or due to the act of applying those changes
|
||||
to the UI. For your application to be performant it is important that the
|
||||
process of computing changes is very fast. For best results it should be under
|
||||
3 milliseconds in order to leave room for the application logic, the UI updates
|
||||
and browser's rendering pipeline to fit withing the 16 millisecond frame
|
||||
(assuming the 60 FPS target frame rate).
|
||||
|
||||
Change detection profiler repeatedly performs change detection without invoking
|
||||
any user actions, such as clicking buttons or entering text in input fields. It
|
||||
then computes the average amount of time it took to perform a single cycle of
|
||||
change detection in milliseconds and prints it to the console. This number
|
||||
depends on the current state of the UI. You will likely see different numbers
|
||||
as you go from one screen in your application to another.
|
||||
|
||||
#### Running the profiler
|
||||
|
||||
Enable debug tools (see above), then in the dev console enter the following:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection();
|
||||
```
|
||||
|
||||
The results will be printed to the console.
|
||||
|
||||
#### Recording CPU profile
|
||||
|
||||
Pass `{record: true}` an argument:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then open the "Profiles" tab. You will see the recorded profile titled
|
||||
"Change Detection". In Chrome, if you record the profile repeatedly, all the
|
||||
profiles will be nested under "Change Detection".
|
||||
|
||||
#### Interpreting the numbers
|
||||
|
||||
In a properly-designed application repeated attempts to detect changes without
|
||||
any user actions should result in no changes to be applied on the UI. It is
|
||||
also desirable to have the cost of a user action be proportional to the amount
|
||||
of UI changes required. For example, popping up a menu with 5 items should be
|
||||
vastly faster than rendering a table of 500 rows and 10 columns. Therefore,
|
||||
change detection with no UI updates should be as fast as possible. Ideally the
|
||||
number printed by the profiler should be well below the length of a single
|
||||
animation frame (16ms). A good rule of thumb is to keep it under 3ms.
|
||||
|
||||
#### Investigating slow change detection
|
||||
|
||||
So you found a screen in your application on which the profiler reports a very
|
||||
high number (i.e. >3ms). This is where a recorded CPU profile can help. Enable
|
||||
recording while profiling:
|
||||
|
||||
```javascript
|
||||
ng.profiler.timeChangeDetection({record: true});
|
||||
```
|
||||
|
||||
Then look for hot spots using
|
||||
[Chrome CPU profiler](https://developer.chrome.com/devtools/docs/cpu-profiling).
|
||||
|
||||
#### Reducing change detection cost
|
||||
|
||||
There are many reasons for slow change detection. To gain intuition about
|
||||
possible causes it would help to understand how change detection works. Such a
|
||||
discussion is outside the scope of this document (TODO link to docs), but here
|
||||
are some key concepts in brief.
|
||||
|
||||
By default Angular uses "dirty checking" mechanism for finding model changes.
|
||||
This mechanism involves evaluating every bound expression that's active on the
|
||||
UI. These usually include text interpolation via `{{expression}}` and property
|
||||
bindings via `[prop]="expression"`. If any of the evaluated expressions are
|
||||
costly to compute they could contribute to slow change detection. A good way to
|
||||
speed things up is to use plain class fields in your expressions and avoid any
|
||||
kinds of computation. Example:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
template: '<button [enabled]="isEnabled">{{title}}</button>'
|
||||
})
|
||||
class FancyButton {
|
||||
// GOOD: no computation, just return the value
|
||||
isEnabled: boolean;
|
||||
|
||||
// BAD: computes the final value upon request
|
||||
_title: String;
|
||||
get title(): String { return this._title.trim().toUpperCase(); }
|
||||
}
|
||||
```
|
||||
|
||||
Most cases like these could be solved by precomputing the value and storing the
|
||||
final value in a field.
|
||||
|
||||
Angular also supports a second type of change detection - the "push" model. In
|
||||
this model Angular does not poll your component for changes. Instead, the
|
||||
component "tells" Angular when it changes and only then does Angular perform
|
||||
the update. This model is suitable in situations when your data model uses
|
||||
observable or immutable objects (also a discussion for another time).
|
@ -1,168 +0,0 @@
|
||||
# Triage Process and Github Labels for Angular 2
|
||||
|
||||
This document describes how the Angular team uses labels and milestones to triage issues on github.
|
||||
|
||||
# Issues and PRs
|
||||
## Triaged vs Untriaged Issues
|
||||
|
||||
Every triaged issue must have four attributes assigned to it:
|
||||
|
||||
* `priority` -- P0 through P4. P0 issues are "drop everything and do this now". P4 are nice to have.
|
||||
* `component` -- Which area of Angular knowledge this relates to.
|
||||
* `effort` -- Rough assessment of how much work this issue is. E.g. `effort: easy` means
|
||||
"probably a few hours of work".
|
||||
* `type` -- Whether this issue is a bug, feature, or other kind of task.
|
||||
|
||||
Untriaged issues are any issues in the queue that don't yet have these four attributes.
|
||||
|
||||
You can view a report of untriaged issues here, in our
|
||||
[Angular Triage Dashboard](http://mhevery.github.io/github_issues/).
|
||||
|
||||
Issues should also have a clear action to complete that can be addressed or resolved within the
|
||||
scope of Angular 2. We'll close issues that don't meet these criteria.
|
||||
|
||||
### Assigning Issues to Milestones
|
||||
|
||||
Any issue that is being worked on must have:
|
||||
|
||||
* An `assignee`: The person doing the work.
|
||||
* A `Milestone`: When we expect to complete this work.
|
||||
|
||||
We aim to only have at most three milestones open at a time:
|
||||
|
||||
* Closing Milestone: A milestone with a very small number of issues, about to release.
|
||||
* Current Milestone: Work that we plan to complete within one week.
|
||||
* Next Milestone: Work that is > 1 week but current for the team.
|
||||
|
||||
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
|
||||
consists of all issues that have been triaged but do not have an assignee or milestone.
|
||||
|
||||
## Triaged vs Untriaged PRs
|
||||
|
||||
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
||||
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
||||
|
||||
Every triaged PR must have a `pr_action` label assigned to it and an assignee:
|
||||
|
||||
* `pr_action: review` -- work is complete and comment is needed from the assignee.
|
||||
* `pr_action: cleanup` -- more work is needed from the current assignee.
|
||||
* `pr_action: discuss` -- discussion is needed, to be led by the current assignee.
|
||||
* `pr_action: merge` -- the PR should be merged. Add this to a PR when you would like to
|
||||
trigger automatic merging following a successful build. This is described in [COMMITTER.md](COMMITTER.md).
|
||||
|
||||
In addition, PRs can have the following states:
|
||||
|
||||
* `pr_state: LGTM` -- PR may have outstanding changes but does not require further review.
|
||||
* `pr_state: WIP` -- PR is experimental or rapidly changing. Not ready for review or triage.
|
||||
* `pr_state: blocked` -- PR is blocked on an issue or other PR. Not ready for review or triage.
|
||||
|
||||
Note that an LGTM state does not mean a PR is ready to merge: for example, a reviewer might set the
|
||||
LGTM state but request a minor tweak that doesn't need further review, e.g., a rebase or small
|
||||
uncontroversial change.
|
||||
|
||||
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
|
||||
PR to land.
|
||||
|
||||
Victor (`vsavkin`) and Tobias (`tbosch`) are owners of the PR queue. Here is a list of [current
|
||||
untriaged PRs](https://github.com/angular/angular/pulls?utf8=%E2%9C%93&q=is%3Aopen+no%3Amilestone+is%3Apr+-label%3A%22pr_action%3A+cleanup%22+-label%3A%22pr_action%3A+merge%22+-label%3A%22pr_action%3A+review%22+-label%3A%22pr_action%3A+discuss%22+-label%3A%22pr_state%3A+blocked%22+-label%3A%22pr_state%3A+WIP%22+).
|
||||
|
||||
# Prioritization of Work
|
||||
|
||||
What should you be working on?
|
||||
|
||||
1. Any PRs that are assigned to you that don't have `pr_state: WIP` or `pr_state: blocked`
|
||||
1. Any issues that are assigned to you in the lowest-numbered Milestone
|
||||
1. Any issues that are assigned to you in any Milestone
|
||||
|
||||
If there are no issues assigned to you in any Milestone, pick an issue, self-assign it, and add
|
||||
it to the most appropriate Milestone based on effort.
|
||||
|
||||
Here are some suggestions for what to work on next:
|
||||
|
||||
* Filter for issues in a component that you are knowledgeable about, and pick something that has a
|
||||
high priority.
|
||||
* Filter for any small effort task that has the special `cust: GT` or `cust:Ionic` tags,
|
||||
and priority > P3.
|
||||
* Add a new task that's really important, add `component`, `priority`, `effort`, `type` and
|
||||
assign it to yourself and the most appropriate milestone.
|
||||
|
||||
# Labels Used in Triage
|
||||
|
||||
## Priority
|
||||
How urgent is this issue? We use priority to determine what should be worked on in each new
|
||||
milestone.
|
||||
|
||||
* `P0: critical` -- drop everything to work on this
|
||||
* `P1: urgent` -- resolve quickly in the current milestone. people are blocked
|
||||
* `P2: required` -- needed for development but not urgent yet. workaround exists, or e.g. new API
|
||||
* `P3: important` -- must complete before Angular 2 is ready for release
|
||||
* `P4: nice to have` -- a good idea, but maybe not until after release
|
||||
|
||||
|
||||
## Effort
|
||||
Rough, non-binding estimate of how much work this issue represents. Please change this assessment
|
||||
for anything you're working on to better reflect reality.
|
||||
|
||||
* `effort: easy` -- straightforward issue that can be resolved in a few hours, e.g. < 1 day of work.
|
||||
* `effort: medium` -- issue that will be a few days of work. Can be completed within a single
|
||||
milestone.
|
||||
* `effort: tough` -- issue that will likely take more than 1 milestone to complete.
|
||||
|
||||
<!-- We don't like these label names as
|
||||
they're not absolute (what is one developer-hour, really?) but decided it wasn't worth arguing
|
||||
over terms. -->
|
||||
|
||||
## Component
|
||||
Which area of Angular knowledge is this issue most closely related to? Helpful when deciding what
|
||||
to work on next.
|
||||
|
||||
* `comp: benchpress` -- benchmarks and performance testing → *tbosch*, *crossj*
|
||||
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks → *iminar*, *caitp*
|
||||
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart → *mprobst*, *alexeagle*
|
||||
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) →
|
||||
*mhevery*
|
||||
* `comp: core/animations` -- animations framework → *matsko*
|
||||
* `comp: core/change_detection` -- change detection → *vsavkin*
|
||||
* `comp: core/di` -- dependency injection → *vicb*, *rkirov*
|
||||
* `comp: core/directives` -- directives
|
||||
* `comp: core/forms` -- forms → *vsavkin*
|
||||
* `comp: core/pipes` -- pipes
|
||||
* `comp: core/view` -- runtime processing of the `View`s
|
||||
* `comp: core/view/compiler` -- static analysis of the templates which generate `ProtoView`s.
|
||||
* `comp: core/testbed` -- e2e tests and support for them
|
||||
* `comp: core/webworker` -- core web worker infrastructure
|
||||
* `comp: dart-transformer` -- Dart transforms → *kegluneq*, *jakemac*
|
||||
* `comp: data-access` -- → *jeffbcross*
|
||||
* `comp: docs` -- API docs and doc generation → *naomiblack*, *petebacondarwin*
|
||||
* `comp: material-components` -- Angular Material components built in Angular 2 → *jelbourn*
|
||||
* `comp: router` -- Component Router → *btford*, *igorminar*, *matsko*
|
||||
* `comp: wrenchjs`
|
||||
|
||||
## Type
|
||||
What kind of problem is this?
|
||||
|
||||
* `type RFC / discussion / question`
|
||||
* `type bug`
|
||||
* `type chore`
|
||||
* `type feature`
|
||||
* `type performance`
|
||||
* `type refactor`
|
||||
|
||||
## Special Labels
|
||||
|
||||
### action:design
|
||||
More active discussion is needed before the issue can be worked on further. Typically used for
|
||||
`type: feature` or `type: RFC/discussion/question`
|
||||
|
||||
[See all issues that need discussion](https://github.com/angular/angular/labels/action:%20Design)
|
||||
|
||||
### cla
|
||||
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
||||
`cla:yes` should be merged into master.
|
||||
|
||||
### cust
|
||||
This is an issue causing user pain for early adopter customers `cust: GT` or `cust: Ionic`.
|
||||
|
||||
### WORKS_AS_INTENDED
|
||||
|
||||
Only used on closed issues, to indicate to the reporter why we closed it.
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "angular2",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"polymer": "Polymer/polymer"
|
||||
"polymer": "dart-lang/polymer_js#0.8.0-preview"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,313 +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 and ChromeBeta should be target:'BS' or target:'SL', and required:true
|
||||
// Currently deactivated due to https://github.com/angular/angular/issues/7560
|
||||
'ChromeBeta': { unitTest: {target: null, required: true}, e2e: {target: null, required: false}},
|
||||
'FirefoxBeta': { unitTest: {target: null, required: false}, e2e: {target: null, required: false}},
|
||||
'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: 'BS', required: false}, 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}},
|
||||
'iOS9': { unitTest: {target: 'BS', 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: '52'
|
||||
},
|
||||
'SL_CHROMEBETA': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'beta'
|
||||
},
|
||||
'SL_CHROMEDEV': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: 'dev'
|
||||
},
|
||||
'SL_FIREFOX': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '46'
|
||||
},
|
||||
'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.0'
|
||||
},
|
||||
'SL_SAFARI8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8.0'
|
||||
},
|
||||
'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.3'
|
||||
},
|
||||
'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: '13.10586'
|
||||
},
|
||||
'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.1'
|
||||
},
|
||||
'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
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
121
build.sh
121
build.sh
@ -1,121 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||
|
||||
|
||||
rm -rf ./dist/all/
|
||||
mkdir -p ./dist/all/
|
||||
|
||||
TSCONFIG=./tools/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
$(npm bin)/tsc -p ${TSCONFIG}
|
||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||
|
||||
echo "====== Copying files needed for e2e tests ====="
|
||||
cp -r ./modules/playground ./dist/all/
|
||||
cp -r ./modules/playground/favicon.ico ./dist/
|
||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||
mkdir ./dist/all/playground/vendor
|
||||
cd ./dist/all/playground/vendor
|
||||
ln -s ../../../../node_modules/es6-shim/es6-shim.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||
ln -s ../../../../node_modules/rxjs .
|
||||
ln -s ../../../../node_modules/angular/angular.js .
|
||||
cd -
|
||||
|
||||
|
||||
TSCONFIG=./modules/tsconfig.json
|
||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||
# compile ts code
|
||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||
$TSC -p modules/tsconfig.json
|
||||
|
||||
rm -rf ./dist/packages-dist
|
||||
|
||||
for PACKAGE in \
|
||||
core \
|
||||
compiler \
|
||||
common \
|
||||
forms \
|
||||
platform-browser \
|
||||
platform-browser-dynamic \
|
||||
platform-server \
|
||||
http \
|
||||
router \
|
||||
router-deprecated \
|
||||
upgrade \
|
||||
compiler-cli
|
||||
do
|
||||
SRCDIR=./modules/@angular/${PACKAGE}
|
||||
DESTDIR=./dist/packages-dist/${PACKAGE}
|
||||
UMD_ES6_PATH=${DESTDIR}/esm/${PACKAGE}.umd.js
|
||||
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
|
||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es5.json
|
||||
else
|
||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-es5.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es5.json
|
||||
fi
|
||||
|
||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||
|
||||
|
||||
echo "====== TSC 1.8 d.ts compat for ${DESTDIR} ====="
|
||||
# safely strips 'readonly' specifier from d.ts files to make them compatible with tsc 1.8
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i '' -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
else
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -e 's/\(^ *(static |private )*\)*readonly */\1/g'
|
||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||
fi
|
||||
|
||||
if [[ ${PACKAGE} != compiler-cli ]]; then
|
||||
|
||||
if [[ ${PACKAGE} == "router-deprecated" ]]; then
|
||||
echo "====== (esm)COMPILING: \$(npm bin)/tsc -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$(npm bin)/tsc --emitDecoratorMetadata -p ${SRCDIR}/tsconfig-es2015.json
|
||||
else
|
||||
echo "====== (esm)COMPILING: $TSC -p ${SRCDIR}/tsconfig-es2015.json ====="
|
||||
$TSC -p ${SRCDIR}/tsconfig-es2015.json
|
||||
fi
|
||||
|
||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||
mkdir ${DESTDIR}/bundles
|
||||
|
||||
(
|
||||
cd ${SRCDIR}
|
||||
echo "..." # here just to have grep match something and not exit with 1
|
||||
../../../node_modules/.bin/rollup -c rollup.config.js
|
||||
) 2>&1 | grep -v "as external dependency"
|
||||
|
||||
$(npm bin)/tsc \
|
||||
--out ${UMD_ES5_PATH} \
|
||||
--target es5 \
|
||||
--lib "es6,dom" \
|
||||
--allowJs \
|
||||
${UMD_ES6_PATH}
|
||||
|
||||
rm ${UMD_ES6_PATH}
|
||||
|
||||
cat ./modules/@angular/license-banner.txt > ${UMD_ES5_PATH}.tmp
|
||||
cat ${UMD_ES5_PATH} >> ${UMD_ES5_PATH}.tmp
|
||||
mv ${UMD_ES5_PATH}.tmp ${UMD_ES5_PATH}
|
||||
|
||||
$(npm bin)/uglifyjs -c --screw-ie8 -o ${UMD_ES5_MIN_PATH} ${UMD_ES5_PATH}
|
||||
fi
|
||||
done
|
||||
|
||||
echo "====== COMPILING: \$(npm bin)/tsc -p benchpress/tsconfig.json ====="
|
||||
$(npm bin)/tsc -p ./modules/benchpress/tsconfig.json
|
11
circle.yml
11
circle.yml
@ -1,11 +0,0 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm
|
||||
|
||||
test:
|
||||
override:
|
||||
- gulp lint
|
366
docs/app/css/app.css
Normal file
366
docs/app/css/app.css
Normal file
@ -0,0 +1,366 @@
|
||||
|
||||
.hide { display: none !important; }
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3f51b5;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 20px;
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: $baseline-grid ($baseline-grid * 2);
|
||||
border-top: 1px solid #ddd;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
border-bottom: 2px solid #ddd;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.md-sidenav-inner {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.layout-content,
|
||||
.doc-content {
|
||||
max-width: 864px;
|
||||
margin: auto;
|
||||
}
|
||||
.layout-label {
|
||||
width: 120px;
|
||||
}
|
||||
.layout-content code.highlight {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
background: none;
|
||||
border-width: 0;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
color: #333;
|
||||
font-size: inherit;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
opacity: 1;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 0px 28px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
-webkit-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
-webkit-transition-property: max-height, background-color, opacity;
|
||||
-moz-transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
-moz-transition-property: max-height, background-color, opacity;
|
||||
transition: 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition-property: max-height, background-color, opacity;
|
||||
}
|
||||
.menu-item.ng-hide {
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
.menu-item:hover {
|
||||
color: #999;
|
||||
}
|
||||
.menu-item:focus {
|
||||
font-weight: bold;
|
||||
}
|
||||
.menu-item.menu-title {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
padding-left: 16px;
|
||||
text-align: left;
|
||||
text-transform: uppercase;
|
||||
transition: color 0.35s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
}
|
||||
.menu-item.menu-title:hover,
|
||||
.menu-item.menu-title.active {
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
.app-toolbar .md-toolbar-tools h3 {
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
border-radius: 4px;
|
||||
margin-top: 16px;
|
||||
-webkit-transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition: 0.02s padding cubic-bezier(0.35, 0, 0.25, 1);
|
||||
position: relative;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.demo-source-tabs {
|
||||
z-index: 1;
|
||||
-webkit-transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
transition: all 0.45s cubic-bezier(0.35, 0, 0.25, 1);
|
||||
max-height: 448px;
|
||||
min-height: 448px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
md-tabs.demo-source-tabs md-tab,
|
||||
md-tabs.demo-source-tabs .md-header {
|
||||
background-color: #444444 !important;
|
||||
}
|
||||
|
||||
|
||||
md-tabs.demo-source-tabs md-tab-label {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
md-tabs.demo-source-tabs .active md-tab-label {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.demo-source-tabs.ng-hide {
|
||||
max-height: 0px;
|
||||
min-height: 0px;
|
||||
}
|
||||
.demo-source-tabs {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
.demo-content {
|
||||
position: relative;
|
||||
overflow:hidden;
|
||||
min-height: 448px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -moz-box;
|
||||
display: -moz-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.small-demo .demo-source-tabs:not(.ng-hide) {
|
||||
min-height: 224px;
|
||||
max-height: 224px;
|
||||
}
|
||||
.small-demo .demo-content {
|
||||
min-height: 128px;
|
||||
}
|
||||
.demo-content > * {
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-moz-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.demo-content > div[layout-fill] {
|
||||
min-height: 448px;
|
||||
}
|
||||
.small-demo .demo-content > div[layout-fill] {
|
||||
min-height: 224px;
|
||||
}
|
||||
.small-demo .demo-toolbar,
|
||||
.small-demo .md-toolbar-tools {
|
||||
min-height: 48px;
|
||||
max-height: 48px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.show-source md-toolbar.demo-toolbar {
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.36);
|
||||
}
|
||||
.demo-toolbar .md-button {
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
md-toolbar.demo-toolbar,
|
||||
.demo-source-tabs md-tab,
|
||||
.demo-source-tabs .tabs-header {
|
||||
background: #E0E0E0 !important;
|
||||
color: #616161;
|
||||
}
|
||||
md-toolbar.demo-toolbar md-tab-label {
|
||||
color: #99E4EE
|
||||
}
|
||||
md-toolbar.demo-toolbar .md-button:hover,
|
||||
md-toolbar.demo-toolbar .md-button:focus,
|
||||
md-toolbar.demo-toolbar .md-button.active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
md-toolbar.demo-toolbar .md-button {
|
||||
-webkit-transition: all 0.3s linear;
|
||||
-moz-transition: all 0.3s linear;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
.demo-source-container {
|
||||
display: block;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #f6f6f6;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.demo-source-content {
|
||||
height: 400px;
|
||||
}
|
||||
.demo-source-content,
|
||||
.demo-source-content pre,
|
||||
.demo-source-content code {
|
||||
background: #f6f6f6;
|
||||
font-family: monospace;
|
||||
}
|
||||
.demo-source-content pre {
|
||||
max-width: 100%;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.show-source div[demo-include] {
|
||||
border-top: #ddd solid 2px;
|
||||
}
|
||||
|
||||
|
||||
.menu-separator-icon {
|
||||
margin: 0;
|
||||
}
|
||||
.menu-module-name {
|
||||
opacity: 0.6;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/************
|
||||
* DOCS
|
||||
************/
|
||||
.api-options-bar .md-button {
|
||||
margin: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
.api-options-bar .md-button:hover,
|
||||
.api-options-bar .md-button:focus {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.api-options-bar.with-icon md-icon {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: 2px;
|
||||
}
|
||||
.api-options-bar.with-icon .md-button span {
|
||||
margin-left: 22px;
|
||||
}
|
||||
|
||||
.api-params-item {
|
||||
min-height: 72px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.api-params-label {
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
margin-top: 14px;
|
||||
-webkit-align-self: flex-start;
|
||||
-moz-align-self: flex-start;
|
||||
-ms-flex-item-align: start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
.api-params-title {
|
||||
color: #888;
|
||||
}
|
||||
code.api-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
ul li {
|
||||
margin-top: 3px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
ul li:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.layout-title {
|
||||
color: #999999;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.api-params-content ul {
|
||||
padding-left: 4px;
|
||||
}
|
||||
ul.methods > li {
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
ul.methods .method-function-syntax {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
}
|
||||
ul.methods li h3 {
|
||||
/* border-bottom: 1px solid #eee; */
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
ul.methods > li {
|
||||
padding-left: 0;
|
||||
border-left: none;
|
||||
list-style: default;
|
||||
}
|
||||
ul.methods .method-function-syntax {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.version {
|
||||
padding-left: 10px;
|
||||
text-decoration: underline;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.demo-source-container pre,
|
||||
.demo-source-container code {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
md-content.demo-source-container > hljs > pre > code.highlight {
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
|
||||
.extraPad {
|
||||
padding-left:32px !important;
|
||||
padding-right:32px !important;
|
||||
}
|
142
docs/app/css/prettify-theme.css
Normal file
142
docs/app/css/prettify-theme.css
Normal file
@ -0,0 +1,142 @@
|
||||
/* GitHub Theme */
|
||||
.prettyprint {
|
||||
background: white;
|
||||
font-family: Menlo, 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
.str {
|
||||
color: #dd1144;
|
||||
}
|
||||
|
||||
.kwd {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.com {
|
||||
color: #999988;
|
||||
}
|
||||
|
||||
.typ {
|
||||
color: #445588;
|
||||
}
|
||||
|
||||
.lit {
|
||||
color: #445588;
|
||||
}
|
||||
|
||||
.pun {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.opn {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.clo {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: navy;
|
||||
}
|
||||
|
||||
.atn {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.atv {
|
||||
color: #dd1144;
|
||||
}
|
||||
|
||||
.dec {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.var {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.fun {
|
||||
color: #990000;
|
||||
}
|
||||
}
|
||||
@media print, projection {
|
||||
.str {
|
||||
color: #006600;
|
||||
}
|
||||
|
||||
.kwd {
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.com {
|
||||
color: #600;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.typ {
|
||||
color: #404;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lit {
|
||||
color: #004444;
|
||||
}
|
||||
|
||||
.pun, .opn, .clo {
|
||||
color: #444400;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.atn {
|
||||
color: #440044;
|
||||
}
|
||||
|
||||
.atv {
|
||||
color: #006600;
|
||||
}
|
||||
}
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* IE indents via margin-left */
|
||||
li.L0,
|
||||
li.L1,
|
||||
li.L2,
|
||||
li.L3,
|
||||
li.L4,
|
||||
li.L5,
|
||||
li.L6,
|
||||
li.L7,
|
||||
li.L8,
|
||||
li.L9 {
|
||||
/* */
|
||||
}
|
||||
|
||||
/* Alternate shading for lines */
|
||||
li.L1,
|
||||
li.L3,
|
||||
li.L5,
|
||||
li.L7,
|
||||
li.L9 {
|
||||
/* */
|
||||
}
|
55
docs/app/index.html
Normal file
55
docs/app/index.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular 2 Docs</title>
|
||||
<base href="/">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/lib/angular-material/angular-material.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/prettify-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/app.css">
|
||||
|
||||
<script src="/lib/hammerjs/hammer.js"></script>
|
||||
<script src="/lib/google-code-prettify/src/prettify.js"></script>
|
||||
<script src="/lib/google-code-prettify/src/lang-css.js"></script>
|
||||
<script src="/lib/angular/angular.js"></script>
|
||||
<script src="/lib/angular-animate/angular-animate.js"></script>
|
||||
<script src="/lib/angular-aria/angular-aria.js"></script>
|
||||
<script src="/lib/angular-material/angular-material.js"></script>
|
||||
<script src="/js/navigation-modules.js"></script>
|
||||
<script src="/js/navigation-guides.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
<script src="/js/code.js"></script>
|
||||
</head>
|
||||
<body ng-app="app" ng-controller="NavController as nav" layout="column">
|
||||
|
||||
<md-toolbar md-scroll-shrink>
|
||||
<h1 class="md-toolbar-tools">Angular V2</h1>
|
||||
</md-toolbar>
|
||||
|
||||
<section layout="row">
|
||||
|
||||
<md-content>
|
||||
<h2>Navigation</h2>
|
||||
<section ng-repeat="area in nav.areas">
|
||||
<h3>{{ area.name }}</h3>
|
||||
<md-list>
|
||||
<md-item ng-repeat="section in area.sections">
|
||||
<h3><a href="{{section.path}}">{{section.name}}</a></h3>
|
||||
<ul>
|
||||
<li ng-repeat="page in section.pages">
|
||||
<a href="{{page.path}}">{{ page.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</md-item>
|
||||
</md-list>
|
||||
</section>
|
||||
</md-content>
|
||||
|
||||
|
||||
<md-content class="md-padding">
|
||||
<ng-include src="nav.currentPage.partial"></ng-include>
|
||||
</md-content>
|
||||
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
47
docs/app/js/app.js
Normal file
47
docs/app/js/app.js
Normal file
@ -0,0 +1,47 @@
|
||||
angular.module('app', ['ngMaterial', 'navigation-modules', 'navigation-guides', 'code'])
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
})
|
||||
|
||||
.controller('NavController', ['$scope', '$location', 'MODULES', 'GUIDES',
|
||||
function($scope, $location, MODULES, GUIDES) {
|
||||
var that = this;
|
||||
|
||||
this.areas = [
|
||||
{ name: 'Guides', sections: [ { pages: GUIDES.pages } ] },
|
||||
{ name: 'Modules', sections: MODULES.sections }
|
||||
];
|
||||
|
||||
this.updateCurrentPage = function(path) {
|
||||
path = path.replace(/^\//, '');
|
||||
console.log('path', path);
|
||||
this.currentPage = null;
|
||||
|
||||
this.areas.forEach(function(area) {
|
||||
area.sections.forEach(function(section) {
|
||||
|
||||
// Short-circuit out if the page has been found
|
||||
if ( that.currentPage ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.path === path) {
|
||||
console.log('found!');
|
||||
that.currentPage = section;
|
||||
} else {
|
||||
section.pages.forEach(function(page) {
|
||||
if (page.path === path) {
|
||||
that.currentPage = page;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch(
|
||||
function getLocationPath() { return $location.path(); },
|
||||
function handleLocationPathChange(path) { that.updateCurrentPage(path); }
|
||||
);
|
||||
}]);
|
15
docs/app/js/code.js
Normal file
15
docs/app/js/code.js
Normal file
@ -0,0 +1,15 @@
|
||||
angular.module('code', [])
|
||||
|
||||
.directive('code', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
terminal: true,
|
||||
compile: function(element) {
|
||||
var linenums = element.hasClass('linenum');
|
||||
var match = /lang-(\S+)/.exec(element[0].className);
|
||||
var lang = match && match[1];
|
||||
var html = element.html();
|
||||
element.html(window.prettyPrintOne(html, lang, linenums));
|
||||
}
|
||||
};
|
||||
});
|
20
docs/bower.json
Normal file
20
docs/bower.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "angular-docs",
|
||||
"main": "index.js",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
"authors": [],
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular-material": "~0.6.0",
|
||||
"google-code-prettify": "~1.0.3"
|
||||
}
|
||||
}
|
147
docs/dgeni-package/index.js
Normal file
147
docs/dgeni-package/index.js
Normal file
@ -0,0 +1,147 @@
|
||||
require('../../tools/transpiler/index.js').init();
|
||||
|
||||
var Package = require('dgeni').Package;
|
||||
var jsdocPackage = require('dgeni-packages/jsdoc');
|
||||
var nunjucksPackage = require('dgeni-packages/nunjucks');
|
||||
var path = require('canonical-path');
|
||||
|
||||
var PARTIAL_PATH = 'partials';
|
||||
var MODULES_DOCS_PATH = PARTIAL_PATH + '/modules';
|
||||
var GUIDES_PATH = PARTIAL_PATH + '/guides';
|
||||
|
||||
// Define the dgeni package for generating the docs
|
||||
module.exports = new Package('angular', [jsdocPackage, nunjucksPackage])
|
||||
|
||||
// Register the services and file readers
|
||||
.factory(require('./services/modules'))
|
||||
.factory(require('./services/atParser'))
|
||||
.factory(require('./services/getJSDocComment'))
|
||||
.factory(require('./services/SourceFile'))
|
||||
.factory(require('./services/TraceurParser'))
|
||||
.factory(require('./services/traceurOptions'))
|
||||
.factory(require('./services/ParseTreeVisitor'))
|
||||
.factory(require('./services/AttachCommentTreeVisitor'))
|
||||
.factory(require('./services/ExportTreeVisitor'))
|
||||
|
||||
.factory(require('./readers/atScript'))
|
||||
.factory(require('./readers/ngdoc'))
|
||||
|
||||
.factory('EXPORT_DOC_TYPES', function() {
|
||||
return [
|
||||
'class',
|
||||
'function',
|
||||
'var',
|
||||
'const'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Register the processors
|
||||
.processor(require('./processors/generateDocsFromComments'))
|
||||
.processor(require('./processors/processModuleDocs'))
|
||||
.processor(require('./processors/processClassDocs'))
|
||||
.processor(require('./processors/generateNavigationDoc'))
|
||||
.processor(require('./processors/extractTitleFromGuides'))
|
||||
|
||||
// Configure the log service
|
||||
.config(function(log) {
|
||||
log.level = 'info';
|
||||
})
|
||||
|
||||
|
||||
// Configure file reading
|
||||
.config(function(readFilesProcessor, atScriptFileReader, ngdocFileReader) {
|
||||
readFilesProcessor.fileReaders = [atScriptFileReader, ngdocFileReader];
|
||||
readFilesProcessor.basePath = path.resolve(__dirname, '../..');
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{ include: 'modules/*/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/src/**/*.js', basePath: 'modules' },
|
||||
{ include: 'modules/*/docs/**/*.md', basePath: 'modules' },
|
||||
{ include: 'docs/content/**/*.md', basePath: 'docs/content' }
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// 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'
|
||||
];
|
||||
})
|
||||
|
||||
|
||||
// Configure ids and paths
|
||||
.config(function(computeIdsProcessor, computePathsProcessor, EXPORT_DOC_TYPES) {
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: EXPORT_DOC_TYPES,
|
||||
idTemplate: '${moduleDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['member'],
|
||||
idTemplate: '${classDoc.id}.${name}',
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['guide'],
|
||||
getId: function(doc) {
|
||||
return doc.fileInfo.relativePath
|
||||
// path should be relative to `modules` folder
|
||||
.replace(/.*\/?modules\//, '')
|
||||
// path should not include `/docs/`
|
||||
.replace(/\/docs\//, '/')
|
||||
// path should not have a suffix
|
||||
.replace(/\.\w*$/, '');
|
||||
},
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['guide'],
|
||||
pathTemplate: '${id}',
|
||||
outputPathTemplate: GUIDES_PATH + '/${id}.html'
|
||||
});
|
||||
});
|
10
docs/dgeni-package/mocks/mockPackage.js
Normal file
10
docs/dgeni-package/mocks/mockPackage.js
Normal file
@ -0,0 +1,10 @@
|
||||
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); });
|
||||
|
||||
};
|
24
docs/dgeni-package/processors/extractTitleFromGuides.js
Normal file
24
docs/dgeni-package/processors/extractTitleFromGuides.js
Normal file
@ -0,0 +1,24 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function extractTitleFromGuides() {
|
||||
|
||||
return {
|
||||
$runAfter: ['processing-docs'],
|
||||
$runBefore: ['docs-processed'],
|
||||
$process: function(docs) {
|
||||
_(docs).forEach(function(doc) {
|
||||
if (doc.docType === 'guide') {
|
||||
doc.name = doc.name || getNameFromHeading(doc.description);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
function getNameFromHeading(text) {
|
||||
var match = /^\s*#\s*(.*)/.exec(text);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
34
docs/dgeni-package/processors/generateDocsFromComments.js
Normal file
34
docs/dgeni-package/processors/generateDocsFromComments.js
Normal file
@ -0,0 +1,34 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function generateDocsFromComments(log) {
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags'],
|
||||
$process: function(docs) {
|
||||
var commentDocs = [];
|
||||
docs = _.filter(docs, function(doc) {
|
||||
if (doc.docType !== 'atScriptFile') {
|
||||
return true;
|
||||
} else {
|
||||
_.forEach(doc.fileInfo.comments, function(comment) {
|
||||
|
||||
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
|
||||
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
|
||||
|
||||
// Create a doc from this comment
|
||||
commentDocs.push({
|
||||
fileInfo: doc.fileInfo,
|
||||
startingLine: comment.range.start.line,
|
||||
endingLine: comment.range.end.line,
|
||||
content: commentBody,
|
||||
codeTree: comment.treeAfter,
|
||||
docType: 'atScriptDoc'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return docs.concat(commentDocs);
|
||||
}
|
||||
};
|
||||
};
|
66
docs/dgeni-package/processors/generateNavigationDoc.js
Normal file
66
docs/dgeni-package/processors/generateNavigationDoc.js
Normal file
@ -0,0 +1,66 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function generateNavigationDoc() {
|
||||
|
||||
return {
|
||||
$runAfter: ['docs-processed'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
var modulesDoc = {
|
||||
value: { sections: [] },
|
||||
moduleName: 'navigation-modules',
|
||||
serviceName: 'MODULES',
|
||||
template: 'data-module.template.js',
|
||||
outputPath: 'js/navigation-modules.js'
|
||||
};
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
var moduleNavItem = {
|
||||
path: doc.path,
|
||||
partial: doc.outputPath,
|
||||
name: doc.id,
|
||||
type: 'module',
|
||||
pages: []
|
||||
};
|
||||
|
||||
modulesDoc.value.sections.push(moduleNavItem);
|
||||
|
||||
_.forEach(doc.exports, function(exportDoc) {
|
||||
var exportNavItem = {
|
||||
path: exportDoc.path,
|
||||
partial: exportDoc.outputPath,
|
||||
name: exportDoc.name,
|
||||
type: exportDoc.docType
|
||||
};
|
||||
moduleNavItem.pages.push(exportNavItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
docs.push(modulesDoc);
|
||||
|
||||
|
||||
var guidesDoc = {
|
||||
value: { pages: [] },
|
||||
moduleName: 'navigation-guides',
|
||||
serviceName: 'GUIDES',
|
||||
template: 'data-module.template.js',
|
||||
outputPath: 'js/navigation-guides.js'
|
||||
};
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'guide' ) {
|
||||
var guideDoc = {
|
||||
path: doc.path,
|
||||
partial: doc.outputPath,
|
||||
name: doc.name,
|
||||
type: 'guide'
|
||||
};
|
||||
guidesDoc.value.pages.push(guideDoc);
|
||||
}
|
||||
});
|
||||
docs.push(guidesDoc);
|
||||
}
|
||||
};
|
||||
};
|
47
docs/dgeni-package/processors/processClassDocs.js
Normal file
47
docs/dgeni-package/processors/processClassDocs.js
Normal file
@ -0,0 +1,47 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processClassDocs(log, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['processModuleDocs'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
ignorePrivateMembers: false,
|
||||
$process: function(docs) {
|
||||
var memberDocs = [];
|
||||
var ignorePrivateMembers = this.ignorePrivateMembers;
|
||||
_.forEach(docs, function(classDoc) {
|
||||
if ( classDoc.docType === 'class' ) {
|
||||
|
||||
classDoc.members = [];
|
||||
|
||||
// Create a new doc for each member of the class
|
||||
_.forEach(classDoc.elements, function(memberDoc) {
|
||||
|
||||
if (ignorePrivateMembers && memberDoc.name.literalToken.value.charAt(0) === '_') return;
|
||||
|
||||
classDoc.members.push(memberDoc);
|
||||
memberDocs.push(memberDoc);
|
||||
|
||||
memberDoc.docType = 'member';
|
||||
memberDoc.classDoc = classDoc;
|
||||
memberDoc.name = memberDoc.name.literalToken.value;
|
||||
|
||||
|
||||
if (memberDoc.commentBefore ) {
|
||||
// If this export has a comment, remove it from the list of
|
||||
// comments collected in the module
|
||||
var index = classDoc.moduleDoc.comments.indexOf(memberDoc.commentBefore);
|
||||
if ( index !== -1 ) {
|
||||
classDoc.moduleDoc.comments.splice(index, 1);
|
||||
}
|
||||
|
||||
_.assign(memberDoc, getJSDocComment(memberDoc.commentBefore));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return docs.concat(memberDocs);
|
||||
}
|
||||
};
|
||||
};
|
46
docs/dgeni-package/processors/processModuleDocs.js
Normal file
46
docs/dgeni-package/processors/processModuleDocs.js
Normal file
@ -0,0 +1,46 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function processModuleDocs(log, ExportTreeVisitor, getJSDocComment) {
|
||||
|
||||
return {
|
||||
$runAfter: ['files-read'],
|
||||
$runBefore: ['parsing-tags', 'generateDocsFromComments'],
|
||||
$process: function(docs) {
|
||||
var exportDocs = [];
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'module' ) {
|
||||
|
||||
log.debug('processing', doc.moduleTree.moduleName);
|
||||
|
||||
doc.exports = [];
|
||||
|
||||
if ( doc.moduleTree.visit ) {
|
||||
var visitor = new ExportTreeVisitor();
|
||||
visitor.visit(doc.moduleTree);
|
||||
|
||||
_.forEach(visitor.exports, function(exportDoc) {
|
||||
|
||||
doc.exports.push(exportDoc);
|
||||
exportDocs.push(exportDoc);
|
||||
exportDoc.moduleDoc = doc;
|
||||
|
||||
if (exportDoc.comment) {
|
||||
// If this export has a comment, remove it from the list of
|
||||
// comments collected in the module
|
||||
var index = doc.comments.indexOf(exportDoc.comment);
|
||||
if ( index !== -1 ) {
|
||||
doc.comments.splice(index, 1);
|
||||
}
|
||||
|
||||
_.assign(exportDoc, getJSDocComment(exportDoc.comment));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return docs.concat(exportDocs);
|
||||
}
|
||||
};
|
||||
};
|
32
docs/dgeni-package/readers/atScript.js
Normal file
32
docs/dgeni-package/readers/atScript.js
Normal file
@ -0,0 +1,32 @@
|
||||
var path = require('canonical-path');
|
||||
|
||||
|
||||
/**
|
||||
* @dgService atScriptFileReader
|
||||
* @description
|
||||
* This file reader will create a simple doc for each
|
||||
* file including a code AST of the AtScript in the file.
|
||||
*/
|
||||
module.exports = function atScriptFileReader(log, atParser, modules) {
|
||||
var reader = {
|
||||
name: 'atScriptFileReader',
|
||||
defaultPattern: /\.js$/,
|
||||
getDocs: function(fileInfo) {
|
||||
|
||||
var moduleDoc = atParser.parseModule(fileInfo);
|
||||
moduleDoc.docType = 'module';
|
||||
moduleDoc.id = moduleDoc.moduleTree.moduleName;
|
||||
moduleDoc.aliases = [moduleDoc.id];
|
||||
|
||||
modules[moduleDoc.id] = moduleDoc;
|
||||
|
||||
// Readers return a collection of docs read from the file
|
||||
// but in this read there is only one document (module) to return
|
||||
return [moduleDoc];
|
||||
}
|
||||
};
|
||||
|
||||
return reader;
|
||||
|
||||
|
||||
};
|
55
docs/dgeni-package/readers/atScript.spec.js
Normal file
55
docs/dgeni-package/readers/atScript.spec.js
Normal file
@ -0,0 +1,55 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('atScript file reader', function() {
|
||||
|
||||
var dgeni, injector, reader;
|
||||
|
||||
var fileContent =
|
||||
'import {CONST} from "facade/lang";\n' +
|
||||
'\n' +
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/\n' +
|
||||
'export class Inject {\n' +
|
||||
'token;\n' +
|
||||
'@CONST()\n' +
|
||||
'constructor(token) {\n' +
|
||||
'this.token = token;\n' +
|
||||
'}\n' +
|
||||
'}';
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
reader = injector.get('atScriptFileReader');
|
||||
});
|
||||
|
||||
|
||||
it('should provide a default pattern', function() {
|
||||
expect(reader.defaultPattern).toEqual(/\.js$/);
|
||||
});
|
||||
|
||||
|
||||
it('should parse the file using the atParser and return a single doc', function() {
|
||||
|
||||
var atParser = injector.get('atParser');
|
||||
spyOn(atParser, 'parseModule').and.callThrough();
|
||||
|
||||
var docs = reader.getDocs({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(atParser.parseModule).toHaveBeenCalled();
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].docType).toEqual('module');
|
||||
});
|
||||
|
||||
});
|
32
docs/dgeni-package/readers/ngdoc.js
Normal file
32
docs/dgeni-package/readers/ngdoc.js
Normal file
@ -0,0 +1,32 @@
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @dgService ngdocFileReader
|
||||
* @description
|
||||
* This file reader will pull the contents from a text file (by default .ngdoc)
|
||||
*
|
||||
* The doc will initially have the form:
|
||||
* ```
|
||||
* {
|
||||
* content: 'the content of the file',
|
||||
* startingLine: 1
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
module.exports = function ngdocFileReader() {
|
||||
var reader = {
|
||||
name: 'ngdocFileReader',
|
||||
defaultPattern: /\.md$/,
|
||||
getDocs: function(fileInfo) {
|
||||
|
||||
// We return a single element array because ngdoc files only contain one document
|
||||
return [{
|
||||
docType: 'guide',
|
||||
content: fileInfo.content,
|
||||
startingLine: 1
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
return reader;
|
||||
};
|
45
docs/dgeni-package/readers/ngdoc.spec.js
Normal file
45
docs/dgeni-package/readers/ngdoc.spec.js
Normal file
@ -0,0 +1,45 @@
|
||||
var ngdocFileReaderFactory = require('./ngdoc');
|
||||
var path = require('canonical-path');
|
||||
|
||||
describe('ngdocFileReader', function() {
|
||||
|
||||
var fileReader;
|
||||
|
||||
var createFileInfo = function(file, content, basePath) {
|
||||
return {
|
||||
fileReader: fileReader.name,
|
||||
filePath: file,
|
||||
baseName: path.basename(file, path.extname(file)),
|
||||
extension: path.extname(file).replace(/^\./, ''),
|
||||
basePath: basePath,
|
||||
relativePath: path.relative(basePath, file),
|
||||
content: content
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
fileReader = ngdocFileReaderFactory();
|
||||
});
|
||||
|
||||
|
||||
describe('defaultPattern', function() {
|
||||
it('should match .md files', function() {
|
||||
expect(fileReader.defaultPattern.test('abc.md')).toBeTruthy();
|
||||
expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getDocs', function() {
|
||||
it('should return an object containing info about the file and its contents', function() {
|
||||
var fileInfo = createFileInfo('project/path/modules/someModule/foo/docs/subfolder/bar.ngdoc', 'A load of content', 'project/path');
|
||||
expect(fileReader.getDocs(fileInfo)).toEqual([{
|
||||
docType: 'guide',
|
||||
content: 'A load of content',
|
||||
startingLine: 1
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
47
docs/dgeni-package/services/AttachCommentTreeVisitor.js
Normal file
47
docs/dgeni-package/services/AttachCommentTreeVisitor.js
Normal file
@ -0,0 +1,47 @@
|
||||
module.exports = function AttachCommentTreeVisitor(ParseTreeVisitor, log) {
|
||||
|
||||
function AttachCommentTreeVisitorImpl() {
|
||||
ParseTreeVisitor.call(this);
|
||||
}
|
||||
|
||||
AttachCommentTreeVisitorImpl.prototype = {
|
||||
|
||||
__proto__: ParseTreeVisitor.prototype,
|
||||
|
||||
|
||||
visit: function(tree, comments) {
|
||||
this.comments = comments;
|
||||
this.index = 0;
|
||||
this.currentComment = this.comments[this.index];
|
||||
|
||||
if (this.currentComment) log.silly('comment: ' +
|
||||
this.currentComment.range.start.line + ' - ' +
|
||||
this.currentComment.range.end.line + ' : ' +
|
||||
this.currentComment.range.toString());
|
||||
|
||||
ParseTreeVisitor.prototype.visit.call(this, tree);
|
||||
},
|
||||
|
||||
// Really we ought to subclass ParseTreeVisitor but this is fiddly in ES5 so
|
||||
// it is easier to simply override the prototype's method on the instance
|
||||
visitAny: function(tree) {
|
||||
if (tree && tree.location && tree.location.start && this.currentComment &&
|
||||
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||
log.silly('tree: ' + tree.constructor.name + ' - ' + tree.location.start.line);
|
||||
while (this.currentComment &&
|
||||
this.currentComment.range.end.offset < tree.location.start.offset) {
|
||||
log.silly('comment: ' + this.currentComment.range.start.line + ' - ' +
|
||||
this.currentComment.range.end.line + ' : ' +
|
||||
this.currentComment.range.toString());
|
||||
tree.commentBefore = this.currentComment;
|
||||
this.currentComment.treeAfter = tree;
|
||||
this.index++;
|
||||
this.currentComment = this.comments[this.index];
|
||||
}
|
||||
}
|
||||
return ParseTreeVisitor.prototype.visitAny.call(this, tree);
|
||||
}
|
||||
};
|
||||
|
||||
return AttachCommentTreeVisitorImpl;
|
||||
};
|
103
docs/dgeni-package/services/ExportTreeVisitor.js
Normal file
103
docs/dgeni-package/services/ExportTreeVisitor.js
Normal file
@ -0,0 +1,103 @@
|
||||
module.exports = function ExportTreeVisitor(ParseTreeVisitor, log) {
|
||||
|
||||
function ExportTreeVisitorImpl() {
|
||||
ParseTreeVisitor.call(this);
|
||||
}
|
||||
ExportTreeVisitorImpl.prototype = {
|
||||
|
||||
__proto__: ParseTreeVisitor.prototype,
|
||||
|
||||
visitExportDeclaration: function(tree) {
|
||||
// We are entering an export declaration - create an object to track it
|
||||
this.currentExport = {
|
||||
comment: tree.commentBefore,
|
||||
location: tree.location
|
||||
};
|
||||
log.silly('enter', tree.type, tree.commentBefore ? 'has comment' : '');
|
||||
ParseTreeVisitor.prototype.visitExportDeclaration.call(this, tree);
|
||||
log.silly('exit', this.currentExport);
|
||||
|
||||
if(this.currentExport) {
|
||||
// We are exiting the export declaration - store the export object
|
||||
this.exports.push(this.currentExport);
|
||||
}
|
||||
this.currentExport = null;
|
||||
},
|
||||
|
||||
visitVariableDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.docType = 'var';
|
||||
this.currentExport.name = tree.lvalue.identifierToken.value;
|
||||
this.currentExport.variableDeclaration = tree;
|
||||
}
|
||||
},
|
||||
|
||||
visitFunctionDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = tree.name.identifierToken.value;
|
||||
this.currentExport.functionKind = tree.functionKind;
|
||||
this.currentExport.parameters = tree.parameterList.parameters;
|
||||
this.currentExport.typeAnnotation = tree.typeAnnotation;
|
||||
this.currentExport.annotations = tree.annotations;
|
||||
this.currentExport.docType = 'function';
|
||||
|
||||
log.silly(tree.type, tree.commentBefore ? 'has comment' : '');
|
||||
}
|
||||
},
|
||||
visitClassDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = tree.name.identifierToken.value;
|
||||
this.currentExport.superClass = tree.superClass;
|
||||
this.currentExport.annotations = tree.annotations;
|
||||
this.currentExport.elements = tree.elements;
|
||||
this.currentExport.docType = 'class';
|
||||
}
|
||||
},
|
||||
visitAsyncFunctionDeclaration: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
}
|
||||
},
|
||||
|
||||
visitExportDefault: function(tree) {
|
||||
if ( this.currentExport ) {
|
||||
this.updateExport(tree);
|
||||
this.currentExport.name = 'DEFAULT';
|
||||
this.currentExport.defaultExport = tree;
|
||||
// Default exports are either classes, functions or expressions
|
||||
// So we let the super class continue down...
|
||||
ParseTreeVisitor.prototype.visitExportDefault.call(this, tree);
|
||||
}
|
||||
},
|
||||
|
||||
visitNamedExport: function(tree) {
|
||||
this.currentExport = null;
|
||||
// if ( this.currentExport ) {
|
||||
// this.updateExport(tree);
|
||||
|
||||
// this.currentExport.namedExport = tree;
|
||||
// this.currentExport.name = 'NAMED_EXPORT';
|
||||
// // TODO: work out this bit!!
|
||||
// // We need to cope with any export specifiers in the named export
|
||||
// }
|
||||
},
|
||||
|
||||
// TODO - if the export is an expression, find the thing that is being
|
||||
// exported and use it and its comments for docs
|
||||
|
||||
updateExport: function(tree) {
|
||||
this.currentExport.comment = this.currentExport.comment || tree.commentBefore;
|
||||
this.currentExport.docType = tree.type;
|
||||
},
|
||||
|
||||
visit: function(tree) {
|
||||
this.exports = [];
|
||||
ParseTreeVisitor.prototype.visit.call(this, tree);
|
||||
}
|
||||
};
|
||||
|
||||
return ExportTreeVisitorImpl;
|
||||
};
|
6
docs/dgeni-package/services/ParseTreeVisitor.js
Normal file
6
docs/dgeni-package/services/ParseTreeVisitor.js
Normal file
@ -0,0 +1,6 @@
|
||||
var traceur = require('traceur/src/node/traceur.js');
|
||||
|
||||
module.exports = function ParseTreeVisitor() {
|
||||
console.log(System.map.traceur);
|
||||
return System.get(System.map.traceur + '/src/syntax/ParseTreeVisitor.js').ParseTreeVisitor;
|
||||
};
|
5
docs/dgeni-package/services/SourceFile.js
Normal file
5
docs/dgeni-package/services/SourceFile.js
Normal file
@ -0,0 +1,5 @@
|
||||
var traceur = require('traceur/src/node/traceur.js');
|
||||
|
||||
module.exports = function SourceFile() {
|
||||
return System.get(System.map.traceur + '/src/syntax/SourceFile.js').SourceFile;
|
||||
};
|
3
docs/dgeni-package/services/TraceurParser.js
Normal file
3
docs/dgeni-package/services/TraceurParser.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function TraceurParser() {
|
||||
return System.get('transpiler/src/parser').Parser;
|
||||
};
|
74
docs/dgeni-package/services/atParser.js
Normal file
74
docs/dgeni-package/services/atParser.js
Normal file
@ -0,0 +1,74 @@
|
||||
var file2modulename = require('../../../tools/build/file2modulename');
|
||||
/**
|
||||
* Wrapper around traceur that can parse the contents of a file
|
||||
*/
|
||||
module.exports = function atParser(AttachCommentTreeVisitor, SourceFile, TraceurParser, traceurOptions, log) {
|
||||
|
||||
var service = {
|
||||
/**
|
||||
* The options to pass to traceur
|
||||
*/
|
||||
traceurOptions: {
|
||||
annotations: true, // parse annotations
|
||||
types: true, // parse types
|
||||
memberVariables: true, // parse class fields
|
||||
commentCallback: true // handle comments
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a module AST from the contents of a file.
|
||||
* @param {Object} fileInfo information about the file to parse
|
||||
* @return { { moduleTree: Object, comments: Array } } An object containing the parsed module
|
||||
* AST and an array of all the comments found in the file
|
||||
*/
|
||||
parseModule: parseModule
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
|
||||
// Parse the contents of the file using traceur
|
||||
function parseModule(fileInfo) {
|
||||
|
||||
var moduleName = file2modulename(fileInfo.relativePath);
|
||||
var sourceFile = new SourceFile(moduleName, fileInfo.content);
|
||||
var comments = [];
|
||||
var moduleTree;
|
||||
var parser = new TraceurParser(sourceFile);
|
||||
|
||||
// Configure the parser
|
||||
parser.handleComment = function(range) {
|
||||
comments.push({ range: range });
|
||||
};
|
||||
traceurOptions.setFromObject(service.traceurOptions);
|
||||
|
||||
try {
|
||||
// Parse the file as a module, attaching the comments
|
||||
moduleTree = parser.parseModule();
|
||||
attachComments(moduleTree, comments);
|
||||
} catch(ex) {
|
||||
// HACK: sometime traceur crashes for various reasons including
|
||||
// Not Yet Implemented (NYI)!
|
||||
log.error(ex.stack);
|
||||
moduleTree = {};
|
||||
}
|
||||
log.debug(moduleName);
|
||||
moduleTree.moduleName = moduleName;
|
||||
|
||||
// We return the module AST but also a collection of all the comments
|
||||
// since it can be helpful to iterate through them without having to
|
||||
// traverse the AST again
|
||||
return {
|
||||
moduleTree: moduleTree,
|
||||
comments: comments
|
||||
};
|
||||
}
|
||||
|
||||
// attach the comments to their nearest code tree
|
||||
function attachComments(tree, comments) {
|
||||
|
||||
var visitor = new AttachCommentTreeVisitor();
|
||||
// Visit every node of the tree using our custom method
|
||||
visitor.visit(tree, comments);
|
||||
}
|
||||
};
|
80
docs/dgeni-package/services/atParser.spec.js
Normal file
80
docs/dgeni-package/services/atParser.spec.js
Normal file
@ -0,0 +1,80 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('atParser service', function() {
|
||||
|
||||
var dgeni, injector, parser;
|
||||
|
||||
var fileContent =
|
||||
'import {CONST} from "facade/lang";\n' +
|
||||
'\n' +
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/\n' +
|
||||
'export class Inject {\n' +
|
||||
'token;\n' +
|
||||
'@CONST()\n' +
|
||||
'constructor({a,b}:{a:string, b:string}) {\n' +
|
||||
'this.token = a;\n' +
|
||||
'}\n' +
|
||||
'}';
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
parser = injector.get('atParser');
|
||||
});
|
||||
|
||||
it('should extract the comments from the file', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.comments[0].range.toString()).toEqual(
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/'
|
||||
);
|
||||
});
|
||||
|
||||
it('should extract a module AST from the file', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.moduleTree.moduleName).toEqual('di/src/annotations');
|
||||
expect(result.moduleTree.scriptItemList[0].type).toEqual('IMPORT_DECLARATION');
|
||||
|
||||
expect(result.moduleTree.scriptItemList[1].type).toEqual('EXPORT_DECLARATION');
|
||||
});
|
||||
|
||||
it('should attach comments to their following AST', function() {
|
||||
var result = parser.parseModule({
|
||||
content: fileContent,
|
||||
relativePath: 'di/src/annotations.js'
|
||||
});
|
||||
|
||||
expect(result.moduleTree.scriptItemList[1].commentBefore.range.toString()).toEqual(
|
||||
'/**\n' +
|
||||
'* A parameter annotation that creates a synchronous eager dependency.\n' +
|
||||
'*\n' +
|
||||
'* class AComponent {\n' +
|
||||
'* constructor(@Inject("aServiceToken") aService) {}\n' +
|
||||
'* }\n' +
|
||||
'*\n' +
|
||||
'*/'
|
||||
);
|
||||
});
|
||||
});
|
28
docs/dgeni-package/services/getJSDocComment.js
Normal file
28
docs/dgeni-package/services/getJSDocComment.js
Normal file
@ -0,0 +1,28 @@
|
||||
var LEADING_STAR = /^[^\S\r\n]*\*[^\S\n\r]?/gm;
|
||||
|
||||
/**
|
||||
* Extact comment info from a comment object
|
||||
* @param {Object} comment object to process
|
||||
* @return { {startingLine, endingLine, content, codeTree}= } a comment info object
|
||||
* or undefined if the comment is not a jsdoc style comment
|
||||
*/
|
||||
module.exports = function getJSDocComment() {
|
||||
return function(comment) {
|
||||
|
||||
var commentInfo;
|
||||
|
||||
// we need to check for `/**` at the start of the comment to find all the jsdoc style comments
|
||||
comment.range.toString().replace(/^\/\*\*([\w\W]*)\*\/$/g, function(match, commentBody) {
|
||||
commentBody = commentBody.replace(LEADING_STAR, '').trim();
|
||||
|
||||
commentInfo = {
|
||||
startingLine: comment.range.start.line,
|
||||
endingLine: comment.range.end.line,
|
||||
content: commentBody,
|
||||
codeTree: comment.treeAfter
|
||||
};
|
||||
});
|
||||
|
||||
return commentInfo;
|
||||
};
|
||||
};
|
67
docs/dgeni-package/services/getJSDocComment.spec.js
Normal file
67
docs/dgeni-package/services/getJSDocComment.spec.js
Normal file
@ -0,0 +1,67 @@
|
||||
var mockPackage = require('../mocks/mockPackage');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('getJSDocComment service', function() {
|
||||
|
||||
var dgeni, injector, getJSDocComment;
|
||||
|
||||
function createComment(commentString, start, end, codeTree) {
|
||||
return {
|
||||
range: {
|
||||
toString: function() { return commentString; },
|
||||
start: { line: start },
|
||||
end: { line: end },
|
||||
},
|
||||
treeAfter: codeTree
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
dgeni = new Dgeni([mockPackage()]);
|
||||
injector = dgeni.configureInjector();
|
||||
getJSDocComment = injector.get('getJSDocComment');
|
||||
});
|
||||
|
||||
it('should only return an object if the comment starts with /** and ends with */', function() {
|
||||
var result = getJSDocComment(createComment('/** this is a jsdoc comment */'));
|
||||
expect(result).toBeDefined();
|
||||
|
||||
result = getJSDocComment(createComment('/* this is a normal comment */'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('this is not a valid comment */'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('nor is this'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('/* or even this'));
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
result = getJSDocComment(createComment('/** and this'));
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should return a result that contains info about the comment', function() {
|
||||
var codeTree = {};
|
||||
var result = getJSDocComment(createComment('/** this is a comment */', 10, 20, codeTree));
|
||||
expect(result.startingLine).toEqual(10);
|
||||
expect(result.endingLine).toEqual(20);
|
||||
expect(result.codeTree).toBe(codeTree);
|
||||
});
|
||||
|
||||
it('should strip off leading stars from each line', function() {
|
||||
var result = getJSDocComment(createComment(
|
||||
'/** this is a jsdoc comment */\n' +
|
||||
' *\n' +
|
||||
' * some content\n' +
|
||||
' */'
|
||||
));
|
||||
expect(result.content).toEqual(
|
||||
'this is a jsdoc comment */\n' +
|
||||
'\n' +
|
||||
'some content'
|
||||
);
|
||||
});
|
||||
});
|
3
docs/dgeni-package/services/modules.js
Normal file
3
docs/dgeni-package/services/modules.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function modules() {
|
||||
return {};
|
||||
};
|
3
docs/dgeni-package/services/traceurOptions.js
Normal file
3
docs/dgeni-package/services/traceurOptions.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function traceurOptions() {
|
||||
return System.get(System.map.traceur + '/src/Options.js').options;
|
||||
};
|
14
docs/dgeni-package/templates/class.template.html
Normal file
14
docs/dgeni-package/templates/class.template.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.name $} <span class="type">class</span></h1>
|
||||
<p class="module">exported from <a href="/{$ doc.moduleDoc.path $}">{$ doc.moduleDoc.id $}</a></p>
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
<h2>Members</h2>
|
||||
{% for member in doc.members %}
|
||||
<h3>{$ member.name $}</h3>
|
||||
<p>{$ member.description | marked $}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
9
docs/dgeni-package/templates/common.template.html
Normal file
9
docs/dgeni-package/templates/common.template.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{$ doc.id $}</h1>
|
||||
<h2>({$ doc.docType $})</h2>
|
||||
<div>
|
||||
{$ doc.description | marked $}
|
||||
</div>
|
||||
{% endblock %}
|
3
docs/dgeni-package/templates/data-module.template.js
Normal file
3
docs/dgeni-package/templates/data-module.template.js
Normal file
@ -0,0 +1,3 @@
|
||||
angular.module('{$ doc.moduleName $}', [])
|
||||
|
||||
.value('{$ doc.serviceName $}', {$ doc.value | json $});
|
5
docs/dgeni-package/templates/guide.template.html
Normal file
5
docs/dgeni-package/templates/guide.template.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
{$ doc.description | marked $}
|
||||
{% endblock %}
|
1
docs/dgeni-package/templates/layout/base.template.html
Normal file
1
docs/dgeni-package/templates/layout/base.template.html
Normal file
@ -0,0 +1 @@
|
||||
{% block body %}{% endblock %}
|
16
docs/dgeni-package/templates/module.template.html
Normal file
16
docs/dgeni-package/templates/module.template.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'layout/base.template.html' %}
|
||||
|
||||
{% block body %}
|
||||
<h1 class="id">{$ doc.id $} <span class="type">module</span></h1>
|
||||
|
||||
<p>{$ doc.description | marked $}</p>
|
||||
|
||||
{% if doc.exports.length %}
|
||||
<h2>Exports</h2>
|
||||
<ul>
|
||||
{%- for exportDoc in doc.exports %}
|
||||
<li><a href="/{$ exportDoc.path $}">{$ exportDoc.name $} {$ exportDoc.docType $}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
21
docs/public-docs-package/index.js
Normal file
21
docs/public-docs-package/index.js
Normal file
@ -0,0 +1,21 @@
|
||||
var Package = require('dgeni').Package;
|
||||
var basePackage = require('../dgeni-package');
|
||||
|
||||
|
||||
module.exports = new Package('angular-public', [basePackage])
|
||||
|
||||
.processor(require('./processors/filterPublicDocs'))
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push({ name: 'publicModule' });
|
||||
})
|
||||
|
||||
.config(function(processClassDocs, filterPublicDocs, EXPORT_DOC_TYPES) {
|
||||
processClassDocs.ignorePrivateMembers = true;
|
||||
filterPublicDocs.docTypes = EXPORT_DOC_TYPES;
|
||||
})
|
||||
|
||||
// Configure file writing
|
||||
.config(function(writeFilesProcessor) {
|
||||
writeFilesProcessor.outputFolder = 'dist/public_docs';
|
||||
});
|
51
docs/public-docs-package/processors/filterPublicDocs.js
Normal file
51
docs/public-docs-package/processors/filterPublicDocs.js
Normal file
@ -0,0 +1,51 @@
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = function filterPublicDocs(modules) {
|
||||
return {
|
||||
$runAfter: ['tags-parsed'],
|
||||
$runBefore: ['computing-ids'],
|
||||
docTypes: [],
|
||||
$validate: {
|
||||
docTypes: { presence: true }
|
||||
},
|
||||
$process: function(docs) {
|
||||
|
||||
docTypes = this.docTypes;
|
||||
|
||||
|
||||
docs = _.filter(docs, function(doc) {
|
||||
|
||||
if (docTypes.indexOf(doc.docType) === -1) return true;
|
||||
if (!doc.publicModule) return false;
|
||||
|
||||
updateModule(doc);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
docs = _.filter(docs, function(doc) {
|
||||
return doc.docType !== 'module' || doc.isPublic;
|
||||
});
|
||||
return docs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function updateModule(classDoc) {
|
||||
|
||||
var originalModule = classDoc.moduleDoc;
|
||||
var publicModule = modules[classDoc.publicModule];
|
||||
|
||||
if (!publicModule) {
|
||||
throw new Error('Missing module definition: "' + classDoc.publicModule + '"\n' +
|
||||
'Referenced in class: "' + classDoc.moduleDoc.id + '/' + classDoc.name + '"');
|
||||
}
|
||||
|
||||
publicModule.isPublic = true;
|
||||
|
||||
_.remove(classDoc.moduleDoc.exports, function(doc) { return doc === classDoc; });
|
||||
classDoc.moduleDoc = publicModule;
|
||||
publicModule.exports.push(classDoc);
|
||||
|
||||
}
|
||||
};
|
836
gulpfile.js
836
gulpfile.js
@ -1,168 +1,742 @@
|
||||
'use strict';
|
||||
var gulp = require('gulp');
|
||||
var gulpPlugins = require('gulp-load-plugins')();
|
||||
var runSequence = require('run-sequence');
|
||||
var madge = require('madge');
|
||||
var merge = require('merge');
|
||||
var path = require('path');
|
||||
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||
require('./tools/check-environment')(
|
||||
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
|
||||
var gulpTraceur = require('./tools/transpiler/gulp-traceur');
|
||||
|
||||
var clean = require('./tools/build/clean');
|
||||
var transpile = require('./tools/build/transpile');
|
||||
var html = require('./tools/build/html');
|
||||
var pubget = require('./tools/build/pubget');
|
||||
var linknodemodules = require('./tools/build/linknodemodules');
|
||||
var pubbuild = require('./tools/build/pubbuild');
|
||||
var dartanalyzer = require('./tools/build/dartanalyzer');
|
||||
var jsserve = require('./tools/build/jsserve');
|
||||
var pubserve = require('./tools/build/pubserve');
|
||||
var rundartpackage = require('./tools/build/rundartpackage');
|
||||
var copy = require('./tools/build/copy');
|
||||
var file2moduleName = require('./tools/build/file2modulename');
|
||||
var karma = require('karma').server;
|
||||
var minimist = require('minimist');
|
||||
var es5build = require('./tools/build/es5build');
|
||||
var runServerDartTests = require('./tools/build/run_server_dart_tests');
|
||||
var transformCJSTests = require('./tools/build/transformCJSTests');
|
||||
var ts2dart = require('gulp-ts2dart');
|
||||
var util = require('./tools/build/util');
|
||||
|
||||
const gulp = require('gulp');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
var DART_SDK = require('./tools/build/dartdetect')(gulp);
|
||||
// -----------------------
|
||||
// configuration
|
||||
|
||||
const srcsToFmt =
|
||||
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
|
||||
'modules/benchpress/**/*.ts', 'modules/playground/**/*.ts'];
|
||||
var _COMPILER_CONFIG_JS_DEFAULT = {
|
||||
sourceMaps: true,
|
||||
annotations: true, // parse annotations
|
||||
types: true, // parse types
|
||||
script: false, // parse as a module
|
||||
memberVariables: true, // parse class fields
|
||||
modules: 'instantiate'
|
||||
};
|
||||
|
||||
gulp.task('format:enforce', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
return gulp.src(srcsToFmt).pipe(
|
||||
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
||||
});
|
||||
|
||||
gulp.task('format', () => {
|
||||
const format = require('gulp-clang-format');
|
||||
const clangFormat = require('clang-format');
|
||||
return gulp.src(srcsToFmt, { base: '.' }).pipe(
|
||||
format.format('file', clangFormat)).pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
const entrypoints = [
|
||||
'dist/packages-dist/core/index.d.ts',
|
||||
'dist/packages-dist/core/testing.d.ts',
|
||||
'dist/packages-dist/common/index.d.ts',
|
||||
'dist/packages-dist/common/testing.d.ts',
|
||||
// The API surface of the compiler is currently unstable - all of the important APIs are exposed
|
||||
// via @angular/core, @angular/platform-browser or @angular/platform-browser-dynamic instead.
|
||||
//'dist/packages-dist/compiler/index.d.ts',
|
||||
//'dist/packages-dist/compiler/testing.d.ts',
|
||||
'dist/packages-dist/upgrade/index.d.ts',
|
||||
'dist/packages-dist/platform-browser/index.d.ts',
|
||||
'dist/packages-dist/platform-browser/testing.d.ts',
|
||||
'dist/packages-dist/platform-browser-dynamic/index.d.ts',
|
||||
'dist/packages-dist/platform-browser-dynamic/testing.d.ts',
|
||||
'dist/packages-dist/platform-server/index.d.ts',
|
||||
'dist/packages-dist/platform-server/testing.d.ts',
|
||||
'dist/packages-dist/http/index.d.ts',
|
||||
'dist/packages-dist/http/testing.d.ts',
|
||||
'dist/packages-dist/forms/index.d.ts',
|
||||
'dist/packages-dist/router/index.d.ts'
|
||||
var _HTML_DEFAULT_SCRIPTS_JS = [
|
||||
{src: gulpTraceur.RUNTIME_PATH, mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
||||
mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/zone.js/zone.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/zone.js/long-stack-trace-zone.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/systemjs/dist/system.src.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'node_modules/systemjs/lib/extension-register.js', mimeType: 'text/javascript', copy: true},
|
||||
{src: 'tools/build/snippets/runtime_paths.js', mimeType: 'text/javascript', copy: true},
|
||||
{
|
||||
inline: 'System.import(\'$MODULENAME$\').then(function(m) { m.main(); }, console.error.bind(console))',
|
||||
mimeType: 'text/javascript'
|
||||
}
|
||||
];
|
||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||
const publicApiArgs = [
|
||||
'--rootDir', 'dist/packages-dist',
|
||||
'--stripExportPattern', '^__',
|
||||
'--allowModuleIdentifiers', 'jasmine',
|
||||
'--allowModuleIdentifiers', 'protractor',
|
||||
'--allowModuleIdentifiers', 'angular',
|
||||
'--onStabilityMissing', 'error'
|
||||
].concat(entrypoints);
|
||||
|
||||
gulp.task('build.sh', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
var _HTML_DEFAULT_SCRIPTS_DART = [
|
||||
{src: '$MODULENAME_WITHOUT_PATH$.dart', mimeType: 'application/dart'},
|
||||
{src: 'packages/browser/dart.js', mimeType: 'text/javascript'}
|
||||
];
|
||||
|
||||
childProcess.exec(path.join(__dirname, 'build.sh'), error => done(error));
|
||||
});
|
||||
var BASE_PACKAGE_JSON = require('./package.json');
|
||||
var COMMON_PACKAGE_JSON = {
|
||||
version: BASE_PACKAGE_JSON.version,
|
||||
homepage: BASE_PACKAGE_JSON.homepage,
|
||||
bugs: BASE_PACKAGE_JSON.bugs,
|
||||
license: BASE_PACKAGE_JSON.license,
|
||||
contributors: BASE_PACKAGE_JSON.contributors,
|
||||
dependencies: BASE_PACKAGE_JSON.dependencies,
|
||||
devDependencies: {
|
||||
"yargs": BASE_PACKAGE_JSON.devDependencies['yargs'],
|
||||
"gulp-sourcemaps": BASE_PACKAGE_JSON.devDependencies['gulp-sourcemaps'],
|
||||
"gulp-traceur": BASE_PACKAGE_JSON.devDependencies['gulp-traceur'],
|
||||
"gulp": BASE_PACKAGE_JSON.devDependencies['gulp'],
|
||||
"gulp-rename": BASE_PACKAGE_JSON.devDependencies['gulp-rename'],
|
||||
"through2": BASE_PACKAGE_JSON.devDependencies['through2']
|
||||
}
|
||||
};
|
||||
|
||||
// Note that these two commands work on built d.ts files instead of the source
|
||||
gulp.task('public-api:enforce', (done) => {
|
||||
const childProcess = require('child_process');
|
||||
var SRC_FOLDER_INSERTION = {
|
||||
js: {
|
||||
'**': ''
|
||||
},
|
||||
dart: {
|
||||
'**': 'lib',
|
||||
'*/test/**': '',
|
||||
'benchmarks/**': 'web',
|
||||
'benchmarks/test/**': '',
|
||||
'benchmarks_external/**': 'web',
|
||||
'benchmarks_external/test/**': '',
|
||||
'example*/**': 'web',
|
||||
'example*/test/**': ''
|
||||
}
|
||||
};
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => {
|
||||
if (errorCode !== 0) {
|
||||
done(new Error(
|
||||
'Public API differs from golden file. Please run `gulp public-api:update`.'));
|
||||
} else {
|
||||
done();
|
||||
var CONFIG = {
|
||||
dest: {
|
||||
js: {
|
||||
all: 'dist/js',
|
||||
dev: {
|
||||
es6: 'dist/js/dev/es6',
|
||||
es5: 'dist/js/dev/es5'
|
||||
},
|
||||
prod: {
|
||||
es6: 'dist/js/prod/es6',
|
||||
es5: 'dist/js/prod/es5'
|
||||
},
|
||||
cjs: 'dist/js/cjs',
|
||||
dart2js: 'dist/js/dart2js'
|
||||
},
|
||||
dart: 'dist/dart',
|
||||
docs: 'dist/docs'
|
||||
},
|
||||
srcFolderInsertion: SRC_FOLDER_INSERTION,
|
||||
transpile: {
|
||||
src: {
|
||||
js: ['modules/**/*.js', 'modules/**/*.es6'],
|
||||
dart: ['modules/**/*.js'],
|
||||
// Migrating to TypeScript, one package at a time.
|
||||
// See https://docs.google.com/document/d/14RJLhu6uuv7NchFkAb6PKzOOO0L7l3Z507eKWzkEUhQ/edit
|
||||
ts2dart: ['modules/angular2/src/di/*.js', 'modules/angular2/test/di/*.js', 'modules/angular2/src/test_lib/*.js']
|
||||
},
|
||||
options: {
|
||||
js: {
|
||||
dev: merge(true, _COMPILER_CONFIG_JS_DEFAULT, {
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
typeAssertions: true,
|
||||
outputLanguage: 'es6'
|
||||
}),
|
||||
prod: merge(true, _COMPILER_CONFIG_JS_DEFAULT, {
|
||||
typeAssertions: false,
|
||||
outputLanguage: 'es6'
|
||||
}),
|
||||
cjs: merge(true, _COMPILER_CONFIG_JS_DEFAULT, {
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
typeAssertions: true,
|
||||
modules: 'commonjs'
|
||||
})
|
||||
},
|
||||
dart: {
|
||||
sourceMaps: true,
|
||||
annotations: true, // parse annotations
|
||||
types: true, // parse types
|
||||
script: false, // parse as a module
|
||||
memberVariables: true, // parse class fields
|
||||
outputLanguage: 'dart'
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
js: {
|
||||
cjs: {
|
||||
src: ['modules/**/README.js.md', 'modules/**/package.json', 'modules/**/*.cjs'],
|
||||
pipes: {
|
||||
'**/*.cjs': gulpPlugins.rename({extname: '.js'}),
|
||||
'**/*.js.md': gulpPlugins.rename(function(file) {
|
||||
file.basename = file.basename.substring(0, file.basename.lastIndexOf('.'));
|
||||
}),
|
||||
'**/package.json': gulpPlugins.template({ 'packageJson': COMMON_PACKAGE_JSON })
|
||||
}
|
||||
},
|
||||
dev: {
|
||||
src: ['modules/**/*.css'],
|
||||
pipes: {}
|
||||
},
|
||||
prod: {
|
||||
src: ['modules/**/*.css'],
|
||||
pipes: {}
|
||||
}
|
||||
},
|
||||
dart: {
|
||||
src: ['modules/**/README.dart.md', 'modules/**/*.dart', 'modules/*/pubspec.yaml', 'modules/**/*.css', '!modules/**/e2e_test/**'],
|
||||
pipes: {
|
||||
'**/*.dart': util.insertSrcFolder(gulpPlugins, SRC_FOLDER_INSERTION.dart),
|
||||
'**/*.dart.md': gulpPlugins.rename(function(file) {
|
||||
file.basename = file.basename.substring(0, file.basename.lastIndexOf('.'));
|
||||
}),
|
||||
'**/pubspec.yaml': gulpPlugins.template({ 'packageJson': COMMON_PACKAGE_JSON })
|
||||
}
|
||||
}
|
||||
},
|
||||
multicopy: {
|
||||
js: {
|
||||
cjs: {
|
||||
src: [
|
||||
'LICENSE'
|
||||
],
|
||||
pipes: {}
|
||||
},
|
||||
dev: {
|
||||
es6: {
|
||||
src: ['tools/build/es5build.js'],
|
||||
pipes: {}
|
||||
}
|
||||
},
|
||||
prod: {
|
||||
es6: {
|
||||
src: ['tools/build/es5build.js'],
|
||||
pipes: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
dart: {
|
||||
src: ['LICENSE'],
|
||||
exclude: ['rtts_assert/'],
|
||||
pipes: {}
|
||||
}
|
||||
},
|
||||
html: {
|
||||
src: {
|
||||
js: ['modules/*/src/**/*.html'],
|
||||
dart: ['modules/*/src/**/*.html']
|
||||
},
|
||||
scriptsPerFolder: {
|
||||
js: {
|
||||
'**': _HTML_DEFAULT_SCRIPTS_JS,
|
||||
'benchmarks/**':
|
||||
[
|
||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||
].concat(_HTML_DEFAULT_SCRIPTS_JS),
|
||||
'benchmarks_external/**':
|
||||
[
|
||||
{ src: 'node_modules/angular/angular.js', mimeType: 'text/javascript', copy: true },
|
||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||
].concat(_HTML_DEFAULT_SCRIPTS_JS),
|
||||
'benchmarks_external/**/*polymer*/**':
|
||||
[
|
||||
{ src: 'bower_components/polymer/lib/polymer.html', copyOnly: true },
|
||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||
]
|
||||
},
|
||||
dart: {
|
||||
'**': _HTML_DEFAULT_SCRIPTS_DART,
|
||||
'benchmarks*/**':
|
||||
[
|
||||
{ src: 'tools/build/snippets/url_params_to_form.js', mimeType: 'text/javascript', copy: true }
|
||||
].concat(_HTML_DEFAULT_SCRIPTS_DART)
|
||||
}
|
||||
}
|
||||
},
|
||||
formatDart: {
|
||||
packageName: 'dart_style',
|
||||
args: ['dart_style:format', '-w', 'dist/dart']
|
||||
},
|
||||
test: {
|
||||
js: {
|
||||
cjs: [
|
||||
'/angular2/test/change_detection/**/*_spec.js',
|
||||
'/angular2/test/core/annotations/**/*_spec.js',
|
||||
'/angular2/test/core/compiler/**/*_spec.js',
|
||||
'/angular2/test/di/**/*_spec.js',
|
||||
'/angular2/test/directives/**/*_spec.js',
|
||||
'/angular2/test/facade/**/*_spec.js',
|
||||
'/angular2/test/forms/**/*_spec.js',
|
||||
'/angular2/test/mock/**/*_spec.js',
|
||||
'/angular2/test/reflection/**/*_spec.js',
|
||||
'/angular2/test/services/**/*_spec.js',
|
||||
'/angular2/test/test_lib/**/*_spec.js'
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
CONFIG.test.js.cjs = CONFIG.test.js.cjs.map(function(s) {return CONFIG.dest.js.cjs + s});
|
||||
|
||||
// ------------
|
||||
// clean
|
||||
|
||||
gulp.task('build/clean.js', clean(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.js.all
|
||||
}));
|
||||
|
||||
gulp.task('build/clean.dart', clean(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.dart
|
||||
}));
|
||||
|
||||
gulp.task('build/clean.docs', clean(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.docs
|
||||
}));
|
||||
|
||||
|
||||
// ------------
|
||||
// transpile
|
||||
|
||||
gulp.task('build/transpile.js.dev.es6', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.js,
|
||||
dest: CONFIG.dest.js.dev.es6,
|
||||
outputExt: 'es6',
|
||||
options: CONFIG.transpile.options.js.dev,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
||||
}));
|
||||
|
||||
gulp.task('build/transpile.js.dev.es5', function() {
|
||||
return es5build({
|
||||
src: CONFIG.dest.js.dev.es6,
|
||||
dest: CONFIG.dest.js.dev.es5,
|
||||
modules: 'instantiate'
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||
const childProcess = require('child_process');
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
gulp.task('build/transpile.js.dev', function(done) {
|
||||
runSequence(
|
||||
'build/transpile.js.dev.es6',
|
||||
'build/transpile.js.dev.es5',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
||||
const tslint = require('gulp-tslint');
|
||||
// Built-in rules are at
|
||||
// https://github.com/palantir/tslint#supported-rules
|
||||
const tslintConfig = require('./tslint.json');
|
||||
return gulp.src(['modules/@angular/**/*.ts', 'modules/benchpress/**/*.ts'])
|
||||
.pipe(tslint({
|
||||
tslint: require('tslint').default,
|
||||
configuration: tslintConfig,
|
||||
rulesDirectory: 'dist/tools/tslint'
|
||||
}))
|
||||
.pipe(tslint.report('prose', {emitError: true}));
|
||||
gulp.task('build/transpile.js.prod.es6', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.js,
|
||||
dest: CONFIG.dest.js.prod.es6,
|
||||
outputExt: 'es6',
|
||||
options: CONFIG.transpile.options.js.prod,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
||||
}));
|
||||
|
||||
gulp.task('build/transpile.js.prod.es5', function() {
|
||||
return es5build({
|
||||
src: CONFIG.dest.js.prod.es6,
|
||||
dest: CONFIG.dest.js.prod.es5,
|
||||
modules: 'instantiate'
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||
gulp.task('build/transpile.js.prod', function(done) {
|
||||
runSequence(
|
||||
'build/transpile.js.prod.es6',
|
||||
'build/transpile.js.prod.es5',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('check-cycle', (done) => {
|
||||
const madge = require('madge');
|
||||
gulp.task('build/transpile.js.cjs', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.js.concat(['modules/**/*.cjs']),
|
||||
dest: CONFIG.dest.js.cjs,
|
||||
outputExt: 'js',
|
||||
options: CONFIG.transpile.options.js.cjs,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js
|
||||
}));
|
||||
gulp.task('build/transformCJSTests', function() {
|
||||
return gulp.src(CONFIG.dest.js.cjs + '/angular2/test/**/*_spec.js').pipe(transformCJSTests()).pipe(gulp.dest(CONFIG.dest.js.cjs + '/angular2/test/'));
|
||||
});
|
||||
|
||||
var dependencyObject = madge(['dist/all/'], {
|
||||
format: 'cjs',
|
||||
extensions: ['.js'],
|
||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||
gulp.task('build/transpile.dart', transpile(gulp, gulpPlugins, {
|
||||
src: CONFIG.transpile.src.dart,
|
||||
dest: CONFIG.dest.dart,
|
||||
outputExt: 'dart',
|
||||
options: CONFIG.transpile.options.dart,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.dart
|
||||
}));
|
||||
|
||||
gulp.task('build/transpile.dart.ts2dart', function() {
|
||||
return gulp.src(CONFIG.transpile.src.ts2dart)
|
||||
.pipe(ts2dart.transpile())
|
||||
.pipe(gulp.dest('dist/dart.ts2dart'))
|
||||
});
|
||||
gulp.task('build/format.dart.ts2dart', rundartpackage(gulp, gulpPlugins, {
|
||||
pub: DART_SDK.PUB,
|
||||
packageName: CONFIG.formatDart.packageName,
|
||||
args: ['dart_style:format', '-w', 'dist/dart.ts2dart']
|
||||
}));
|
||||
|
||||
// Temporary tasks for development on ts2dart. Will likely fail.
|
||||
gulp.task('build/transpile.dart.ts2dart.all', function() {
|
||||
return gulp.src(CONFIG.transpile.src.dart)
|
||||
.pipe(ts2dart.transpile())
|
||||
.pipe(gulp.dest('dist/dart.ts2dart'));
|
||||
});
|
||||
gulp.task('ts2dart', function(done) {
|
||||
runSequence('build/transpile.dart.ts2dart.all', 'build/format.dart.ts2dart', done);
|
||||
});
|
||||
|
||||
// ------------
|
||||
// html
|
||||
|
||||
gulp.task('build/html.js.dev', html(gulp, gulpPlugins, {
|
||||
src: CONFIG.html.src.js,
|
||||
dest: CONFIG.dest.js.dev.es5,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js,
|
||||
scriptsPerFolder: CONFIG.html.scriptsPerFolder.js
|
||||
}));
|
||||
|
||||
gulp.task('build/html.js.prod', html(gulp, gulpPlugins, {
|
||||
src: CONFIG.html.src.js,
|
||||
dest: CONFIG.dest.js.prod.es5,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.js,
|
||||
scriptsPerFolder: CONFIG.html.scriptsPerFolder.js
|
||||
}));
|
||||
|
||||
gulp.task('build/html.dart', html(gulp, gulpPlugins, {
|
||||
src: CONFIG.html.src.dart,
|
||||
dest: CONFIG.dest.dart,
|
||||
srcFolderInsertion: CONFIG.srcFolderInsertion.dart,
|
||||
scriptsPerFolder: CONFIG.html.scriptsPerFolder.dart
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// copy
|
||||
|
||||
gulp.task('build/copy.js.cjs', copy.copy(gulp, gulpPlugins, {
|
||||
src: CONFIG.copy.js.cjs.src,
|
||||
pipes: CONFIG.copy.js.cjs.pipes,
|
||||
dest: CONFIG.dest.js.cjs
|
||||
}));
|
||||
|
||||
gulp.task('build/copy.js.dev', copy.copy(gulp, gulpPlugins, {
|
||||
src: CONFIG.copy.js.dev.src,
|
||||
pipes: CONFIG.copy.js.dev.pipes,
|
||||
dest: CONFIG.dest.js.dev.es5
|
||||
}));
|
||||
|
||||
gulp.task('build/copy.js.prod', copy.copy(gulp, gulpPlugins, {
|
||||
src: CONFIG.copy.js.prod.src,
|
||||
pipes: CONFIG.copy.js.prod.pipes,
|
||||
dest: CONFIG.dest.js.prod.es5
|
||||
}));
|
||||
|
||||
gulp.task('build/copy.dart', copy.copy(gulp, gulpPlugins, {
|
||||
src: CONFIG.copy.dart.src,
|
||||
pipes: CONFIG.copy.dart.pipes,
|
||||
dest: CONFIG.dest.dart
|
||||
}));
|
||||
|
||||
|
||||
// ------------
|
||||
// multicopy
|
||||
|
||||
gulp.task('build/multicopy.js.cjs', copy.multicopy(gulp, gulpPlugins, {
|
||||
src: CONFIG.multicopy.js.cjs.src,
|
||||
pipes: CONFIG.multicopy.js.cjs.pipes,
|
||||
exclude: CONFIG.multicopy.js.cjs.exclude,
|
||||
dest: CONFIG.dest.js.cjs
|
||||
}));
|
||||
|
||||
gulp.task('build/multicopy.js.dev.es6', copy.multicopy(gulp, gulpPlugins, {
|
||||
src: CONFIG.multicopy.js.dev.es6.src,
|
||||
pipes: CONFIG.multicopy.js.dev.es6.pipes,
|
||||
exclude: CONFIG.multicopy.js.dev.es6.exclude,
|
||||
dest: CONFIG.dest.js.dev.es6
|
||||
}));
|
||||
|
||||
gulp.task('build/multicopy.js.prod.es6', copy.multicopy(gulp, gulpPlugins, {
|
||||
src: CONFIG.multicopy.js.prod.es6.src,
|
||||
pipes: CONFIG.multicopy.js.prod.es6.pipes,
|
||||
exclude: CONFIG.multicopy.js.prod.es6.exclude,
|
||||
dest: CONFIG.dest.js.prod.es6
|
||||
}));
|
||||
|
||||
gulp.task('build/multicopy.dart', copy.multicopy(gulp, gulpPlugins, {
|
||||
src: CONFIG.multicopy.dart.src,
|
||||
pipes: CONFIG.multicopy.dart.pipes,
|
||||
exclude: CONFIG.multicopy.dart.exclude,
|
||||
dest: CONFIG.dest.dart
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// pubspec
|
||||
|
||||
gulp.task('build/pubspec.dart', pubget(gulp, gulpPlugins, {
|
||||
dir: CONFIG.dest.dart,
|
||||
command: DART_SDK.PUB
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// linknodemodules
|
||||
|
||||
gulp.task('build/linknodemodules.js.cjs', linknodemodules(gulp, gulpPlugins, {
|
||||
dir: CONFIG.dest.js.cjs
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// dartanalyzer
|
||||
|
||||
gulp.task('build/analyze.dart', dartanalyzer(gulp, gulpPlugins, {
|
||||
dest: CONFIG.dest.dart,
|
||||
command: DART_SDK.ANALYZER
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// pubbuild
|
||||
|
||||
gulp.task('build/pubbuild.dart', pubbuild(gulp, gulpPlugins, {
|
||||
src: CONFIG.dest.dart,
|
||||
dest: CONFIG.dest.js.dart2js,
|
||||
command: DART_SDK.PUB
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// format dart
|
||||
|
||||
gulp.task('build/format.dart', rundartpackage(gulp, gulpPlugins, {
|
||||
pub: DART_SDK.PUB,
|
||||
packageName: CONFIG.formatDart.packageName,
|
||||
args: CONFIG.formatDart.args
|
||||
}));
|
||||
|
||||
// ------------
|
||||
// check circular dependencies in Node.js context
|
||||
gulp.task('build/checkCircularDependencies', function (done) {
|
||||
var dependencyObject = madge(CONFIG.dest.js.dev.es6, {
|
||||
format: 'es6',
|
||||
paths: [CONFIG.dest.js.dev.es6],
|
||||
extensions: ['.js', '.es6'],
|
||||
onParseFile: function(data) {
|
||||
data.src = data.src.replace(/import \* as/g, "//import * as");
|
||||
}
|
||||
});
|
||||
var circularDependencies = dependencyObject.circular().getArray();
|
||||
if (circularDependencies.length > 0) {
|
||||
console.log('Found circular dependencies!');
|
||||
console.log(circularDependencies);
|
||||
process.exit(1);
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('serve', () => {
|
||||
let connect = require('gulp-connect');
|
||||
let cors = require('cors');
|
||||
// ------------------
|
||||
// web servers
|
||||
gulp.task('serve.js.dev', jsserve(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.js.dev.es5,
|
||||
port: 8000
|
||||
}));
|
||||
|
||||
connect.server({
|
||||
root: `${__dirname}/dist`,
|
||||
port: 8000,
|
||||
livereload: false,
|
||||
open: false,
|
||||
middleware: (connect, opt) => [cors()]
|
||||
gulp.task('serve.js.prod', jsserve(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.js.prod.es5,
|
||||
port: 8001
|
||||
}));
|
||||
|
||||
gulp.task('serve.js.dart2js', jsserve(gulp, gulpPlugins, {
|
||||
path: CONFIG.dest.js.dart2js,
|
||||
port: 8002
|
||||
}));
|
||||
|
||||
gulp.task('serve/examples.dart', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
path: CONFIG.dest.dart + '/examples'
|
||||
}));
|
||||
|
||||
gulp.task('serve/benchmarks.dart', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
path: CONFIG.dest.dart + '/benchmarks'
|
||||
}));
|
||||
|
||||
gulp.task('serve/benchmarks_external.dart', pubserve(gulp, gulpPlugins, {
|
||||
command: DART_SDK.PUB,
|
||||
path: CONFIG.dest.dart + '/benchmarks_external'
|
||||
}));
|
||||
|
||||
// --------------
|
||||
// doc generation
|
||||
var Dgeni = require('dgeni');
|
||||
var bower = require('bower');
|
||||
var jasmine = require('gulp-jasmine');
|
||||
var webserver = require('gulp-webserver');
|
||||
|
||||
gulp.task('docs/bower', function() {
|
||||
var bowerTask = bower.commands.install(undefined, undefined, { cwd: 'docs' });
|
||||
bowerTask.on('log', function (result) {
|
||||
console.log('bower:', result.id, result.data.endpoint.name);
|
||||
});
|
||||
bowerTask.on('error', function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
return bowerTask;
|
||||
});
|
||||
|
||||
|
||||
gulp.task('changelog', () => {
|
||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||
function createDocsTasks(public) {
|
||||
var dgeniPackage = public ? './docs/public-docs-package' : './docs/dgeni-package';
|
||||
var distDocsPath = public ? 'dist/public_docs' : 'dist/docs';
|
||||
var taskPrefix = public ? 'public_docs' : 'docs';
|
||||
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 1
|
||||
}, {
|
||||
// Conventional Changelog Context
|
||||
// We have to manually set version number so it doesn't get prefixed with `v`
|
||||
// See https://github.com/conventional-changelog/conventional-changelog-core/issues/10
|
||||
currentTag: require('./package.json').version
|
||||
}))
|
||||
.pipe(gulp.dest('./'));
|
||||
});
|
||||
gulp.task(taskPrefix + '/dgeni', function() {
|
||||
try {
|
||||
var dgeni = new Dgeni([require(dgeniPackage)]);
|
||||
return dgeni.generate();
|
||||
} catch(x) {
|
||||
console.log(x.stack);
|
||||
throw x;
|
||||
}
|
||||
});
|
||||
|
||||
function tsc(projectPath, done) {
|
||||
const childProcess = require('child_process');
|
||||
gulp.task(taskPrefix + '/assets', ['docs/bower'], function() {
|
||||
return gulp.src('docs/bower_components/**/*')
|
||||
.pipe(gulp.dest(distDocsPath + '/lib'));
|
||||
});
|
||||
|
||||
childProcess
|
||||
.spawn(
|
||||
path.normalize(`${__dirname}/node_modules/.bin/tsc`) + (/^win/.test(os.platform()) ? '.cmd' : ''),
|
||||
['-p', path.join(__dirname, projectPath)],
|
||||
{stdio: 'inherit'})
|
||||
.on('close', (errorCode) => done(errorCode));
|
||||
gulp.task(taskPrefix + '/app', function() {
|
||||
return gulp.src('docs/app/**/*')
|
||||
.pipe(gulp.dest(distDocsPath));
|
||||
});
|
||||
|
||||
gulp.task(taskPrefix, [taskPrefix + '/assets', taskPrefix + '/app', taskPrefix + '/dgeni']);
|
||||
gulp.task(taskPrefix + '/watch', function() {
|
||||
return gulp.watch('docs/app/**/*', [taskPrefix + '/app']);
|
||||
});
|
||||
|
||||
gulp.task(taskPrefix + '/test', function () {
|
||||
return gulp.src('docs/**/*.spec.js')
|
||||
.pipe(jasmine({
|
||||
includeStackTrace: true
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task(taskPrefix + '/serve', function() {
|
||||
gulp.src(distDocsPath + '/')
|
||||
.pipe(webserver({
|
||||
fallback: 'index.html'
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
createDocsTasks(true);
|
||||
createDocsTasks(false);
|
||||
|
||||
// ------------------
|
||||
// karma tests
|
||||
// These tests run in the browser and are allowed to access
|
||||
// HTML DOM APIs.
|
||||
function getBrowsersFromCLI() {
|
||||
var args = minimist(process.argv.slice(2));
|
||||
return [args.browsers?args.browsers:'DartiumWithWebPlatform']
|
||||
}
|
||||
gulp.task('test.unit.js', function (done) {
|
||||
karma.start({configFile: __dirname + '/karma-js.conf.js'}, done);
|
||||
});
|
||||
gulp.task('test.unit.dart', function (done) {
|
||||
karma.start({configFile: __dirname + '/karma-dart.conf.js'}, done);
|
||||
});
|
||||
gulp.task('test.unit.js/ci', function (done) {
|
||||
karma.start({configFile: __dirname + '/karma-js.conf.js',
|
||||
singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done);
|
||||
});
|
||||
gulp.task('test.unit.dart/ci', function (done) {
|
||||
karma.start({configFile: __dirname + '/karma-dart.conf.js',
|
||||
singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done);
|
||||
});
|
||||
gulp.task('test.unit.cjs/ci', function () {
|
||||
return gulp.src(CONFIG.test.js.cjs).pipe(jasmine({includeStackTrace: true, timeout: 1000}));
|
||||
});
|
||||
gulp.task('test.unit.cjs', ['build.js.cjs'], function () {
|
||||
//Run tests once
|
||||
runSequence('test.unit.cjs/ci', function() {});
|
||||
//Watcher to transpile file changed
|
||||
gulp.watch(CONFIG.transpile.src.js.concat(['modules/**/*.cjs']), function(event) {
|
||||
var relPath = path.relative(__dirname, event.path).replace(/\\/g, "/");
|
||||
gulp.src(relPath)
|
||||
.pipe(gulpPlugins.rename({extname: '.'+ 'js'}))
|
||||
.pipe(util.insertSrcFolder(gulpPlugins, CONFIG.srcFolderInsertion.js))
|
||||
.pipe(gulpTraceur(CONFIG.transpile.options.js.cjs, file2moduleName))
|
||||
.pipe(transformCJSTests())
|
||||
.pipe(gulp.dest(CONFIG.dest.js.cjs + path.dirname(relPath.replace("modules", ""))));
|
||||
});
|
||||
//Watcher to run tests when dist/js/cjs/angular2 is updated by the first watcher (after clearing the node cache)
|
||||
gulp.watch(CONFIG.dest.js.cjs + '/angular2/**/*.js', function(event) {
|
||||
for (var id in require.cache) {
|
||||
if (id.replace(/\\/g, "/").indexOf(CONFIG.dest.js.cjs) > -1) {
|
||||
delete require.cache[id];
|
||||
}
|
||||
}
|
||||
runSequence('test.unit.cjs/ci', function() {});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ------------------
|
||||
// server tests
|
||||
// These tests run on the VM on the command-line and are
|
||||
// allowed to access the file system and network.
|
||||
gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, {
|
||||
dest: 'dist/dart'
|
||||
}));
|
||||
|
||||
// -----------------
|
||||
// test builders
|
||||
gulp.task('test.transpiler.unittest', function (done) {
|
||||
return gulp.src('tools/transpiler/unittest/**/*.js')
|
||||
.pipe(jasmine({
|
||||
includeStackTrace: true
|
||||
}))
|
||||
});
|
||||
|
||||
// Copy test resources to dist
|
||||
gulp.task('tests/transform.dart', function() {
|
||||
return gulp.src('modules/angular2/test/transform/**')
|
||||
.pipe(gulp.dest('dist/dart/angular2/test/transform'));
|
||||
});
|
||||
|
||||
|
||||
|
||||
// -----------------
|
||||
// orchestrated targets
|
||||
|
||||
// Builds all Dart packages, but does not compile them
|
||||
gulp.task('build/packages.dart', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.dart.ts2dart', 'build/transpile.dart', 'build/html.dart', 'build/copy.dart', 'build/multicopy.dart'],
|
||||
'tests/transform.dart',
|
||||
['build/format.dart.ts2dart', 'build/format.dart'],
|
||||
'build/pubspec.dart',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
// Builds and compiles all Dart packages
|
||||
gulp.task('build.dart', function(done) {
|
||||
runSequence(
|
||||
'build/packages.dart',
|
||||
'build/analyze.dart',
|
||||
'build/pubbuild.dart',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.js.dev', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.dev', 'build/html.js.dev', 'build/copy.js.dev', 'build/multicopy.js.dev.es6'],
|
||||
'build/checkCircularDependencies',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.js.prod', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.prod', 'build/html.js.prod', 'build/copy.js.prod', 'build/multicopy.js.prod.es6'],
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.js.cjs', function(done) {
|
||||
runSequence(
|
||||
['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'],
|
||||
['build/linknodemodules.js.cjs'],
|
||||
'build/transformCJSTests',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
gulp.task('build.js', ['build.js.dev', 'build.js.prod', 'build.js.cjs']);
|
||||
|
||||
gulp.task('clean', ['build/clean.js', 'build/clean.dart', 'build/clean.docs']);
|
||||
|
||||
gulp.task('build', ['build.js', 'build.dart']);
|
||||
|
93
karma-dart.conf.js
Normal file
93
karma-dart.conf.js
Normal file
@ -0,0 +1,93 @@
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
var file2moduleName = require('./tools/build/file2modulename');
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
frameworks: ['dart-unittest'],
|
||||
|
||||
files: [
|
||||
// Unit test files needs to be included.
|
||||
// Karma-dart generates `__adapter_unittest.dart` that imports these files.
|
||||
{pattern: 'modules/*/test/**/*_spec.js', included: true},
|
||||
{pattern: 'tools/transpiler/spec/**/*_spec.js', included: true},
|
||||
|
||||
// These files are not included, they are imported by the unit tests above.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**/*', included: false},
|
||||
|
||||
// Dependencies, installed with `pub install`.
|
||||
{pattern: 'packages/**/*.dart', included: false, watched: false},
|
||||
|
||||
// Init and configure guiness.
|
||||
{pattern: 'test-main.dart', included: true}
|
||||
],
|
||||
|
||||
karmaDartImports: {
|
||||
guinness: 'package:guinness/guinness_html.dart'
|
||||
},
|
||||
|
||||
// TODO(vojta): Remove the localhost:9877 from urls, once the proxy fix is merged:
|
||||
// https://github.com/karma-runner/karma/pull/1207
|
||||
//
|
||||
// Map packages to the correct urls where Karma serves them.
|
||||
proxies: {
|
||||
// Dependencies installed with `pub install`.
|
||||
'/packages/unittest': 'http://localhost:9877/base/packages/unittest',
|
||||
'/packages/guinness': 'http://localhost:9877/base/packages/guinness',
|
||||
'/packages/matcher': 'http://localhost:9877/base/packages/matcher',
|
||||
'/packages/stack_trace': 'http://localhost:9877/base/packages/stack_trace',
|
||||
'/packages/collection': 'http://localhost:9877/base/packages/collection',
|
||||
'/packages/path': 'http://localhost:9877/base/packages/path',
|
||||
|
||||
// Local dependencies, transpiled from the source.
|
||||
'/packages/angular': 'http://localhost:9877/base/modules/angular',
|
||||
'/packages/benchpress': 'http://localhost:9877/base/modules/benchpress',
|
||||
'/packages/core': 'http://localhost:9877/base/modules/core',
|
||||
'/packages/change_detection': 'http://localhost:9877/base/modules/change_detection',
|
||||
'/packages/reflection': 'http://localhost:9877/base/modules/reflection',
|
||||
'/packages/di': 'http://localhost:9877/base/modules/di',
|
||||
'/packages/directives': 'http://localhost:9877/base/modules/directives',
|
||||
'/packages/facade': 'http://localhost:9877/base/modules/facade',
|
||||
'/packages/forms': 'http://localhost:9877/base/modules/forms',
|
||||
'/packages/test_lib': 'http://localhost:9877/base/modules/test_lib',
|
||||
'/packages/mock': 'http://localhost:9877/base/modules/mock',
|
||||
},
|
||||
|
||||
preprocessors: {
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'tools/**/*.js': ['traceur']
|
||||
},
|
||||
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
outputLanguage: 'dart',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
modules: 'register',
|
||||
memberVariables: true,
|
||||
types: true,
|
||||
// typeAssertions: true,
|
||||
// typeAssertionModule: 'assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace('.js', '.dart');
|
||||
}
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
DartiumWithWebPlatform: {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] }
|
||||
},
|
||||
browsers: ['DartiumWithWebPlatform'],
|
||||
|
||||
port: 9877
|
||||
});
|
||||
|
||||
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
};
|
119
karma-js.conf.js
119
karma-js.conf.js
@ -1,8 +1,7 @@
|
||||
var browserProvidersConf = require('./browser-providers.conf.js');
|
||||
var internalAngularReporter = require('./tools/karma/reporter.js');
|
||||
|
||||
// Karma configuration
|
||||
// Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT)
|
||||
var file2moduleName = require('./tools/build/file2modulename');
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
@ -10,102 +9,60 @@ module.exports = function(config) {
|
||||
|
||||
files: [
|
||||
// Sources and specs.
|
||||
// Loaded through the System loader, in `test-main.js`.
|
||||
{pattern: 'dist/all/@angular/**/*.js', included: false, watched: true},
|
||||
|
||||
'node_modules/es6-shim/es6-shim.js',
|
||||
// include Angular v1 for upgrade module testing
|
||||
'node_modules/angular/angular.min.js',
|
||||
|
||||
'node_modules/zone.js/dist/zone.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/zone.js/dist/jasmine-patch.js',
|
||||
'node_modules/zone.js/dist/async-test.js',
|
||||
'node_modules/zone.js/dist/fake-async-test.js',
|
||||
// Loaded through the es6-module-loader, in `test-main.js`.
|
||||
{pattern: 'modules/**', included: false},
|
||||
{pattern: 'tools/transpiler/spec/**', included: false},
|
||||
|
||||
'node_modules/traceur/bin/traceur-runtime.js',
|
||||
'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js',
|
||||
// Including systemjs because it defines `__eval`, which produces correct stack traces.
|
||||
'shims_for_IE.js',
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
{pattern: 'node_modules/rxjs/**', included: false, watched: false, served: true},
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
'node_modules/systemjs/lib/extension-register.js',
|
||||
'node_modules/zone.js/zone.js',
|
||||
'node_modules/zone.js/long-stack-trace-zone.js',
|
||||
|
||||
'tools/build/file2modulename.js',
|
||||
'test-main.js',
|
||||
{pattern: 'dist/all/empty.*', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/static_assets/**', included: false, watched: false},
|
||||
{pattern: 'modules/@angular/platform-browser/test/browser/static_assets/**', included: false, watched: false}
|
||||
'test-main.js'
|
||||
],
|
||||
|
||||
exclude: [
|
||||
'dist/all/@angular/**/e2e_test/**',
|
||||
'dist/all/@angular/examples/**',
|
||||
'dist/all/@angular/router/**',
|
||||
'dist/all/@angular/compiler-cli/**',
|
||||
'dist/all/angular1_router.js',
|
||||
'dist/all/@angular/platform-browser/testing/e2e_util.js'
|
||||
],
|
||||
|
||||
customLaunchers: browserProvidersConf.customLaunchers,
|
||||
|
||||
plugins: [
|
||||
'karma-jasmine',
|
||||
'karma-browserstack-launcher',
|
||||
'karma-sauce-launcher',
|
||||
'karma-chrome-launcher',
|
||||
'karma-sourcemap-loader',
|
||||
internalAngularReporter
|
||||
'modules/**/e2e_test/**'
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'**/*.js': ['sourcemap']
|
||||
'modules/**/*.js': ['traceur'],
|
||||
'modules/**/*.es6': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.js': ['traceur'],
|
||||
'tools/transpiler/spec/**/*.es6': ['traceur'],
|
||||
},
|
||||
|
||||
reporters: ['internal-angular'],
|
||||
sauceLabs: {
|
||||
testName: 'Angular2',
|
||||
retryLimit: 3,
|
||||
startConnect: false,
|
||||
recordVideo: false,
|
||||
recordScreenshots: false,
|
||||
traceurPreprocessor: {
|
||||
options: {
|
||||
'selenium-version': '2.53.0',
|
||||
'command-timeout': 600,
|
||||
'idle-timeout': 600,
|
||||
'max-duration': 5400
|
||||
outputLanguage: 'es5',
|
||||
sourceMaps: true,
|
||||
script: false,
|
||||
memberVariables: true,
|
||||
modules: 'instantiate',
|
||||
types: true,
|
||||
typeAssertions: true,
|
||||
typeAssertionModule: 'rtts_assert/rtts_assert',
|
||||
annotations: true
|
||||
},
|
||||
resolveModuleName: file2moduleName,
|
||||
transformPath: function(fileName) {
|
||||
return fileName.replace(/\.es6$/, '.js');
|
||||
}
|
||||
},
|
||||
|
||||
browserStack: {
|
||||
project: 'Angular2',
|
||||
startTunnel: false,
|
||||
retryLimit: 3,
|
||||
timeout: 600,
|
||||
pollingTimeout: 10000
|
||||
customLaunchers: {
|
||||
DartiumWithWebPlatform: {
|
||||
base: 'Dartium',
|
||||
flags: ['--enable-experimental-web-platform-features'] }
|
||||
},
|
||||
browsers: ['ChromeCanary'],
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
port: 9876,
|
||||
captureTimeout: 60000,
|
||||
browserDisconnectTimeout : 60000,
|
||||
browserDisconnectTolerance : 3,
|
||||
browserNoActivityTimeout : 60000,
|
||||
port: 9876
|
||||
});
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.CI_MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
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.CI_MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
}
|
||||
config.plugins.push(require('./tools/transpiler/karma-traceur-preprocessor'));
|
||||
};
|
||||
|
1
karma.conf.js
Symbolic link
1
karma.conf.js
Symbolic link
@ -0,0 +1 @@
|
||||
karma-js.conf.js
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModule} from '@angular/core';
|
||||
import {COMMON_DIRECTIVES} from './src/common_directives';
|
||||
import {COMMON_PIPES} from './src/pipes';
|
||||
|
||||
export * from './src/pipes';
|
||||
export * from './src/directives';
|
||||
export * from './src/forms-deprecated';
|
||||
export * from './src/common_directives';
|
||||
export * from './src/location';
|
||||
export {NgLocalization} from './src/localization';
|
||||
|
||||
// Note: This does not contain the location providers,
|
||||
// as they need some platform specific implementations to work.
|
||||
/**
|
||||
* The module that includes all the basic Angular directives like {@link NgIf}, ${link NgFor}, ...
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@NgModule(
|
||||
{declarations: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES]})
|
||||
export class CommonModule {
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "@angular/common",
|
||||
"version": "0.0.0-PLACEHOLDER",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"jsnext:main": "esm/index.js",
|
||||
"typings": "index.d.ts",
|
||||
"author": "angular",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/core": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.git"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
|
||||
export default {
|
||||
entry: '../../../dist/packages-dist/common/esm/index.js',
|
||||
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
|
||||
format: 'umd',
|
||||
moduleName: 'ng.common',
|
||||
globals: {
|
||||
'@angular/core': 'ng.core',
|
||||
'rxjs/Subject': 'Rx',
|
||||
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
|
||||
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
|
||||
'rxjs/Observable': 'Rx'
|
||||
},
|
||||
plugins: [
|
||||
// nodeResolve({ jsnext: true, main: true }),
|
||||
]
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {CORE_DIRECTIVES} from './directives';
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application. This includes core directives (e.g., NgIf and NgFor), and forms directives (e.g.,
|
||||
* NgModel).
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` decorator.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Instead of writing:
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm} from
|
||||
* '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, NgModel, NgForm,
|
||||
* OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* one could import all the common directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {COMMON_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [COMMON_DIRECTIVES, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental Contains forms which are experimental.
|
||||
*/
|
||||
export const COMMON_DIRECTIVES: Type[][] = [CORE_DIRECTIVES];
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* Common directives shipped with Angular.
|
||||
*/
|
||||
export {CORE_DIRECTIVES} from './directives/core_directives';
|
||||
export {NgClass} from './directives/ng_class';
|
||||
export {NgFor} from './directives/ng_for';
|
||||
export {NgIf} from './directives/ng_if';
|
||||
export {NgPlural, NgPluralCase} from './directives/ng_plural';
|
||||
export {NgStyle} from './directives/ng_style';
|
||||
export {NgSwitch, NgSwitchCase, NgSwitchDefault} from './directives/ng_switch';
|
||||
export {NgTemplateOutlet} from './directives/ng_template_outlet';
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '../facade/lang';
|
||||
|
||||
import {NgClass} from './ng_class';
|
||||
import {NgFor} from './ng_for';
|
||||
import {NgIf} from './ng_if';
|
||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||
import {NgStyle} from './ng_style';
|
||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
||||
import {NgTemplateOutlet} from './ng_template_outlet';
|
||||
|
||||
/**
|
||||
* A collection of Angular core directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
*
|
||||
* This collection can be used to quickly enumerate all the built-in directives in the `directives`
|
||||
* property of the `@Component` annotation.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yakGwpCdUkg0qfzX5m8g?p=preview))
|
||||
*
|
||||
* Instead of writing:
|
||||
*
|
||||
* ```typescript
|
||||
* import {NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [NgClass, NgIf, NgFor, NgSwitch, NgSwitchWhen, NgSwitchDefault, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
* one could import all the core directives at once:
|
||||
*
|
||||
* ```typescript
|
||||
* import {CORE_DIRECTIVES} from '@angular/common';
|
||||
* import {OtherDirective} from './myDirectives';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'myComponent.html',
|
||||
* directives: [CORE_DIRECTIVES, OtherDirective]
|
||||
* })
|
||||
* export class MyComponent {
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export const CORE_DIRECTIVES: Type[] = [
|
||||
NgClass,
|
||||
NgFor,
|
||||
NgIf,
|
||||
NgTemplateOutlet,
|
||||
NgStyle,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
NgPlural,
|
||||
NgPluralCase,
|
||||
];
|
@ -1,189 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper, isListLikeIterable} from '../facade/collection';
|
||||
import {isArray, isPresent, isString} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
||||
* an expression's evaluation result.
|
||||
*
|
||||
* The result of an expression evaluation is interpreted differently depending on type of
|
||||
* the expression evaluation result:
|
||||
* - `string` - all the CSS classes listed in a string (space delimited) are added
|
||||
* - `Array` - all the CSS classes (Array elements) are added
|
||||
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
|
||||
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
|
||||
* is added - otherwise it is removed.
|
||||
*
|
||||
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
|
||||
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
|
||||
* all the CSS class names in a template.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgClass} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'toggle-button',
|
||||
* inputs: ['isDisabled'],
|
||||
* template: `
|
||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
||||
* (click)="toggle(!isOn)">
|
||||
* Click me!
|
||||
* </div>`,
|
||||
* styles: [`
|
||||
* .button {
|
||||
* width: 120px;
|
||||
* border: medium solid black;
|
||||
* }
|
||||
*
|
||||
* .active {
|
||||
* background-color: red;
|
||||
* }
|
||||
*
|
||||
* .disabled {
|
||||
* color: gray;
|
||||
* border: medium solid gray;
|
||||
* }
|
||||
* `],
|
||||
* directives: [NgClass]
|
||||
* })
|
||||
* class ToggleButton {
|
||||
* isOn = false;
|
||||
* isDisabled = false;
|
||||
*
|
||||
* toggle(newState) {
|
||||
* if (!this.isDisabled) {
|
||||
* this.isOn = newState;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngClass]'})
|
||||
export class NgClass implements DoCheck {
|
||||
private _iterableDiffer: IterableDiffer;
|
||||
private _keyValueDiffer: KeyValueDiffer;
|
||||
private _initialClasses: string[] = [];
|
||||
private _rawClass: string[]|Set<string>;
|
||||
|
||||
constructor(
|
||||
private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
|
||||
@Input('class')
|
||||
set initialClasses(v: string) {
|
||||
this._applyInitialClasses(true);
|
||||
this._initialClasses = isPresent(v) && isString(v) ? v.split(' ') : [];
|
||||
this._applyInitialClasses(false);
|
||||
this._applyClasses(this._rawClass, false);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngClass(v: string|string[]|Set<string>|{[key: string]: any}) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
|
||||
if (isString(v)) {
|
||||
v = (<string>v).split(' ');
|
||||
}
|
||||
|
||||
this._rawClass = <string[]|Set<string>>v;
|
||||
this._iterableDiffer = null;
|
||||
this._keyValueDiffer = null;
|
||||
if (isPresent(v)) {
|
||||
if (isListLikeIterable(v)) {
|
||||
this._iterableDiffer = this._iterableDiffers.find(v).create(null);
|
||||
} else {
|
||||
this._keyValueDiffer = this._keyValueDiffers.find(v).create(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (isPresent(this._iterableDiffer)) {
|
||||
var changes = this._iterableDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyIterableChanges(changes);
|
||||
}
|
||||
}
|
||||
if (isPresent(this._keyValueDiffer)) {
|
||||
var changes = this._keyValueDiffer.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[key: string]: any}): void {
|
||||
this._applyClasses(rawClassVal, true);
|
||||
this._applyInitialClasses(false);
|
||||
}
|
||||
|
||||
private _applyKeyValueChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
||||
if (record.previousValue) {
|
||||
this._toggleClass(record.key, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem(
|
||||
(record: CollectionChangeRecord) => { this._toggleClass(record.item, false); });
|
||||
}
|
||||
|
||||
private _applyInitialClasses(isCleanup: boolean) {
|
||||
this._initialClasses.forEach(className => this._toggleClass(className, !isCleanup));
|
||||
}
|
||||
|
||||
private _applyClasses(
|
||||
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
||||
if (isPresent(rawClassVal)) {
|
||||
if (isArray(rawClassVal)) {
|
||||
(<string[]>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else if (rawClassVal instanceof Set) {
|
||||
(<Set<string>>rawClassVal).forEach(className => this._toggleClass(className, !isCleanup));
|
||||
} else {
|
||||
StringMapWrapper.forEach(
|
||||
<{[k: string]: any}>rawClassVal, (expVal: any, className: string) => {
|
||||
if (isPresent(expVal)) this._toggleClass(className, !isCleanup);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _toggleClass(className: string, enabled: boolean): void {
|
||||
className = className.trim();
|
||||
if (className.length > 0) {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
var classes = className.split(/\s+/g);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
||||
}
|
||||
} else {
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {getTypeNameForDebugging, isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
export class NgForRow {
|
||||
constructor(public $implicit: any, public index: number, public count: number) {}
|
||||
|
||||
get first(): boolean { return this.index === 0; }
|
||||
|
||||
get last(): boolean { return this.index === this.count - 1; }
|
||||
|
||||
get even(): boolean { return this.index % 2 === 0; }
|
||||
|
||||
get odd(): boolean { return !this.even; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||
* each instantiated template inherits from the outer context with the given loop variable set
|
||||
* to the current item from the iterable.
|
||||
*
|
||||
* ### Local Variables
|
||||
*
|
||||
* `NgFor` provides several exported values that can be aliased to local variables:
|
||||
*
|
||||
* * `index` will be set to the current loop iteration for each template context.
|
||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
||||
* iteration.
|
||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
||||
* iteration.
|
||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
||||
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
|
||||
*
|
||||
* ### Change Propagation
|
||||
*
|
||||
* When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
|
||||
*
|
||||
* * When an item is added, a new instance of the template is added to the DOM.
|
||||
* * When an item is removed, its template instance is removed from the DOM.
|
||||
* * When items are reordered, their respective templates are reordered in the DOM.
|
||||
* * Otherwise, the DOM element for that item will remain the same.
|
||||
*
|
||||
* Angular uses object identity to track insertions and deletions within the iterator and reproduce
|
||||
* those changes in the DOM. This has important implications for animations and any stateful
|
||||
* controls
|
||||
* (such as `<input>` elements which accept user input) that are present. Inserted rows can be
|
||||
* animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such
|
||||
* as user input.
|
||||
*
|
||||
* It is possible for the identities of elements in the iterator to change while the data does not.
|
||||
* This can happen, for example, if the iterator produced from an RPC to the server, and that
|
||||
* RPC is re-run. Even if the data hasn't changed, the second response will produce objects with
|
||||
* different identities, and Angular will tear down the entire DOM and rebuild it (as if all old
|
||||
* elements were deleted and all new elements inserted). This is an expensive operation and should
|
||||
* be avoided if possible.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<li *ngFor="let item of items; let i = index">...</li>`
|
||||
* - `<li template="ngFor let item of items; let i = index">...</li>`
|
||||
* - `<template ngFor let-item [ngForOf]="items" let-i="index"><li>...</li></template>`
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
|
||||
* example.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngFor][ngForOf]'})
|
||||
export class NgFor implements DoCheck, OnChanges {
|
||||
@Input() ngForOf: any;
|
||||
@Input() ngForTrackBy: TrackByFn;
|
||||
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
|
||||
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
@Input()
|
||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||
if (isPresent(value)) {
|
||||
this._templateRef = value;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if ('ngForOf' in changes) {
|
||||
// React on ngForOf changes only once all inputs have been initialized
|
||||
const value = changes['ngForOf'].currentValue;
|
||||
if (isBlank(this._differ) && isPresent(value)) {
|
||||
try {
|
||||
this._differ = this._iterableDiffers.find(value).create(this._cdr, this.ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new BaseException(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (isPresent(changes)) this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
||||
const insertTuples: RecordViewTuple[] = [];
|
||||
changes.forEachOperation(
|
||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||
if (item.previousIndex == null) {
|
||||
let view = this._viewContainer.createEmbeddedView(
|
||||
this._templateRef, new NgForRow(null, null, null), currentIndex);
|
||||
let tuple = new RecordViewTuple(item, view);
|
||||
insertTuples.push(tuple);
|
||||
} else if (currentIndex == null) {
|
||||
this._viewContainer.remove(adjustedPreviousIndex);
|
||||
} else {
|
||||
let view = this._viewContainer.get(adjustedPreviousIndex);
|
||||
this._viewContainer.move(view, currentIndex);
|
||||
let tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
||||
insertTuples.push(tuple);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < insertTuples.length; i++) {
|
||||
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isBlank} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Removes or recreates a portion of the DOM tree based on an {expression}.
|
||||
*
|
||||
* If the expression assigned to `ngIf` evaluates to a false value then the element
|
||||
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* <div *ngIf="errorCount > 0" class="error">
|
||||
* <!-- Error message displayed when the errorCount property on the current context is greater
|
||||
* than 0. -->
|
||||
* {{errorCount}} errors detected
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div *ngIf="condition">...</div>`
|
||||
* - `<div template="ngIf condition">...</div>`
|
||||
* - `<template [ngIf]="condition"><div>...</div></template>`
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngIf]'})
|
||||
export class NgIf {
|
||||
private _prevCondition: boolean = null;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngIf(newCondition: any) {
|
||||
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
|
||||
this._prevCondition = true;
|
||||
this._viewContainer.createEmbeddedView(this._templateRef);
|
||||
} else if (!newCondition && (isBlank(this._prevCondition) || this._prevCondition)) {
|
||||
this._prevCondition = false;
|
||||
this._viewContainer.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, Directive, Host, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {NgLocalization, getPluralCategory} from '../localization';
|
||||
|
||||
import {SwitchView} from './ng_switch';
|
||||
|
||||
|
||||
/**
|
||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
||||
*
|
||||
* To use this directive, you must provide an extension of `NgLocalization` that maps values to
|
||||
* category names. You then define a container element that sets the `[ngPlural]` attribute to a
|
||||
* switch expression.
|
||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
||||
* expression.
|
||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||
* matches the switch expression exactly.
|
||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
||||
* value matches aren't found and the value maps to its category using the `getPluralCategory`
|
||||
* function provided.
|
||||
*
|
||||
* ```typescript
|
||||
* class MyLocalization extends NgLocalization {
|
||||
* getPluralCategory(value: any) {
|
||||
* if(value < 5) {
|
||||
* return 'few';
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* providers: [{provide: NgLocalization, useClass: MyLocalization}]
|
||||
* })
|
||||
* @View({
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngPlural]="value">
|
||||
* <template ngPluralCase="=0">there is nothing</template>
|
||||
* <template ngPluralCase="=1">there is one</template>
|
||||
* <template ngPluralCase="few">there are a few</template>
|
||||
* <template ngPluralCase="other">there is some number</template>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [NgPlural, NgPluralCase]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPlural]'})
|
||||
export class NgPlural {
|
||||
private _switchValue: number;
|
||||
private _activeView: SwitchView;
|
||||
private _caseViews: {[k: string]: SwitchView} = {};
|
||||
|
||||
constructor(private _localization: NgLocalization) {}
|
||||
|
||||
@Input()
|
||||
set ngPlural(value: number) {
|
||||
this._switchValue = value;
|
||||
this._updateView();
|
||||
}
|
||||
|
||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||
|
||||
/** @internal */
|
||||
_updateView(): void {
|
||||
this._clearViews();
|
||||
|
||||
var key =
|
||||
getPluralCategory(this._switchValue, Object.keys(this._caseViews), this._localization);
|
||||
this._activateView(this._caseViews[key]);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_clearViews() {
|
||||
if (isPresent(this._activeView)) this._activeView.destroy();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateView(view: SwitchView) {
|
||||
if (!isPresent(view)) return;
|
||||
this._activeView = view;
|
||||
this._activeView.create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngPluralCase]'})
|
||||
export class NgPluralCase {
|
||||
constructor(
|
||||
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
|
||||
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
|
||||
ngPlural.addCase(value, new SwitchView(viewContainer, template));
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
||||
*
|
||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
||||
*
|
||||
* ```
|
||||
* import {Component} from '@angular/core';
|
||||
* import {NgStyle} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ngStyle-example',
|
||||
* template: `
|
||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
||||
* Change style of this text!
|
||||
* </h1>
|
||||
*
|
||||
* <hr>
|
||||
*
|
||||
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
|
||||
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
|
||||
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
|
||||
* `,
|
||||
* directives: [NgStyle]
|
||||
* })
|
||||
* export class NgStyleExample {
|
||||
* style = 'normal';
|
||||
* weight = 'normal';
|
||||
* size = '20px';
|
||||
*
|
||||
* changeStyle($event: any) {
|
||||
* this.style = $event.target.checked ? 'italic' : 'normal';
|
||||
* }
|
||||
*
|
||||
* changeWeight($event: any) {
|
||||
* this.weight = $event.target.checked ? 'bold' : 'normal';
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
||||
* based on the `style` property's value changes.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({selector: '[ngStyle]'})
|
||||
export class NgStyle implements DoCheck {
|
||||
/** @internal */
|
||||
_ngStyle: {[key: string]: string};
|
||||
/** @internal */
|
||||
_differ: KeyValueDiffer;
|
||||
|
||||
constructor(
|
||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||
|
||||
@Input()
|
||||
set ngStyle(v: {[key: string]: string}) {
|
||||
this._ngStyle = v;
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._ngStyle).create(null);
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._ngStyle);
|
||||
if (isPresent(changes)) {
|
||||
this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: any): void {
|
||||
changes.forEachRemovedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, null); });
|
||||
changes.forEachAddedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem(
|
||||
(record: KeyValueChangeRecord) => { this._setStyle(record.key, record.currentValue); });
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
const nameParts = name.split('.');
|
||||
const nameToSet = nameParts[0];
|
||||
const valToSet = isPresent(val) && nameParts.length === 2 ? `${val}${nameParts[1]}` : val;
|
||||
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, nameToSet, valToSet);
|
||||
}
|
||||
}
|
@ -1,235 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {isBlank, isPresent, normalizeBlank} from '../facade/lang';
|
||||
|
||||
const _CASE_DEFAULT = new Object();
|
||||
|
||||
// TODO: remove when fully deprecated
|
||||
let _warned: boolean = false;
|
||||
|
||||
export class SwitchView {
|
||||
constructor(
|
||||
private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef<Object>) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
|
||||
destroy(): void { this._viewContainerRef.clear(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
||||
*
|
||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
||||
* preserved at the location as specified in the template.
|
||||
*
|
||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
||||
* (where you place the directive with a switch expression on the
|
||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
||||
* place a `[ngSwitchCase]` attribute per element.
|
||||
*
|
||||
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
|
||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
|
||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'app',
|
||||
* template: `
|
||||
* <p>Value = {{value}}</p>
|
||||
* <button (click)="inc()">Increment</button>
|
||||
*
|
||||
* <div [ngSwitch]="value">
|
||||
* <p *ngSwitchCase="'init'">increment to start</p>
|
||||
* <p *ngSwitchCase="0">0, increment again</p>
|
||||
* <p *ngSwitchCase="1">1, increment again</p>
|
||||
* <p *ngSwitchCase="2">2, stop incrementing</p>
|
||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
||||
* </div>
|
||||
*
|
||||
* <!-- alternate syntax -->
|
||||
*
|
||||
* <p [ngSwitch]="value">
|
||||
* <template ngSwitchCase="init">increment to start</template>
|
||||
* <template [ngSwitchCase]="0">0, increment again</template>
|
||||
* <template [ngSwitchCase]="1">1, increment again</template>
|
||||
* <template [ngSwitchCase]="2">2, stop incrementing</template>
|
||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
||||
* </p>
|
||||
* `,
|
||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
||||
* })
|
||||
* export class App {
|
||||
* value = 'init';
|
||||
*
|
||||
* inc() {
|
||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* bootstrap(App).catch(err => console.error(err));
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngSwitch]'})
|
||||
export class NgSwitch {
|
||||
private _switchValue: any;
|
||||
private _useDefault: boolean = false;
|
||||
private _valueViews = new Map<any, SwitchView[]>();
|
||||
private _activeViews: SwitchView[] = [];
|
||||
|
||||
@Input()
|
||||
set ngSwitch(value: any) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
// Add the ViewContainers matching the value (with a fallback to default)
|
||||
this._useDefault = false;
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
this._useDefault = true;
|
||||
views = normalizeBlank(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
this._activateViews(views);
|
||||
|
||||
this._switchValue = value;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_onCaseValueChanged(oldCase: any, newCase: any, view: SwitchView): void {
|
||||
this._deregisterView(oldCase, view);
|
||||
this._registerView(newCase, view);
|
||||
|
||||
if (oldCase === this._switchValue) {
|
||||
view.destroy();
|
||||
ListWrapper.remove(this._activeViews, view);
|
||||
} else if (newCase === this._switchValue) {
|
||||
if (this._useDefault) {
|
||||
this._useDefault = false;
|
||||
this._emptyAllActiveViews();
|
||||
}
|
||||
view.create();
|
||||
this._activeViews.push(view);
|
||||
}
|
||||
|
||||
// Switch to default when there is no more active ViewContainers
|
||||
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||
this._useDefault = true;
|
||||
this._activateViews(this._valueViews.get(_CASE_DEFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_emptyAllActiveViews(): void {
|
||||
var activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].destroy();
|
||||
}
|
||||
this._activeViews = [];
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_activateViews(views: SwitchView[]): void {
|
||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
||||
if (isPresent(views)) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
}
|
||||
this._activeViews = views;
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_registerView(value: any, view: SwitchView): void {
|
||||
var views = this._valueViews.get(value);
|
||||
if (isBlank(views)) {
|
||||
views = [];
|
||||
this._valueViews.set(value, views);
|
||||
}
|
||||
views.push(view);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_deregisterView(value: any, view: SwitchView): void {
|
||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||
if (value === _CASE_DEFAULT) return;
|
||||
var views = this._valueViews.get(value);
|
||||
if (views.length == 1) {
|
||||
this._valueViews.delete(value);
|
||||
} else {
|
||||
ListWrapper.remove(views, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
|
||||
* enclosing switch expression.
|
||||
*
|
||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchCase],[ngSwitchWhen]'})
|
||||
export class NgSwitchCase {
|
||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||
/** @internal */
|
||||
_value: any = _CASE_DEFAULT;
|
||||
/** @internal */
|
||||
_view: SwitchView;
|
||||
private _switch: NgSwitch;
|
||||
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() ngSwitch: NgSwitch) {
|
||||
this._switch = ngSwitch;
|
||||
this._view = new SwitchView(viewContainer, templateRef);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchCase(value: any) {
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set ngSwitchWhen(value: any) {
|
||||
if (!_warned) {
|
||||
_warned = true;
|
||||
console.warn('*ngSwitchWhen is deprecated and will be removed. Use *ngSwitchCase instead');
|
||||
}
|
||||
this._switch._onCaseValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default case statements are displayed when no match expression matches the switch expression
|
||||
* value.
|
||||
*
|
||||
* See {@link NgSwitch} for more details and example.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngSwitchDefault]'})
|
||||
export class NgSwitchDefault {
|
||||
constructor(
|
||||
viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
|
||||
@Host() sswitch: NgSwitch) {
|
||||
sswitch._registerView(_CASE_DEFAULT, new SwitchView(viewContainer, templateRef));
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||
* available within the `TemplateRef`.
|
||||
*
|
||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* ```
|
||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||
* [ngOutletContext]="objectExpression">
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: '[ngTemplateOutlet]'})
|
||||
export class NgTemplateOutlet implements OnChanges {
|
||||
private _viewRef: EmbeddedViewRef<any>;
|
||||
private _context: Object;
|
||||
private _templateRef: TemplateRef<any>;
|
||||
|
||||
constructor(private _viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
@Input()
|
||||
set ngOutletContext(context: Object) { this._context = context; }
|
||||
|
||||
@Input()
|
||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||
|
||||
ngOnChanges() {
|
||||
if (this._viewRef) {
|
||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||
}
|
||||
|
||||
if (this._templateRef) {
|
||||
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../facade/src
|
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
* @description
|
||||
* This module is used for handling user input, by defining and building a {@link ControlGroup} that
|
||||
* consists of
|
||||
* {@link Control} objects, and mapping them onto the DOM. {@link Control} objects can then be used
|
||||
* to read information
|
||||
* from the form DOM elements.
|
||||
*
|
||||
* Forms providers are not included in default providers; you must import these providers
|
||||
* explicitly.
|
||||
*/
|
||||
import {NgModule, Type} from '@angular/core';
|
||||
|
||||
import {FORM_DIRECTIVES} from './forms-deprecated/directives';
|
||||
import {RadioControlRegistry} from './forms-deprecated/directives/radio_control_value_accessor';
|
||||
import {FormBuilder} from './forms-deprecated/form_builder';
|
||||
|
||||
export {FORM_DIRECTIVES, RadioButtonState} from './forms-deprecated/directives';
|
||||
export {AbstractControlDirective} from './forms-deprecated/directives/abstract_control_directive';
|
||||
export {CheckboxControlValueAccessor} from './forms-deprecated/directives/checkbox_value_accessor';
|
||||
export {ControlContainer} from './forms-deprecated/directives/control_container';
|
||||
export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms-deprecated/directives/control_value_accessor';
|
||||
export {DefaultValueAccessor} from './forms-deprecated/directives/default_value_accessor';
|
||||
export {Form} from './forms-deprecated/directives/form_interface';
|
||||
export {NgControl} from './forms-deprecated/directives/ng_control';
|
||||
export {NgControlGroup} from './forms-deprecated/directives/ng_control_group';
|
||||
export {NgControlName} from './forms-deprecated/directives/ng_control_name';
|
||||
export {NgControlStatus} from './forms-deprecated/directives/ng_control_status';
|
||||
export {NgForm} from './forms-deprecated/directives/ng_form';
|
||||
export {NgFormControl} from './forms-deprecated/directives/ng_form_control';
|
||||
export {NgFormModel} from './forms-deprecated/directives/ng_form_model';
|
||||
export {NgModel} from './forms-deprecated/directives/ng_model';
|
||||
export {NgSelectOption, SelectControlValueAccessor} from './forms-deprecated/directives/select_control_value_accessor';
|
||||
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms-deprecated/directives/validators';
|
||||
export {FormBuilder} from './forms-deprecated/form_builder';
|
||||
export {AbstractControl, Control, ControlArray, ControlGroup} from './forms-deprecated/model';
|
||||
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms-deprecated/validators';
|
||||
|
||||
|
||||
/**
|
||||
* Shorthand set of providers used for building Angular forms.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* bootstrap(MyApp, [FORM_PROVIDERS]);
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const FORM_PROVIDERS: Type[] = [FormBuilder, RadioControlRegistry];
|
||||
|
||||
|
||||
/**
|
||||
* The ng module for the deprecated forms API.
|
||||
* @deprecated
|
||||
*/
|
||||
@NgModule({
|
||||
providers: [
|
||||
FORM_PROVIDERS,
|
||||
],
|
||||
declarations: FORM_DIRECTIVES,
|
||||
exports: FORM_DIRECTIVES
|
||||
})
|
||||
export class DeprecatedFormsModule {
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
import {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||
import {NgControlGroup} from './directives/ng_control_group';
|
||||
import {NgControlName} from './directives/ng_control_name';
|
||||
import {NgControlStatus} from './directives/ng_control_status';
|
||||
import {NgForm} from './directives/ng_form';
|
||||
import {NgFormControl} from './directives/ng_form_control';
|
||||
import {NgFormModel} from './directives/ng_form_model';
|
||||
import {NgModel} from './directives/ng_model';
|
||||
import {NumberValueAccessor} from './directives/number_value_accessor';
|
||||
import {RadioControlValueAccessor} from './directives/radio_control_value_accessor';
|
||||
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||
import {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
||||
|
||||
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||
export {DefaultValueAccessor} from './directives/default_value_accessor';
|
||||
export {NgControl} from './directives/ng_control';
|
||||
export {NgControlGroup} from './directives/ng_control_group';
|
||||
export {NgControlName} from './directives/ng_control_name';
|
||||
export {NgControlStatus} from './directives/ng_control_status';
|
||||
export {NgForm} from './directives/ng_form';
|
||||
export {NgFormControl} from './directives/ng_form_control';
|
||||
export {NgFormModel} from './directives/ng_form_model';
|
||||
export {NgModel} from './directives/ng_model';
|
||||
export {NumberValueAccessor} from './directives/number_value_accessor';
|
||||
export {RadioButtonState, RadioControlValueAccessor} from './directives/radio_control_value_accessor';
|
||||
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||
export {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||
export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* A list of all the form directives used as part of a `@Component` annotation.
|
||||
*
|
||||
* This is a shorthand for importing them each individually.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* })
|
||||
* class MyApp {}
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
export const FORM_DIRECTIVES: Type[] = [
|
||||
NgControlName,
|
||||
NgControlGroup,
|
||||
|
||||
NgFormControl,
|
||||
NgModel,
|
||||
NgFormModel,
|
||||
NgForm,
|
||||
|
||||
NgSelectOption,
|
||||
NgSelectMultipleOption,
|
||||
DefaultValueAccessor,
|
||||
NumberValueAccessor,
|
||||
CheckboxControlValueAccessor,
|
||||
SelectControlValueAccessor,
|
||||
SelectMultipleControlValueAccessor,
|
||||
RadioControlValueAccessor,
|
||||
NgControlStatus,
|
||||
|
||||
RequiredValidator,
|
||||
MinLengthValidator,
|
||||
MaxLengthValidator,
|
||||
PatternValidator,
|
||||
];
|
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
import {AbstractControl} from '../model';
|
||||
|
||||
|
||||
/**
|
||||
* Base class for control directives.
|
||||
*
|
||||
* Only used internally in the forms module.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class AbstractControlDirective {
|
||||
get control(): AbstractControl { return unimplemented(); }
|
||||
|
||||
get value(): any { return isPresent(this.control) ? this.control.value : null; }
|
||||
|
||||
get valid(): boolean { return isPresent(this.control) ? this.control.valid : null; }
|
||||
|
||||
get errors(): {[key: string]: any} {
|
||||
return isPresent(this.control) ? this.control.errors : null;
|
||||
}
|
||||
|
||||
get pristine(): boolean { return isPresent(this.control) ? this.control.pristine : null; }
|
||||
|
||||
get dirty(): boolean { return isPresent(this.control) ? this.control.dirty : null; }
|
||||
|
||||
get touched(): boolean { return isPresent(this.control) ? this.control.touched : null; }
|
||||
|
||||
get untouched(): boolean { return isPresent(this.control) ? this.control.untouched : null; }
|
||||
|
||||
get path(): string[] { return null; }
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const CHECKBOX_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CheckboxControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a checkbox input element.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="checkbox" ngControl="rememberLogin">
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=checkbox][ngControl],input[type=checkbox][ngFormControl],input[type=checkbox][ngModel]',
|
||||
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
|
||||
providers: [CHECKBOX_VALUE_ACCESSOR]
|
||||
})
|
||||
export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
|
||||
}
|
||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {Form} from './form_interface';
|
||||
|
||||
|
||||
/**
|
||||
* A directive that contains multiple {@link NgControl}s.
|
||||
*
|
||||
* Only used by the forms module.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export class ControlContainer extends AbstractControlDirective {
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Get the form to which this container belongs.
|
||||
*/
|
||||
get formDirective(): Form { return null; }
|
||||
|
||||
/**
|
||||
* Get the path to this container.
|
||||
*/
|
||||
get path(): string[] { return null; }
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {OpaqueToken} from '@angular/core';
|
||||
|
||||
/**
|
||||
* A bridge between a control and a native element.
|
||||
*
|
||||
* A `ControlValueAccessor` abstracts the operations of writing a new value to a
|
||||
* DOM element representing an input control.
|
||||
*
|
||||
* Please see {@link DefaultValueAccessor} for more information.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface ControlValueAccessor {
|
||||
/**
|
||||
* Write a new value to the element.
|
||||
*/
|
||||
writeValue(obj: any): void;
|
||||
|
||||
/**
|
||||
* Set the function to be called when the control receives a change event.
|
||||
*/
|
||||
registerOnChange(fn: any): void;
|
||||
|
||||
/**
|
||||
* Set the function to be called when the control receives a touch event.
|
||||
*/
|
||||
registerOnTouched(fn: any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to provide a {@link ControlValueAccessor} for form controls.
|
||||
*
|
||||
* See {@link DefaultValueAccessor} for how to implement one.
|
||||
* @experimental
|
||||
*/
|
||||
export const NG_VALUE_ACCESSOR: OpaqueToken = new OpaqueToken('NgValueAccessor');
|
@ -1,54 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {isBlank} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const DEFAULT_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DefaultValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The default accessor for writing a value and listening to changes that is used by the
|
||||
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="text" ngControl="searchQuery">
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input:not([type=checkbox])[ngControl],textarea[ngControl],input:not([type=checkbox])[ngFormControl],textarea[ngFormControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
|
||||
// TODO: vsavkin replace the above selector with the one below it once
|
||||
// https://github.com/angular/angular/issues/3011 is implemented
|
||||
// selector: '[ngControl],[ngModel],[ngFormControl]',
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
providers: [DEFAULT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
var normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Control, ControlGroup} from '../model';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
|
||||
|
||||
/**
|
||||
* An interface that {@link NgFormModel} and {@link NgForm} implement.
|
||||
*
|
||||
* Only used by the forms module.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface Form {
|
||||
/**
|
||||
* Add a control to this form.
|
||||
*/
|
||||
addControl(dir: NgControl): void;
|
||||
|
||||
/**
|
||||
* Remove a control from this form.
|
||||
*/
|
||||
removeControl(dir: NgControl): void;
|
||||
|
||||
/**
|
||||
* Look up the {@link Control} associated with a particular {@link NgControl}.
|
||||
*/
|
||||
getControl(dir: NgControl): Control;
|
||||
|
||||
/**
|
||||
* Add a group of controls to this form.
|
||||
*/
|
||||
addControlGroup(dir: NgControlGroup): void;
|
||||
|
||||
/**
|
||||
* Remove a group of controls from this form.
|
||||
*/
|
||||
removeControlGroup(dir: NgControlGroup): void;
|
||||
|
||||
/**
|
||||
* Look up the {@link ControlGroup} associated with a particular {@link NgControlGroup}.
|
||||
*/
|
||||
getControlGroup(dir: NgControlGroup): ControlGroup;
|
||||
|
||||
/**
|
||||
* Update the model for a particular control with a new value.
|
||||
*/
|
||||
updateModel(dir: NgControl, value: any): void;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {unimplemented} from '../../facade/exceptions';
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
/**
|
||||
* A base class that all control directive extend.
|
||||
* It binds a {@link Control} object to a DOM element.
|
||||
*
|
||||
* Used internally by Angular forms.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export abstract class NgControl extends AbstractControlDirective {
|
||||
name: string = null;
|
||||
valueAccessor: ControlValueAccessor = null;
|
||||
|
||||
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
|
||||
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }
|
||||
|
||||
abstract viewToModelUpdate(newValue: any): void;
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Inject, OnDestroy, OnInit, Optional, Self, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {ControlGroup} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {Form} from './form_interface';
|
||||
import {composeAsyncValidators, composeValidators, controlPath} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const controlGroupProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgControlGroup)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control group to a DOM element.
|
||||
*
|
||||
* This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/7EJ11uGeaggViYM6T5nq?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>Angular Control & ControlGroup Example</h2>
|
||||
* <form #f="ngForm">
|
||||
* <div ngControlGroup="name" #cgName="ngForm">
|
||||
* <h3>Enter your name:</h3>
|
||||
* <p>First: <input ngControl="first" required></p>
|
||||
* <p>Middle: <input ngControl="middle"></p>
|
||||
* <p>Last: <input ngControl="last" required></p>
|
||||
* </div>
|
||||
* <h3>Name value:</h3>
|
||||
* <pre>{{valueOf(cgName)}}</pre>
|
||||
* <p>Name is {{cgName?.control?.valid ? "valid" : "invalid"}}</p>
|
||||
* <h3>What's your favorite food?</h3>
|
||||
* <p><input ngControl="food"></p>
|
||||
* <h3>Form value</h3>
|
||||
* <pre>{{valueOf(f)}}</pre>
|
||||
* </form>
|
||||
* </div>
|
||||
* `
|
||||
* })
|
||||
* export class App {
|
||||
* valueOf(cg: NgControlGroup): string {
|
||||
* if (cg.control == null) {
|
||||
* return null;
|
||||
* }
|
||||
* return JSON.stringify(cg.control.value, null, 2);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This example declares a control group for a user's name. The value and validation state of
|
||||
* this group can be accessed separately from the overall form.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngControlGroup]',
|
||||
providers: [controlGroupProvider],
|
||||
inputs: ['name: ngControlGroup'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgControlGroup extends ControlContainer implements OnInit,
|
||||
OnDestroy {
|
||||
/** @internal */
|
||||
_parent: ControlContainer;
|
||||
|
||||
constructor(
|
||||
@Host() @SkipSelf() parent: ControlContainer,
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
|
||||
super();
|
||||
this._parent = parent;
|
||||
}
|
||||
|
||||
ngOnInit(): void { this.formDirective.addControlGroup(this); }
|
||||
|
||||
ngOnDestroy(): void { this.formDirective.removeControlGroup(this); }
|
||||
|
||||
/**
|
||||
* Get the {@link ControlGroup} backing this binding.
|
||||
*/
|
||||
get control(): ControlGroup { return this.formDirective.getControlGroup(this); }
|
||||
|
||||
/**
|
||||
* Get the path to this control group.
|
||||
*/
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
/**
|
||||
* Get the {@link Form} to which this group belongs.
|
||||
*/
|
||||
get formDirective(): Form { return this._parent.formDirective; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn { return composeAsyncValidators(this._asyncValidators); }
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Host, Inject, OnChanges, OnDestroy, Optional, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
export const controlNameBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgControlName)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and binds a control with a specified name to a DOM element.
|
||||
*
|
||||
* This directive can only be used as a child of {@link NgForm} or {@link NgFormModel}.
|
||||
|
||||
* ### Example
|
||||
*
|
||||
* In this example, we create the login and password controls.
|
||||
* We can work with each control separately: check its validity, get its value, listen to its
|
||||
* changes.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form #f="ngForm" (submit)='onLogIn(f.value)'>
|
||||
* Login <input type='text' ngControl='login' #l="ngForm">
|
||||
* <div *ngIf="!l.valid">Login is invalid</div>
|
||||
*
|
||||
* Password <input type='password' ngControl='password'>
|
||||
* <button type='submit'>Log in!</button>
|
||||
* </form>
|
||||
* `})
|
||||
* class LoginComp {
|
||||
* onLogIn(value): void {
|
||||
* // value === {login: 'some login', password: 'some password'}
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can also use ngModel to bind a domain model to the form.
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form (submit)='onLogIn()'>
|
||||
* Login <input type='text' ngControl='login' [(ngModel)]="credentials.login">
|
||||
* Password <input type='password' ngControl='password'
|
||||
* [(ngModel)]="credentials.password">
|
||||
* <button type='submit'>Log in!</button>
|
||||
* </form>
|
||||
* `})
|
||||
* class LoginComp {
|
||||
* credentials: {login:string, password:string};
|
||||
*
|
||||
* onLogIn(): void {
|
||||
* // this.credentials.login === "some login"
|
||||
* // this.credentials.password === "some password"
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngControl]',
|
||||
providers: [controlNameBinding],
|
||||
inputs: ['name: ngControl', 'model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgControlName extends NgControl implements OnChanges,
|
||||
OnDestroy {
|
||||
/** @internal */
|
||||
update = new EventEmitter();
|
||||
model: any;
|
||||
viewModel: any;
|
||||
private _added = false;
|
||||
|
||||
constructor(@Host() @SkipSelf() private _parent: ControlContainer,
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators:
|
||||
/* Array<Validator|Function> */ any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators:
|
||||
/* Array<Validator|Function> */ any[],
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||
valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this._added) {
|
||||
this.formDirective.addControl(this);
|
||||
this._added = true;
|
||||
}
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this.viewModel = this.model;
|
||||
this.formDirective.updateModel(this, this.model);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this.formDirective.removeControl(this); }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
get formDirective(): any { return this._parent.formDirective; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._asyncValidators);
|
||||
}
|
||||
|
||||
get control(): Control { return this.formDirective.getControl(this); }
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Self} from '@angular/core';
|
||||
|
||||
import {isPresent} from '../../facade/lang';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
|
||||
|
||||
/**
|
||||
* Directive automatically applied to Angular forms that sets CSS classes
|
||||
* based on control status (valid/invalid/dirty/etc).
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngControl],[ngModel],[ngFormControl]',
|
||||
host: {
|
||||
'[class.ng-untouched]': 'ngClassUntouched',
|
||||
'[class.ng-touched]': 'ngClassTouched',
|
||||
'[class.ng-pristine]': 'ngClassPristine',
|
||||
'[class.ng-dirty]': 'ngClassDirty',
|
||||
'[class.ng-valid]': 'ngClassValid',
|
||||
'[class.ng-invalid]': 'ngClassInvalid'
|
||||
}
|
||||
})
|
||||
export class NgControlStatus {
|
||||
private _cd: NgControl;
|
||||
|
||||
constructor(@Self() cd: NgControl) { this._cd = cd; }
|
||||
|
||||
get ngClassUntouched(): boolean {
|
||||
return isPresent(this._cd.control) ? this._cd.control.untouched : false;
|
||||
}
|
||||
get ngClassTouched(): boolean {
|
||||
return isPresent(this._cd.control) ? this._cd.control.touched : false;
|
||||
}
|
||||
get ngClassPristine(): boolean {
|
||||
return isPresent(this._cd.control) ? this._cd.control.pristine : false;
|
||||
}
|
||||
get ngClassDirty(): boolean {
|
||||
return isPresent(this._cd.control) ? this._cd.control.dirty : false;
|
||||
}
|
||||
get ngClassValid(): boolean {
|
||||
return isPresent(this._cd.control) ? this._cd.control.valid : false;
|
||||
}
|
||||
get ngClassInvalid(): boolean {
|
||||
return isPresent(this._cd.control) ? !this._cd.control.valid : false;
|
||||
}
|
||||
}
|
@ -1,202 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
import {AbstractControl, Control, ControlGroup} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {Form} from './form_interface';
|
||||
import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
|
||||
|
||||
export const formDirectiveProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgForm)
|
||||
};
|
||||
|
||||
let _formWarningDisplayed: boolean = false;
|
||||
|
||||
const resolvedPromise = Promise.resolve(null);
|
||||
|
||||
/**
|
||||
* If `NgForm` is bound in a component, `<form>` elements in that component will be
|
||||
* upgraded to use the Angular form system.
|
||||
*
|
||||
* ### Typical Use
|
||||
*
|
||||
* Include `FORM_DIRECTIVES` in the `directives` section of a {@link Component} annotation
|
||||
* to use `NgForm` and its associated controls.
|
||||
*
|
||||
* ### Structure
|
||||
*
|
||||
* An Angular form is a collection of `Control`s in some hierarchy.
|
||||
* `Control`s can be at the top level or can be organized in `ControlGroup`s
|
||||
* or `ControlArray`s. This hierarchy is reflected in the form's `value`, a
|
||||
* JSON object that mirrors the form structure.
|
||||
*
|
||||
* ### Submission
|
||||
*
|
||||
* The `ngSubmit` event signals when the user triggers a form submission.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ltdgYj4P0iY64AR71EpL?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: `
|
||||
* <div>
|
||||
* <p>Submit the form to see the data object Angular builds</p>
|
||||
* <h2>NgForm demo</h2>
|
||||
* <form #f="ngForm" (ngSubmit)="onSubmit(f.value)">
|
||||
* <h3>Control group: credentials</h3>
|
||||
* <div ngControlGroup="credentials">
|
||||
* <p>Login: <input type="text" ngControl="login"></p>
|
||||
* <p>Password: <input type="password" ngControl="password"></p>
|
||||
* </div>
|
||||
* <h3>Control group: person</h3>
|
||||
* <div ngControlGroup="person">
|
||||
* <p>First name: <input type="text" ngControl="firstName"></p>
|
||||
* <p>Last name: <input type="text" ngControl="lastName"></p>
|
||||
* </div>
|
||||
* <button type="submit">Submit Form</button>
|
||||
* <p>Form data submitted:</p>
|
||||
* </form>
|
||||
* <pre>{{data}}</pre>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
|
||||
* })
|
||||
* export class App {
|
||||
* constructor() {}
|
||||
*
|
||||
* data: string;
|
||||
*
|
||||
* onSubmit(data) {
|
||||
* this.data = JSON.stringify(data, null, 2);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
|
||||
@Directive({
|
||||
selector: 'form:not([ngNoForm]):not([ngFormModel]),ngForm,[ngForm]',
|
||||
providers: [formDirectiveProvider],
|
||||
host: {
|
||||
'(submit)': 'onSubmit()',
|
||||
},
|
||||
outputs: ['ngSubmit'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgForm extends ControlContainer implements Form {
|
||||
private _submitted: boolean = false;
|
||||
|
||||
form: ControlGroup;
|
||||
ngSubmit = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) {
|
||||
super();
|
||||
this._displayWarning();
|
||||
this.form = new ControlGroup(
|
||||
{}, null, composeValidators(validators), composeAsyncValidators(asyncValidators));
|
||||
}
|
||||
|
||||
private _displayWarning() {
|
||||
// TODO(kara): Update this when the new forms module becomes the default
|
||||
if (!_formWarningDisplayed) {
|
||||
_formWarningDisplayed = true;
|
||||
console.warn(`
|
||||
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
|
||||
will eventually be removed in favor of the new forms module. For more information, see:
|
||||
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
get submitted(): boolean { return this._submitted; }
|
||||
|
||||
get formDirective(): Form { return this; }
|
||||
|
||||
get control(): ControlGroup { return this.form; }
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get controls(): {[key: string]: AbstractControl} { return this.form.controls; }
|
||||
|
||||
addControl(dir: NgControl): void {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
var ctrl = new Control();
|
||||
setUpControl(ctrl, dir);
|
||||
container.registerControl(dir.name, ctrl);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
});
|
||||
}
|
||||
|
||||
getControl(dir: NgControl): Control { return <Control>this.form.find(dir.path); }
|
||||
|
||||
removeControl(dir: NgControl): void {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
if (isPresent(container)) {
|
||||
container.removeControl(dir.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addControlGroup(dir: NgControlGroup): void {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
var group = new ControlGroup({});
|
||||
setUpControlGroup(group, dir);
|
||||
container.registerControl(dir.name, group);
|
||||
group.updateValueAndValidity({emitEvent: false});
|
||||
});
|
||||
}
|
||||
|
||||
removeControlGroup(dir: NgControlGroup): void {
|
||||
resolvedPromise.then(() => {
|
||||
var container = this._findContainer(dir.path);
|
||||
if (isPresent(container)) {
|
||||
container.removeControl(dir.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getControlGroup(dir: NgControlGroup): ControlGroup {
|
||||
return <ControlGroup>this.form.find(dir.path);
|
||||
}
|
||||
|
||||
updateModel(dir: NgControl, value: any): void {
|
||||
resolvedPromise.then(() => {
|
||||
var ctrl = <Control>this.form.find(dir.path);
|
||||
ctrl.updateValue(value);
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(): boolean {
|
||||
this._submitted = true;
|
||||
this.ngSubmit.emit(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_findContainer(path: string[]): ControlGroup {
|
||||
path.pop();
|
||||
return ListWrapper.isEmpty(path) ? this.form : <ControlGroup>this.form.find(path);
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {StringMapWrapper} from '../../facade/collection';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const formControlBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgFormControl)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds an existing {@link Control} to a DOM element.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/jcQlZ2tTh22BZZ2ucNAT?p=preview))
|
||||
*
|
||||
* In this example, we bind the control to an input element. When the value of the input element
|
||||
* changes, the value of the control will reflect that change. Likewise, if the value of the
|
||||
* control changes, the input element reflects that change.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>NgFormControl Example</h2>
|
||||
* <form>
|
||||
* <p>Element with existing control: <input type="text"
|
||||
* [ngFormControl]="loginControl"></p>
|
||||
* <p>Value of existing control: {{loginControl.value}}</p>
|
||||
* </form>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
|
||||
* })
|
||||
* export class App {
|
||||
* loginControl: Control = new Control('');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ### ngModel
|
||||
*
|
||||
* We can also use `ngModel` to bind a domain model to the form.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/yHMLuHO7DNgT8XvtjTDH?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: "<input type='text' [ngFormControl]='loginControl' [(ngModel)]='login'>"
|
||||
* })
|
||||
* class LoginComp {
|
||||
* loginControl: Control = new Control('');
|
||||
* login:string;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngFormControl]',
|
||||
providers: [formControlBinding],
|
||||
inputs: ['form: ngFormControl', 'model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgFormControl extends NgControl implements OnChanges {
|
||||
form: Control;
|
||||
update = new EventEmitter();
|
||||
model: any;
|
||||
viewModel: any;
|
||||
|
||||
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) private _validators:
|
||||
/* Array<Validator|Function> */ any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators:
|
||||
/* Array<Validator|Function> */ any[],
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||
valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this._isControlChanged(changes)) {
|
||||
setUpControl(this.form, this);
|
||||
this.form.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this.form.updateValue(this.model);
|
||||
this.viewModel = this.model;
|
||||
}
|
||||
}
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._asyncValidators);
|
||||
}
|
||||
|
||||
get control(): Control { return this.form; }
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
private _isControlChanged(changes: {[key: string]: any}): boolean {
|
||||
return StringMapWrapper.contains(changes, 'form');
|
||||
}
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
|
||||
import {BaseException} from '../../facade/exceptions';
|
||||
import {isBlank} from '../../facade/lang';
|
||||
import {Control, ControlGroup} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../validators';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {Form} from './form_interface';
|
||||
import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {composeAsyncValidators, composeValidators, setUpControl, setUpControlGroup} from './shared';
|
||||
|
||||
export const formDirectiveProvider: any = {
|
||||
provide: ControlContainer,
|
||||
useExisting: forwardRef(() => NgFormModel)
|
||||
};
|
||||
|
||||
let _formModelWarningDisplayed: boolean = false;
|
||||
|
||||
/**
|
||||
* Binds an existing control group to a DOM element.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/jqrVirudY8anJxTMUjTP?p=preview))
|
||||
*
|
||||
* In this example, we bind the control group to the form element, and we bind the login and
|
||||
* password controls to the login and password elements.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* template: `
|
||||
* <div>
|
||||
* <h2>NgFormModel Example</h2>
|
||||
* <form [ngFormModel]="loginForm">
|
||||
* <p>Login: <input type="text" ngControl="login"></p>
|
||||
* <p>Password: <input type="password" ngControl="password"></p>
|
||||
* </form>
|
||||
* <p>Value:</p>
|
||||
* <pre>{{value}}</pre>
|
||||
* </div>
|
||||
* `,
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* })
|
||||
* export class App {
|
||||
* loginForm: ControlGroup;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginForm = new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* password: new Control("")
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* get value(): string {
|
||||
* return JSON.stringify(this.loginForm.value, null, 2);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can also use ngModel to bind a domain model to the form.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: "login-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `
|
||||
* <form [ngFormModel]='loginForm'>
|
||||
* Login <input type='text' ngControl='login' [(ngModel)]='credentials.login'>
|
||||
* Password <input type='password' ngControl='password'
|
||||
* [(ngModel)]='credentials.password'>
|
||||
* <button (click)="onLogin()">Login</button>
|
||||
* </form>`
|
||||
* })
|
||||
* class LoginComp {
|
||||
* credentials: {login: string, password: string};
|
||||
* loginForm: ControlGroup;
|
||||
*
|
||||
* constructor() {
|
||||
* this.loginForm = new ControlGroup({
|
||||
* login: new Control(""),
|
||||
* password: new Control("")
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* onLogin(): void {
|
||||
* // this.credentials.login === 'some login'
|
||||
* // this.credentials.password === 'some password'
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
|
||||
@Directive({
|
||||
selector: '[ngFormModel]',
|
||||
providers: [formDirectiveProvider],
|
||||
inputs: ['form: ngFormModel'],
|
||||
host: {'(submit)': 'onSubmit()'},
|
||||
outputs: ['ngSubmit'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgFormModel extends ControlContainer implements Form,
|
||||
OnChanges {
|
||||
private _submitted: boolean = false;
|
||||
|
||||
form: ControlGroup = null;
|
||||
directives: NgControl[] = [];
|
||||
ngSubmit = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) {
|
||||
super();
|
||||
this._displayWarning();
|
||||
}
|
||||
|
||||
private _displayWarning() {
|
||||
// TODO(kara): Update this when the new forms module becomes the default
|
||||
if (!_formModelWarningDisplayed) {
|
||||
_formModelWarningDisplayed = true;
|
||||
console.warn(`
|
||||
*It looks like you're using the old forms module. This will be opt-in in the next RC, and
|
||||
will eventually be removed in favor of the new forms module. For more information, see:
|
||||
https://docs.google.com/document/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/preview
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this._checkFormPresent();
|
||||
if (StringMapWrapper.contains(changes, 'form')) {
|
||||
var sync = composeValidators(this._validators);
|
||||
this.form.validator = Validators.compose([this.form.validator, sync]);
|
||||
|
||||
var async = composeAsyncValidators(this._asyncValidators);
|
||||
this.form.asyncValidator = Validators.composeAsync([this.form.asyncValidator, async]);
|
||||
|
||||
this.form.updateValueAndValidity({onlySelf: true, emitEvent: false});
|
||||
}
|
||||
|
||||
this._updateDomValue();
|
||||
}
|
||||
|
||||
get submitted(): boolean { return this._submitted; }
|
||||
|
||||
get formDirective(): Form { return this; }
|
||||
|
||||
get control(): ControlGroup { return this.form; }
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
addControl(dir: NgControl): void {
|
||||
var ctrl: any = this.form.find(dir.path);
|
||||
setUpControl(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
this.directives.push(dir);
|
||||
}
|
||||
|
||||
getControl(dir: NgControl): Control { return <Control>this.form.find(dir.path); }
|
||||
|
||||
removeControl(dir: NgControl): void { ListWrapper.remove(this.directives, dir); }
|
||||
|
||||
addControlGroup(dir: NgControlGroup) {
|
||||
var ctrl: any = this.form.find(dir.path);
|
||||
setUpControlGroup(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
removeControlGroup(dir: NgControlGroup) {}
|
||||
|
||||
getControlGroup(dir: NgControlGroup): ControlGroup {
|
||||
return <ControlGroup>this.form.find(dir.path);
|
||||
}
|
||||
|
||||
updateModel(dir: NgControl, value: any): void {
|
||||
var ctrl = <Control>this.form.find(dir.path);
|
||||
ctrl.updateValue(value);
|
||||
}
|
||||
|
||||
onSubmit(): boolean {
|
||||
this._submitted = true;
|
||||
this.ngSubmit.emit(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_updateDomValue() {
|
||||
this.directives.forEach(dir => {
|
||||
var ctrl: any = this.form.find(dir.path);
|
||||
dir.valueAccessor.writeValue(ctrl.value);
|
||||
});
|
||||
}
|
||||
|
||||
private _checkFormPresent() {
|
||||
if (isBlank(this.form)) {
|
||||
throw new BaseException(
|
||||
`ngFormModel expects a form. Please pass one in. Example: <form [ngFormModel]="myCoolForm">`);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, Inject, OnChanges, Optional, Self, SimpleChanges, forwardRef} from '@angular/core';
|
||||
|
||||
import {EventEmitter} from '../../facade/async';
|
||||
import {Control} from '../model';
|
||||
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
export const formControlBinding: any = {
|
||||
provide: NgControl,
|
||||
useExisting: forwardRef(() => NgModel)
|
||||
};
|
||||
|
||||
/**
|
||||
* Binds a domain model to a form control.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* `ngModel` binds an existing domain model to a form control. For a
|
||||
* two-way binding, use `[(ngModel)]` to ensure the model updates in
|
||||
* both directions.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/R3UX5qDaUqFO2VYR0UzH?p=preview))
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: "search-comp",
|
||||
* directives: [FORM_DIRECTIVES],
|
||||
* template: `<input type='text' [(ngModel)]="searchQuery">`
|
||||
* })
|
||||
* class SearchComp {
|
||||
* searchQuery: string;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngModel]:not([ngControl]):not([ngFormControl])',
|
||||
providers: [formControlBinding],
|
||||
inputs: ['model: ngModel'],
|
||||
outputs: ['update: ngModelChange'],
|
||||
exportAs: 'ngForm'
|
||||
})
|
||||
export class NgModel extends NgControl implements OnChanges {
|
||||
/** @internal */
|
||||
_control = new Control();
|
||||
/** @internal */
|
||||
_added = false;
|
||||
update = new EventEmitter();
|
||||
model: any;
|
||||
viewModel: any;
|
||||
|
||||
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[],
|
||||
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[],
|
||||
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
|
||||
valueAccessors: ControlValueAccessor[]) {
|
||||
super();
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this._added) {
|
||||
setUpControl(this._control, this);
|
||||
this._control.updateValueAndValidity({emitEvent: false});
|
||||
this._added = true;
|
||||
}
|
||||
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
this._control.updateValue(this.model);
|
||||
this.viewModel = this.model;
|
||||
}
|
||||
}
|
||||
|
||||
get control(): Control { return this._control; }
|
||||
|
||||
get path(): string[] { return []; }
|
||||
|
||||
get validator(): ValidatorFn { return composeValidators(this._validators); }
|
||||
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._asyncValidators);
|
||||
}
|
||||
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AbstractControl} from '../model';
|
||||
|
||||
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
|
||||
|
||||
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <ValidatorFn>validator;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
|
||||
if ((<Validator>validator).validate !== undefined) {
|
||||
return (c: AbstractControl) => (<Validator>validator).validate(c);
|
||||
} else {
|
||||
return <AsyncValidatorFn>validator;
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {NumberWrapper, isBlank} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const NUMBER_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => NumberValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* The accessor for writing a number value and listening to changes that is used by the
|
||||
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* <input type="number" [(ngModel)]="age">
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=number][ngControl],input[type=number][ngFormControl],input[type=number][ngModel]',
|
||||
host: {
|
||||
'(change)': 'onChange($event.target.value)',
|
||||
'(input)': 'onChange($event.target.value)',
|
||||
'(blur)': 'onTouched()'
|
||||
},
|
||||
providers: [NUMBER_VALUE_ACCESSOR]
|
||||
})
|
||||
export class NumberValueAccessor implements ControlValueAccessor {
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: number): void {
|
||||
// The value needs to be normalized for IE9, otherwise it is set to 'null' when null
|
||||
const normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number) => void): void {
|
||||
this.onChange = (value) => { fn(value == '' ? null : NumberWrapper.parseFloat(value)); };
|
||||
}
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {ListWrapper} from '../../facade/collection';
|
||||
import {isPresent} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
|
||||
export const RADIO_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => RadioControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal class used by Angular to uncheck radio buttons with the matching name.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RadioControlRegistry {
|
||||
private _accessors: any[] = [];
|
||||
|
||||
add(control: NgControl, accessor: RadioControlValueAccessor) {
|
||||
this._accessors.push([control, accessor]);
|
||||
}
|
||||
|
||||
remove(accessor: RadioControlValueAccessor) {
|
||||
var indexToRemove = -1;
|
||||
for (var i = 0; i < this._accessors.length; ++i) {
|
||||
if (this._accessors[i][1] === accessor) {
|
||||
indexToRemove = i;
|
||||
}
|
||||
}
|
||||
ListWrapper.removeAt(this._accessors, indexToRemove);
|
||||
}
|
||||
|
||||
select(accessor: RadioControlValueAccessor) {
|
||||
this._accessors.forEach((c) => {
|
||||
if (this._isSameGroup(c, accessor) && c[1] !== accessor) {
|
||||
c[1].fireUncheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _isSameGroup(
|
||||
controlPair: [NgControl, RadioControlValueAccessor], accessor: RadioControlValueAccessor) {
|
||||
return controlPair[0].control.root === accessor._control.control.root &&
|
||||
controlPair[1].name === accessor.name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value provided by the forms API for radio buttons.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export class RadioButtonState {
|
||||
constructor(public checked: boolean, public value: string) {}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The accessor for writing a radio control value and listening to changes that is used by the
|
||||
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* @Component({
|
||||
* template: `
|
||||
* <input type="radio" name="food" [(ngModel)]="foodChicken">
|
||||
* <input type="radio" name="food" [(ngModel)]="foodFish">
|
||||
* `
|
||||
* })
|
||||
* class FoodCmp {
|
||||
* foodChicken = new RadioButtonState(true, "chicken");
|
||||
* foodFish = new RadioButtonState(false, "fish");
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'input[type=radio][ngControl],input[type=radio][ngFormControl],input[type=radio][ngModel]',
|
||||
host: {'(change)': 'onChange()', '(blur)': 'onTouched()'},
|
||||
providers: [RADIO_VALUE_ACCESSOR]
|
||||
})
|
||||
export class RadioControlValueAccessor implements ControlValueAccessor,
|
||||
OnDestroy, OnInit {
|
||||
/** @internal */
|
||||
_state: RadioButtonState;
|
||||
/** @internal */
|
||||
_control: NgControl;
|
||||
@Input() name: string;
|
||||
/** @internal */
|
||||
_fn: Function;
|
||||
onChange = () => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(
|
||||
private _renderer: Renderer, private _elementRef: ElementRef,
|
||||
private _registry: RadioControlRegistry, private _injector: Injector) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._control = this._injector.get(NgControl);
|
||||
this._registry.add(this._control, this);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void { this._registry.remove(this); }
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._state = value;
|
||||
if (isPresent(value) && value.checked) {
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => {}): void {
|
||||
this._fn = fn;
|
||||
this.onChange = () => {
|
||||
fn(new RadioButtonState(true, this._state.value));
|
||||
this._registry.select(this);
|
||||
};
|
||||
}
|
||||
|
||||
fireUncheck(): void { this._fn(new RadioButtonState(false, this._state.value)); }
|
||||
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core';
|
||||
|
||||
import {MapWrapper} from '../../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const SELECT_VALUE_ACCESSOR: any = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (!isPrimitive(value)) value = 'Object';
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(':')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*
|
||||
* Note: We have to listen to the 'change' event because 'input' events aren't fired
|
||||
* for selects in Firefox and IE:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1024350
|
||||
* https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4660045/
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector:
|
||||
'select:not([multiple])[ngControl],select:not([multiple])[ngFormControl],select:not([multiple])[ngModel]',
|
||||
host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, any> = new Map<string, any>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
var valueString = _buildValueString(this._getOptionId(value), value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', valueString);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (valueString: string) => {
|
||||
this.value = valueString;
|
||||
fn(this._getOptionValue(valueString));
|
||||
};
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(): string { return (this._idCounter++).toString(); }
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id), value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let value = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(value) ? value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <select ngControl="city">
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectOption implements OnDestroy {
|
||||
id: string;
|
||||
|
||||
constructor(
|
||||
private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectControlValueAccessor) {
|
||||
if (isPresent(this._select)) this.id = this._select._registerOption();
|
||||
}
|
||||
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._select._optionMap.set(this.id, value);
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
this._setElementValue(value);
|
||||
if (isPresent(this._select)) this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,185 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Host, Input, OnDestroy, OpaqueToken, Optional, Renderer, Type, forwardRef} from '@angular/core';
|
||||
|
||||
import {MapWrapper} from '../../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../../facade/lang';
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor';
|
||||
|
||||
export const SELECT_MULTIPLE_VALUE_ACCESSOR = {
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectMultipleControlValueAccessor),
|
||||
multi: true
|
||||
};
|
||||
|
||||
function _buildValueString(id: string, value: any): string {
|
||||
if (isBlank(id)) return `${value}`;
|
||||
if (isString(value)) value = `'${value}'`;
|
||||
if (!isPrimitive(value)) value = 'Object';
|
||||
return StringWrapper.slice(`${id}: ${value}`, 0, 50);
|
||||
}
|
||||
|
||||
function _extractId(valueString: string): string {
|
||||
return valueString.split(':')[0];
|
||||
}
|
||||
|
||||
/** Mock interface for HTML Options */
|
||||
interface HTMLOption {
|
||||
value: string;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
/** Mock interface for HTMLCollection */
|
||||
abstract class HTMLCollection {
|
||||
length: number;
|
||||
abstract item(_: number): HTMLOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* The accessor for writing a value and listening to changes on a select element.
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[multiple][ngControl],select[multiple][ngFormControl],select[multiple][ngModel]',
|
||||
host: {'(change)': 'onChange($event.target)', '(blur)': 'onTouched()'},
|
||||
providers: [SELECT_MULTIPLE_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectMultipleControlValueAccessor implements ControlValueAccessor {
|
||||
value: any;
|
||||
/** @internal */
|
||||
_optionMap: Map<string, NgSelectMultipleOption> = new Map<string, NgSelectMultipleOption>();
|
||||
/** @internal */
|
||||
_idCounter: number = 0;
|
||||
|
||||
onChange = (_: any) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor() {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
if (value == null) return;
|
||||
let values: Array<any> = <Array<any>>value;
|
||||
// convert values to ids
|
||||
let ids = values.map((v) => this._getOptionId(v));
|
||||
this._optionMap.forEach((opt, o) => { opt._setSelected(ids.indexOf(o.toString()) > -1); });
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => any): void {
|
||||
this.onChange = (_: any) => {
|
||||
let selected: Array<any> = [];
|
||||
if (_.hasOwnProperty('selectedOptions')) {
|
||||
let options: HTMLCollection = _.selectedOptions;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let opt: any = options.item(i);
|
||||
let val: any = this._getOptionValue(opt.value);
|
||||
selected.push(val);
|
||||
}
|
||||
}
|
||||
// Degrade on IE
|
||||
else {
|
||||
let options: HTMLCollection = <HTMLCollection>_.options;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
let opt: HTMLOption = options.item(i);
|
||||
if (opt.selected) {
|
||||
let val: any = this._getOptionValue(opt.value);
|
||||
selected.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn(selected);
|
||||
};
|
||||
}
|
||||
registerOnTouched(fn: () => any): void { this.onTouched = fn; }
|
||||
|
||||
/** @internal */
|
||||
_registerOption(value: NgSelectMultipleOption): string {
|
||||
let id: string = (this._idCounter++).toString();
|
||||
this._optionMap.set(id, value);
|
||||
return id;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionId(value: any): string {
|
||||
for (let id of MapWrapper.keys(this._optionMap)) {
|
||||
if (looseIdentical(this._optionMap.get(id)._value, value)) return id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_getOptionValue(valueString: string): any {
|
||||
let opt = this._optionMap.get(_extractId(valueString));
|
||||
return isPresent(opt) ? opt._value : valueString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks `<option>` as dynamic, so Angular can be notified when options change.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <select multiple ngControl="city">
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*/
|
||||
@Directive({selector: 'option'})
|
||||
export class NgSelectMultipleOption implements OnDestroy {
|
||||
id: string;
|
||||
/** @internal */
|
||||
_value: any;
|
||||
|
||||
constructor(
|
||||
private _element: ElementRef, private _renderer: Renderer,
|
||||
@Optional() @Host() private _select: SelectMultipleControlValueAccessor) {
|
||||
if (isPresent(this._select)) {
|
||||
this.id = this._select._registerOption(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Input('ngValue')
|
||||
set ngValue(value: any) {
|
||||
if (this._select == null) return;
|
||||
this._value = value;
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
|
||||
@Input('value')
|
||||
set value(value: any) {
|
||||
if (isPresent(this._select)) {
|
||||
this._value = value;
|
||||
this._setElementValue(_buildValueString(this.id, value));
|
||||
this._select.writeValue(this._select.value);
|
||||
} else {
|
||||
this._setElementValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setElementValue(value: string): void {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setSelected(selected: boolean) {
|
||||
this._renderer.setElementProperty(this._element.nativeElement, 'selected', selected);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (isPresent(this._select)) {
|
||||
this._select._optionMap.delete(this.id);
|
||||
this._select.writeValue(this._select.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SELECT_DIRECTIVES = [SelectMultipleControlValueAccessor, NgSelectMultipleOption];
|
@ -1,128 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
|
||||
import {BaseException} from '../../facade/exceptions';
|
||||
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang';
|
||||
import {Control, ControlGroup} from '../model';
|
||||
import {Validators} from '../validators';
|
||||
|
||||
import {AbstractControlDirective} from './abstract_control_directive';
|
||||
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
||||
import {ControlContainer} from './control_container';
|
||||
import {ControlValueAccessor} from './control_value_accessor';
|
||||
import {DefaultValueAccessor} from './default_value_accessor';
|
||||
import {NgControl} from './ng_control';
|
||||
import {NgControlGroup} from './ng_control_group';
|
||||
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
|
||||
import {NumberValueAccessor} from './number_value_accessor';
|
||||
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
||||
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
||||
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
|
||||
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
||||
|
||||
|
||||
export function controlPath(name: string, parent: ControlContainer): string[] {
|
||||
var p = ListWrapper.clone(parent.path);
|
||||
p.push(name);
|
||||
return p;
|
||||
}
|
||||
|
||||
export function setUpControl(control: Control, dir: NgControl): void {
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
|
||||
if (isBlank(dir.valueAccessor)) _throwError(dir, 'No value accessor for form control with');
|
||||
|
||||
control.validator = Validators.compose([control.validator, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||
dir.valueAccessor.writeValue(control.value);
|
||||
|
||||
// view -> model
|
||||
dir.valueAccessor.registerOnChange((newValue: any) => {
|
||||
dir.viewToModelUpdate(newValue);
|
||||
control.updateValue(newValue, {emitModelToViewChange: false});
|
||||
control.markAsDirty();
|
||||
});
|
||||
|
||||
// model -> view
|
||||
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
|
||||
|
||||
// touched
|
||||
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
|
||||
}
|
||||
|
||||
export function setUpControlGroup(control: ControlGroup, dir: NgControlGroup) {
|
||||
if (isBlank(control)) _throwError(dir, 'Cannot find control with');
|
||||
control.validator = Validators.compose([control.validator, dir.validator]);
|
||||
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
||||
}
|
||||
|
||||
function _throwError(dir: AbstractControlDirective, message: string): void {
|
||||
let messageEnd: string;
|
||||
if (dir.path.length > 1) {
|
||||
messageEnd = `path: '${dir.path.join(' -> ')}'`;
|
||||
} else if (dir.path[0]) {
|
||||
messageEnd = `name: '${dir.path}'`;
|
||||
} else {
|
||||
messageEnd = 'unspecified name';
|
||||
}
|
||||
throw new BaseException(`${message} ${messageEnd}`);
|
||||
}
|
||||
|
||||
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
|
||||
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
|
||||
}
|
||||
|
||||
export function composeAsyncValidators(validators: /* Array<Validator|Function> */ any[]):
|
||||
AsyncValidatorFn {
|
||||
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
|
||||
null;
|
||||
}
|
||||
|
||||
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
|
||||
if (!StringMapWrapper.contains(changes, 'model')) return false;
|
||||
var change = changes['model'];
|
||||
|
||||
if (change.isFirstChange()) return true;
|
||||
return !looseIdentical(viewModel, change.currentValue);
|
||||
}
|
||||
|
||||
// TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented
|
||||
export function selectValueAccessor(
|
||||
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
|
||||
if (isBlank(valueAccessors)) return null;
|
||||
|
||||
var defaultAccessor: ControlValueAccessor;
|
||||
var builtinAccessor: ControlValueAccessor;
|
||||
var customAccessor: ControlValueAccessor;
|
||||
valueAccessors.forEach((v: ControlValueAccessor) => {
|
||||
if (hasConstructor(v, DefaultValueAccessor)) {
|
||||
defaultAccessor = v;
|
||||
|
||||
} else if (
|
||||
hasConstructor(v, CheckboxControlValueAccessor) || hasConstructor(v, NumberValueAccessor) ||
|
||||
hasConstructor(v, SelectControlValueAccessor) ||
|
||||
hasConstructor(v, SelectMultipleControlValueAccessor) ||
|
||||
hasConstructor(v, RadioControlValueAccessor)) {
|
||||
if (isPresent(builtinAccessor))
|
||||
_throwError(dir, 'More than one built-in value accessor matches form control with');
|
||||
builtinAccessor = v;
|
||||
|
||||
} else {
|
||||
if (isPresent(customAccessor))
|
||||
_throwError(dir, 'More than one custom value accessor matches form control with');
|
||||
customAccessor = v;
|
||||
}
|
||||
});
|
||||
|
||||
if (isPresent(customAccessor)) return customAccessor;
|
||||
if (isPresent(builtinAccessor)) return builtinAccessor;
|
||||
if (isPresent(defaultAccessor)) return defaultAccessor;
|
||||
|
||||
_throwError(dir, 'No valid value accessor for form control with');
|
||||
return null;
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Attribute, Directive, forwardRef} from '@angular/core';
|
||||
|
||||
import {NumberWrapper} from '../../facade/lang';
|
||||
import {AbstractControl} from '../model';
|
||||
import {NG_VALIDATORS, Validators} from '../validators';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface that can be implemented by classes that can act as validators.
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* ```typescript
|
||||
* @Directive({
|
||||
* selector: '[custom-validator]',
|
||||
* providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}]
|
||||
* })
|
||||
* class CustomValidatorDirective implements Validator {
|
||||
* validate(c: Control): {[key: string]: any} {
|
||||
* return {"custom": true};
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export interface Validator { validate(c: AbstractControl): {[key: string]: any}; }
|
||||
|
||||
export const REQUIRED = Validators.required;
|
||||
|
||||
export const REQUIRED_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useValue: REQUIRED,
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A Directive that adds the `required` validator to any controls marked with the
|
||||
* `required` attribute, via the {@link NG_VALIDATORS} binding.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input ngControl="fullName" required>
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[required][ngControl],[required][ngFormControl],[required][ngModel]',
|
||||
providers: [REQUIRED_VALIDATOR]
|
||||
})
|
||||
export class RequiredValidator {
|
||||
}
|
||||
|
||||
export interface ValidatorFn { (c: AbstractControl): {[key: string]: any}; }
|
||||
export interface AsyncValidatorFn {
|
||||
(c: AbstractControl): any /*Promise<{[key: string]: any}>|Observable<{[key: string]: any}>*/;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provivder which adds {@link MinLengthValidator} to {@link NG_VALIDATORS}.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='min'}
|
||||
*/
|
||||
export const MIN_LENGTH_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MinLengthValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A directive which installs the {@link MinLengthValidator} for any `ngControl`,
|
||||
* `ngFormControl`, or control with `ngModel` that also has a `minlength` attribute.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[minlength][ngControl],[minlength][ngFormControl],[minlength][ngModel]',
|
||||
providers: [MIN_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MinLengthValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute('minlength') minLength: string) {
|
||||
this._validator = Validators.minLength(NumberWrapper.parseInt(minLength, 10));
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider which adds {@link MaxLengthValidator} to {@link NG_VALIDATORS}.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* {@example common/forms/ts/validators/validators.ts region='max'}
|
||||
*/
|
||||
export const MAX_LENGTH_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MaxLengthValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A directive which installs the {@link MaxLengthValidator} for any `ngControl, `ngFormControl`,
|
||||
* or control with `ngModel` that also has a `maxlength` attribute.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[maxlength][ngControl],[maxlength][ngFormControl],[maxlength][ngModel]',
|
||||
providers: [MAX_LENGTH_VALIDATOR]
|
||||
})
|
||||
export class MaxLengthValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute('maxlength') maxLength: string) {
|
||||
this._validator = Validators.maxLength(NumberWrapper.parseInt(maxLength, 10));
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
||||
|
||||
|
||||
export const PATTERN_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => PatternValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A Directive that adds the `pattern` validator to any controls marked with the
|
||||
* `pattern` attribute, via the {@link NG_VALIDATORS} binding. Uses attribute value
|
||||
* as the regex to validate Control value against. Follows pattern attribute
|
||||
* semantics; i.e. regex must match entire Control value.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input [ngControl]="fullName" pattern="[a-zA-Z ]*">
|
||||
* ```
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[pattern][ngControl],[pattern][ngFormControl],[pattern][ngModel]',
|
||||
providers: [PATTERN_VALIDATOR]
|
||||
})
|
||||
export class PatternValidator implements Validator {
|
||||
private _validator: ValidatorFn;
|
||||
|
||||
constructor(@Attribute('pattern') pattern: string) {
|
||||
this._validator = Validators.pattern(pattern);
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} { return this._validator(c); }
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {StringMapWrapper} from '../facade/collection';
|
||||
import {isArray, isPresent} from '../facade/lang';
|
||||
|
||||
import {AsyncValidatorFn, ValidatorFn} from './directives/validators';
|
||||
import {AbstractControl, Control, ControlArray, ControlGroup} from './model';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a form object from a user-specified configuration.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/ENgZo8EuIECZNensZCVr?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* selector: 'my-app',
|
||||
* viewProviders: [FORM_BINDINGS]
|
||||
* template: `
|
||||
* <form [ngFormModel]="loginForm">
|
||||
* <p>Login <input ngControl="login"></p>
|
||||
* <div ngControlGroup="passwordRetry">
|
||||
* <p>Password <input type="password" ngControl="password"></p>
|
||||
* <p>Confirm password <input type="password" ngControl="passwordConfirmation"></p>
|
||||
* </div>
|
||||
* </form>
|
||||
* <h3>Form value:</h3>
|
||||
* <pre>{{value}}</pre>
|
||||
* `,
|
||||
* directives: [FORM_DIRECTIVES]
|
||||
* })
|
||||
* export class App {
|
||||
* loginForm: ControlGroup;
|
||||
*
|
||||
* constructor(builder: FormBuilder) {
|
||||
* this.loginForm = builder.group({
|
||||
* login: ["", Validators.required],
|
||||
* passwordRetry: builder.group({
|
||||
* password: ["", Validators.required],
|
||||
* passwordConfirmation: ["", Validators.required, asyncValidator]
|
||||
* })
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* get value(): string {
|
||||
* return JSON.stringify(this.loginForm.value, null, 2);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Injectable()
|
||||
export class FormBuilder {
|
||||
/**
|
||||
* Construct a new {@link ControlGroup} with the given map of configuration.
|
||||
* Valid keys for the `extra` parameter map are `optionals` and `validator`.
|
||||
*
|
||||
* See the {@link ControlGroup} constructor for more details.
|
||||
*/
|
||||
group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any} = null): ControlGroup {
|
||||
var controls = this._reduceControls(controlsConfig);
|
||||
var optionals = <{[key: string]: boolean}>(
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, 'optionals') : null);
|
||||
var validator: ValidatorFn = isPresent(extra) ? StringMapWrapper.get(extra, 'validator') : null;
|
||||
var asyncValidator: AsyncValidatorFn =
|
||||
isPresent(extra) ? StringMapWrapper.get(extra, 'asyncValidator') : null;
|
||||
return new ControlGroup(controls, optionals, validator, asyncValidator);
|
||||
}
|
||||
/**
|
||||
* Construct a new {@link Control} with the given `value`,`validator`, and `asyncValidator`.
|
||||
*/
|
||||
control(value: Object, validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null):
|
||||
Control {
|
||||
return new Control(value, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an array of {@link Control}s from the given `controlsConfig` array of
|
||||
* configuration, with the given optional `validator` and `asyncValidator`.
|
||||
*/
|
||||
array(
|
||||
controlsConfig: any[], validator: ValidatorFn = null,
|
||||
asyncValidator: AsyncValidatorFn = null): ControlArray {
|
||||
var controls = controlsConfig.map(c => this._createControl(c));
|
||||
return new ControlArray(controls, validator, asyncValidator);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_reduceControls(controlsConfig: {[k: string]: any}): {[key: string]: AbstractControl} {
|
||||
var controls: {[key: string]: AbstractControl} = {};
|
||||
StringMapWrapper.forEach(controlsConfig, (controlConfig: any, controlName: string) => {
|
||||
controls[controlName] = this._createControl(controlConfig);
|
||||
});
|
||||
return controls;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createControl(controlConfig: any): AbstractControl {
|
||||
if (controlConfig instanceof Control || controlConfig instanceof ControlGroup ||
|
||||
controlConfig instanceof ControlArray) {
|
||||
return controlConfig;
|
||||
|
||||
} else if (isArray(controlConfig)) {
|
||||
var value = controlConfig[0];
|
||||
var validator: ValidatorFn = controlConfig.length > 1 ? controlConfig[1] : null;
|
||||
var asyncValidator: AsyncValidatorFn = controlConfig.length > 2 ? controlConfig[2] : null;
|
||||
return this.control(value, validator, asyncValidator);
|
||||
|
||||
} else {
|
||||
return this.control(controlConfig);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user