Compare commits

..

15 Commits

Author SHA1 Message Date
2800683e64 improved fix for non-TS syntax 2015-03-23 16:03:22 -07:00
a1158a06bd Separate task for running ts2dart over all angular sources 2015-03-23 14:39:32 -07:00
35a376acdb Merge branch 'ts2dart' of github.com:alexeagle/angular into ts2dart
Conflicts:
	gulpfile.js
2015-03-23 13:36:39 -07:00
b8f0209ba5 Fix sequencing of ts2dart tasks. 2015-03-23 13:34:24 -07:00
9386bb1cb1 Use the newer dart formatter, which gives better errors 2015-03-20 12:22:12 -07:00
01592ff773 transpile the js files in one package, and fail the build if it fails 2015-03-20 12:22:12 -07:00
2a5d0dd59a Explicitly include gulp-ts2dart in package.json. 2015-03-20 12:22:11 -07:00
e07a2d10c7 Transpile .js files, experimentally. 2015-03-20 12:22:11 -07:00
7234d368e3 First hook in the angular build to run ts2dart.
This expects files we are interested in to have the '.ts' extension.
2015-03-20 12:22:11 -07:00
cdc83730df Use the newer dart formatter, which gives better errors 2015-03-20 12:08:32 -07:00
0f30ce6e92 Merge branch 'ts2dart' of github.com:angular/angular into ts2dart
Conflicts:
	gulpfile.js
2015-03-19 11:01:44 -07:00
fb6b8b22a1 transpile the js files in one package, and fail the build if it fails 2015-03-19 11:00:45 -07:00
853c24f4d4 Transpile .js files, experimentally. 2015-03-18 18:51:43 -07:00
e37f58a228 Merge branch 'master' into ts2dart 2015-03-18 16:01:24 -07:00
e569e0b1ee First hook in the angular build to run ts2dart.
This expects files we are interested in to have the '.ts' extension.
2015-03-18 14:42:18 -07:00
1819 changed files with 48129 additions and 191005 deletions

View File

@ -1,3 +0,0 @@
{
"directory" : "bower_components"
}

View File

@ -1,3 +0,0 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 100

3
.gitattributes vendored
View File

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

View File

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

View File

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

@ -1,18 +1,11 @@
.DS_STORE
# Dont 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/

1
.nvmrc
View File

@ -1 +0,0 @@
5.4.1

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
# Developer Tools for Angular 2
- [JavaScript](TOOLS_JS.md)
- [Dart](TOOLS_DART.md)

View File

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

View File

@ -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 &rarr; *tbosch*, *crossj*
* `comp: build/dev-productivity` -- build process, e.g. CLI and related tasks &rarr; *iminar*, *caitp*
* `comp: build/pipeline` -- build pipeline, e.g. ts2dart &rarr; *mprobst*, *alexeagle*
* `comp: core` -- general core Angular issues, not related to a sub-category (see below) &rarr;
*mhevery*
* `comp: core/animations` -- animations framework &rarr; *matsko*
* `comp: core/change_detection` -- change detection &rarr; *vsavkin*
* `comp: core/di` -- dependency injection &rarr; *vicb*, *rkirov*
* `comp: core/directives` -- directives
* `comp: core/forms` -- forms &rarr; *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 &rarr; *kegluneq*, *jakemac*
* `comp: data-access` -- &rarr; *jeffbcross*
* `comp: docs` -- API docs and doc generation &rarr; *naomiblack*, *petebacondarwin*
* `comp: material-components` -- Angular Material components built in Angular 2 &rarr; *jelbourn*
* `comp: router` -- Component Router &rarr; *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.

View File

@ -1,6 +1,7 @@
{
"name": "angular2",
"version": "0.0.0",
"dependencies": {
"polymer": "Polymer/polymer"
"polymer": "dart-lang/polymer_js#0.8.0-preview"
}
}
}

View File

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

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

View File

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

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

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

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

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

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

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

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

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

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

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

View 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
}]);
});
});
});

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

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

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

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

View File

@ -0,0 +1,3 @@
module.exports = function TraceurParser() {
return System.get('transpiler/src/parser').Parser;
};

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

View 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' +
'*/'
);
});
});

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

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

View File

@ -0,0 +1,3 @@
module.exports = function modules() {
return {};
};

View File

@ -0,0 +1,3 @@
module.exports = function traceurOptions() {
return System.get(System.map.traceur + '/src/Options.js').options;
};

View 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 %}

View 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 %}

View File

@ -0,0 +1,3 @@
angular.module('{$ doc.moduleName $}', [])
.value('{$ doc.serviceName $}', {$ doc.value | json $});

View File

@ -0,0 +1,5 @@
{% extends 'layout/base.template.html' %}
{% block body %}
{$ doc.description | marked $}
{% endblock %}

View File

@ -0,0 +1 @@
{% block body %}{% endblock %}

View 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 %}

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

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

View File

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

View File

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

@ -0,0 +1 @@
karma-js.conf.js

View File

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

View File

@ -1,17 +0,0 @@
{
"name": "@angular/common",
"version": "0.0.0-PLACEHOLDER",
"description": "",
"main": "index.js",
"jsnext:main": "esm/index.js",
"typings": "index.d.ts",
"author": "angular",
"license": "MIT",
"peerDependencies": {
"@angular/core": "0.0.0-PLACEHOLDER"
},
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.git"
}
}

View File

@ -1,17 +0,0 @@
export default {
entry: '../../../dist/packages-dist/common/esm/index.js',
dest: '../../../dist/packages-dist/common/esm/common.umd.js',
format: 'umd',
moduleName: 'ng.common',
globals: {
'@angular/core': 'ng.core',
'rxjs/Subject': 'Rx',
'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update.
'rxjs/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/Observable': 'Rx'
},
plugins: [
// nodeResolve({ jsnext: true, main: true }),
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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