Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fef0cfc837 | |||
| 5de845506b | |||
| 38ea542662 | |||
| 2db66f5b69 | |||
| 55fe6d6331 | |||
| f4c08fee85 | |||
| 71bc451f95 | |||
| 7e4e696ec3 | |||
| 6da44a4410 | |||
| a81195c6ca | |||
| 102a3201c1 | |||
| 59244a7776 | |||
| 293cb1fc5d | |||
| 81395ac298 | |||
| ba66c4f3cc | |||
| 4ea57e7e96 | |||
| 6e420ff28d | |||
| 5393814756 | |||
| fab59e7515 | |||
| 553c252d5c | |||
| e145a8df72 | |||
| b49d0cc6e7 | |||
| 97b171ecb2 | |||
| 245de33c00 | |||
| 8d4d437e8c | |||
| 7287dbf71d | |||
| da88449f25 | |||
| eaf1f8546d | |||
| 20d926cc45 | |||
| 9ae9c1c0da | |||
| 8a5972461c | |||
| 35d635cbcb | |||
| db2a4c04d6 | |||
| 0d62257c5f | |||
| f911b84aef | |||
| 32c09c1d19 | |||
| 26064375ca | |||
| 7a294369ab | |||
| fbab287ea2 | |||
| 254dcee93d | |||
| 69e5c369d7 | |||
| c0ccbb7b6a | |||
| b87713687e | |||
| 950ffb5a84 | |||
| 259003056d | |||
| fedc4194d9 | |||
| 16862705e1 | |||
| c694c96e4c | |||
| 5ac8a6e74a | |||
| 0e5106ec2c | |||
| 9091b77fe6 | |||
| 849f998be3 | |||
| 8bc77b68b3 | |||
| 95bd046881 | |||
| bfce9126e1 | |||
| bf2264e2aa | |||
| 5ced7b20ff | |||
| 114cf9e418 | |||
| 1b61b73f28 | |||
| d657d63a21 | |||
| c5bb3a9098 | |||
| 77edce5ded | |||
| f37cd4e4ef | |||
| 2acc91098b | |||
| 5b1c89931e | |||
| 99a2ad381b | |||
| 300263abec | |||
| b0bcf18892 | |||
| ea3b6310c9 | |||
| 764a3beecc | |||
| a603330e7a | |||
| 1f842b1c63 | |||
| 3b09f1bf4e | |||
| 561ddc9ff1 | |||
| 512ecf8f1b | |||
| 6636f1d03f | |||
| 7cccb8b777 | |||
| a275d539f9 | |||
| 17fa2468bc | |||
| fb2ae5660e | |||
| 2c4b3573cc | |||
| b2363e3102 | |||
| edfca4c769 | |||
| a9b5a1087d | |||
| 87b18b9fbe | |||
| ad128e09ff | |||
| 187b4adbd2 | |||
| 375c47d0c0 | |||
| 8fd47a1cd5 | |||
| e48c28fe92 | |||
| 10d3e1e447 | |||
| 93d1c95c61 | |||
| 01a34f513b | |||
| 916e53ce14 | |||
| b91b3119a4 | |||
| 0d60f8d367 | |||
| ca69dc6f17 | |||
| 5b7f1bcc00 | |||
| 9ab594a66c | |||
| 6c82a497c6 | |||
| df804406fb | |||
| dadce485a7 | |||
| 344cdce136 | |||
| f0347d5efa | |||
| 1c27e5fc37 | |||
| 5fb298b90f | |||
| 483325a7b5 | |||
| a86cb7d794 | |||
| c7e60153a5 | |||
| c0416866f5 | |||
| 8ba452544e | |||
| 8f7f0d26ed | |||
| 98d825e10d | |||
| 57b0d91fd8 | |||
| 9226b36572 | |||
| 39635fd0d7 | |||
| cad307fa1f | |||
| 78bc84c497 | |||
| 1f2750136e | |||
| 5b93e5fcfc | |||
| 770fd5a917 | |||
| 4b29186696 | |||
| 7b5be9ee29 | |||
| eeb261bcd5 | |||
| ef88a8a020 | |||
| 86ab885fd9 | |||
| de07ddeac6 | |||
| dc149de936 | |||
| 320f6d1214 | |||
| 1517d6d2f2 | |||
| 6bb17af2e3 | |||
| 505ead7e58 | |||
| 1da4e89385 | |||
| 83f37d78ba | |||
| d4ac25496a | |||
| e84da2283c | |||
| 3dd9572754 | |||
| 922cb7e42f | |||
| 103cb513d9 | |||
| c24e4e4ed5 | |||
| 1b46a7dcdf | |||
| 8d28d65b36 | |||
| fbb125a3af | |||
| ca7336391a | |||
| 771bccc35c | |||
| c794b96bdc | |||
| f108a2a994 | |||
| 7cbf61cabb | |||
| dfdb72559f | |||
| d69793d93c | |||
| b068c8b605 | |||
| ec16352579 | |||
| aa4ba23350 | |||
| 25e639b474 | |||
| ee8e4a946e | |||
| a41a2a1d2c | |||
| eadd8d08d3 | |||
| 602a1142e8 | |||
| 0b7fef3d94 | |||
| 809d47ec77 | |||
| f3444d495d | |||
| 612c882b83 | |||
| f2a6be3129 | |||
| 465663ed77 | |||
| 1102ffaaf8 | |||
| 98f6a82390 | |||
| a43c6e1828 | |||
| 0db301f863 | |||
| 8e6d3875c6 | |||
| b9d77d46ff | |||
| 96f94d4347 | |||
| 63831f118f | |||
| ebe280eede | |||
| 95d6cdc7c7 | |||
| 9223215add | |||
| 309cfd109f | |||
| cfc6175aab | |||
| dc39f368c3 | |||
| c0f3400573 | |||
| 822d7e5ae9 | |||
| 5874db84ab | |||
| e9e8d49216 | |||
| 550fc21ce5 | |||
| a8aba8957b | |||
| ca0ac64997 | |||
| 7678501bc9 | |||
| dec5eb6e83 | |||
| 2eff326781 | |||
| 50ce5746a7 | |||
| 1537f80267 | |||
| 021d3aa21a | |||
| e8c8c5459e | |||
| 1c20aed318 | |||
| 4c4d24a338 | |||
| 8c7b9b8de4 | |||
| f39ac571c4 | |||
| 84f36701bc | |||
| 6d4ce240de | |||
| 229a155aef | |||
| 73250089cd | |||
| a72bc4e69f | |||
| 0812061274 |
+2
-1
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireRightStickedOperators": ["!"]
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"requireLeftStickedOperators": [","],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
|
||||
+114
-15
@@ -1,3 +1,102 @@
|
||||
<a name="1.2.16"></a>
|
||||
# 1.2.16 badger-enumeration (2014-04-03)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure the CSS driver properly works with SVG elements
|
||||
([38ea5426](https://github.com/angular/angular.js/commit/38ea542662b2b74703d583e3a637d65369fc26eb),
|
||||
[#6030](https://github.com/angular/angular.js/issues/6030))
|
||||
- prevent cancellation timestamp from being too far in the future
|
||||
([35d635cb](https://github.com/angular/angular.js/commit/35d635cbcbdc20f304781655f3563111afa6567f),
|
||||
[#6748](https://github.com/angular/angular.js/issues/6748))
|
||||
- run CSS animations before JS animations to avoid style inheritance
|
||||
([0e5106ec](https://github.com/angular/angular.js/commit/0e5106ec2ccc8596c589b89074d3b27d27bf395a),
|
||||
[#6675](https://github.com/angular/angular.js/issues/6675))
|
||||
- **$parse:** mark constant unary minus expressions as constant
|
||||
([6e420ff2](https://github.com/angular/angular.js/commit/6e420ff28d9b3e76ac2c3598bf3797540ef8a1d3),
|
||||
[#6932](https://github.com/angular/angular.js/issues/6932))
|
||||
- **Scope:**
|
||||
- revert the __proto__ cleanup as that could cause regressions
|
||||
([2db66f5b](https://github.com/angular/angular.js/commit/2db66f5b695a06cff62a52e55e55d1a0a25eec2f))
|
||||
- more scope clean up on $destroy to minimize leaks
|
||||
([7e4e696e](https://github.com/angular/angular.js/commit/7e4e696ec3adf9d6fc77a7aa7e0909a9675fd43a),
|
||||
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856), [#6968](https://github.com/angular/angular.js/issues/6968))
|
||||
- aggressively clean up scope on $destroy to minimize leaks
|
||||
([8d4d437e](https://github.com/angular/angular.js/commit/8d4d437e8cd8d7cebab5d9ae5c8bcfeef2118ce9),
|
||||
[#6794](https://github.com/angular/angular.js/issues/6794), [#6856](https://github.com/angular/angular.js/issues/6856))
|
||||
- **filter.ngdoc:** Check if "input" variable is defined
|
||||
([a275d539](https://github.com/angular/angular.js/commit/a275d539f9631d6ec64d03814b3b09420e6cf1ee),
|
||||
[#6819](https://github.com/angular/angular.js/issues/6819))
|
||||
- **input:** don't perform HTML5 validation on updated model-value
|
||||
([b2363e31](https://github.com/angular/angular.js/commit/b2363e31023df8240113f68b4e01d942f8009b60),
|
||||
[#6796](https://github.com/angular/angular.js/issues/6796), [#6806](https://github.com/angular/angular.js/issues/6806))
|
||||
- **ngClass:** handle ngClassOdd/Even affecting the same classes
|
||||
([55fe6d63](https://github.com/angular/angular.js/commit/55fe6d6331e501325c2658df8995dcc083fc4ffb),
|
||||
[#5271](https://github.com/angular/angular.js/issues/5271))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$http:** add xhr statusText to completeRequest callback
|
||||
([32c09c1d](https://github.com/angular/angular.js/commit/32c09c1d195fcb98f6e29fc7e554a867f4762301),
|
||||
[#2335](https://github.com/angular/angular.js/issues/2335), [#2665](https://github.com/angular/angular.js/issues/2665), [#6713](https://github.com/angular/angular.js/issues/6713))
|
||||
|
||||
|
||||
<a name="v1.2.15"></a>
|
||||
# v1.2.15 beer-underestimating (2014-03-21)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$$RAFProvider:** check for webkitCancelRequestAnimationFrame
|
||||
([e84da228](https://github.com/angular/angular.js/commit/e84da2283c4e195be557f7b06c8783fe502acbbb),
|
||||
[#6526](https://github.com/angular/angular.js/issues/6526))
|
||||
- **$$rAF:** always fallback to a $timeout incase native rAF isn't supported
|
||||
([ee8e4a94](https://github.com/angular/angular.js/commit/ee8e4a946ed8f943e00846b88d8d51c0b2cd1fab),
|
||||
[#6654](https://github.com/angular/angular.js/issues/6654))
|
||||
- **$compile:** support templates with thead and tfoot root elements
|
||||
([ca0ac649](https://github.com/angular/angular.js/commit/ca0ac649971ae4fb50419b38f92a98d2226eb696),
|
||||
[#6289](https://github.com/angular/angular.js/issues/6289))
|
||||
- **$http:**
|
||||
- allow sending Blob data using $http
|
||||
([fbb125a3](https://github.com/angular/angular.js/commit/fbb125a3af164e52af2f8119175b04cbbed2f331),
|
||||
[#5012](https://github.com/angular/angular.js/issues/5012))
|
||||
- don't covert 0 status codes to 404 for non-file protocols
|
||||
([f108a2a9](https://github.com/angular/angular.js/commit/f108a2a994149ecc011e29f327bcb8e11adf72d9),
|
||||
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
|
||||
- **$rootScope:**
|
||||
- ng-repeat can't handle NaN values. #4605
|
||||
([e48c28fe](https://github.com/angular/angular.js/commit/e48c28fe9292efe7af6205b2be116d2350990c73),
|
||||
[#4605](https://github.com/angular/angular.js/issues/4605))
|
||||
- $watchCollection should call listener with oldValue
|
||||
([3dd95727](https://github.com/angular/angular.js/commit/3dd9572754c7bafec30dd625f5c611346959c969),
|
||||
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
|
||||
- **angular.bootstrap:** only allow angular to load once
|
||||
([0d60f8d3](https://github.com/angular/angular.js/commit/0d60f8d367e38224696749b0f7de04bd60649815),
|
||||
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
|
||||
- **jqLite:** traverse `host` property for DocumentFragment in inheritedData()
|
||||
([98d825e1](https://github.com/angular/angular.js/commit/98d825e10d3bf76f47e69abba857a8933c8cb7d9),
|
||||
[#6637](https://github.com/angular/angular.js/issues/6637))
|
||||
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
|
||||
([a41a2a1d](https://github.com/angular/angular.js/commit/a41a2a1d2ce20f86ac2709592e4ada527160e580),
|
||||
[#6539](https://github.com/angular/angular.js/issues/6539))
|
||||
- **ngCookie:** convert non-string values to string
|
||||
([93d1c95c](https://github.com/angular/angular.js/commit/93d1c95c61dbfa565333bb64527a103242175af7),
|
||||
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
|
||||
- **ngTouch:** update workaround for desktop Webkit quirk
|
||||
([01a34f51](https://github.com/angular/angular.js/commit/01a34f513bb567ed6d4c81d00d7c2a777c0dae01),
|
||||
[#6302](https://github.com/angular/angular.js/issues/6302))
|
||||
- **orderBy:** support string predicates containing non-ident characters
|
||||
([10d3e1e4](https://github.com/angular/angular.js/commit/10d3e1e4472ab9f5cf4418b6438ec2e0f2b0b288),
|
||||
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
|
||||
- **select:** avoid checking option element selected properties in render
|
||||
([dc149de9](https://github.com/angular/angular.js/commit/dc149de9364c66b988f169f67cad39577ba43434),
|
||||
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994), [#6769](https://github.com/angular/angular.js/issues/6769))
|
||||
|
||||
|
||||
|
||||
<a name="1.2.14"></a>
|
||||
# 1.2.14 feisty-cryokinesis (2014-03-01)
|
||||
|
||||
@@ -275,26 +374,26 @@ The animation mock module has been renamed from `mock.animate` to `ngAnimateMock
|
||||
## Breaking Changes
|
||||
|
||||
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
|
||||
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
|
||||
|
||||
To migrate your code, follow the example below:
|
||||
To migrate your code, follow the example below:
|
||||
|
||||
Before:
|
||||
Before:
|
||||
|
||||
// Will apply to POST, PUT and PATCH methods
|
||||
$httpProvider.defaults.headers.post = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
// Will apply to POST, PUT and PATCH methods
|
||||
$httpProvider.defaults.headers.post = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
After:
|
||||
After:
|
||||
|
||||
// POST, PUT and PATCH default headers must be specified separately,
|
||||
// as they do not share data.
|
||||
$httpProvider.defaults.headers.post =
|
||||
$httpProvider.defaults.headers.put =
|
||||
$httpProviders.defaults.headers.patch = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
// POST, PUT and PATCH default headers must be specified separately,
|
||||
// as they do not share data.
|
||||
$httpProvider.defaults.headers.post =
|
||||
$httpProvider.defaults.headers.put =
|
||||
$httpProviders.defaults.headers.patch = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
+35
-29
@@ -50,7 +50,7 @@ Comment on an issue to let others know what you're working on, or create a new i
|
||||
doesn't fit within the scope of any of the existing doc fix projects.
|
||||
|
||||
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
|
||||
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
|
||||
accidentally introduced any layout or formatting issues. You should also make sure that your commit message
|
||||
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
|
||||
@@ -92,16 +92,19 @@ Before you submit your pull request consider the following guidelines:
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
`validate-commit-msg.js`):
|
||||
* Create your patch, **including appropriate test cases**.
|
||||
* Follow our [Coding Rules](#coding-rules).
|
||||
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||
and ensure that all tests pass.
|
||||
* Commit your changes using a descriptive commit message that follows our
|
||||
[commit message conventions](#commit-message-format) and passes our commit message presubmit hook
|
||||
`validate-commit-msg.js`. Adherence to the [commit message conventions](#commit-message-format)
|
||||
is required because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
|
||||
* Build your changes locally to ensure all the tests pass
|
||||
|
||||
@@ -109,15 +112,17 @@ Before you submit your pull request consider the following guidelines:
|
||||
grunt test
|
||||
```
|
||||
|
||||
* Push your branch to Github:
|
||||
* Push your branch to GitHub:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In Github, send a pull request to `angular:master`.
|
||||
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
|
||||
repository to update the Pull Request:
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suite to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
@@ -126,10 +131,12 @@ Before you submit your pull request consider the following guidelines:
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
#### After your pull request is merged
|
||||
|
||||
After your pull request is merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on Github:
|
||||
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
@@ -245,24 +252,23 @@ You can find out more detailed information about contributing in the
|
||||
|
||||
|
||||
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[contributing]: http://docs.angularjs.org/misc/contribute
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
[contributing]: http://docs.angularjs.org/misc/contribute
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
|
||||
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[jsfiddle]: http://jsfiddle.net/
|
||||
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
var versionInfo = require('./lib/versions/version-info');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
@@ -8,10 +9,10 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
var NG_VERSION = versionInfo.currentVersion;
|
||||
NG_VERSION.cdn = versionInfo.cdnVersion;
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
|
||||
//global beforeEach
|
||||
util.init();
|
||||
|
||||
@@ -278,7 +279,7 @@ module.exports = function(grunt) {
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
AngularJS [](https://travis-ci.org/angular/angular.js)
|
||||
AngularJS [](https://travis-ci.org/angular/angular.js)
|
||||
=========
|
||||
|
||||
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
|
||||
|
||||
+3
-3
@@ -61,9 +61,9 @@ This process based on the idea of minimizing user pain
|
||||
1. Label `origin: google` for issues from Google
|
||||
|
||||
1. Assign a milestone:
|
||||
* Current 1.x.y milestone - regressions and urgent bugs only
|
||||
* Backlog - fixes; changes that should go into a patch release
|
||||
* Ice Box - new features; changes that belong inß a major/minor release
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
|
||||
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<h1>Oops!</h1>
|
||||
|
||||
<p>The page you requested does not exist. Perhaps you were looking for something else...</p>
|
||||
|
||||
<div ng-controller="Error404SearchCtrl">
|
||||
|
||||
<dl ng-repeat="(key, value) in results" ng-show="value.length" style="float: left; margin-right:20px">
|
||||
<dt>{{ key }}</dt>
|
||||
<dd ng-repeat="item in value"><a ng-href="{{ item.path }}">{{ item.name }}</a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
@@ -184,10 +184,12 @@ h1,h2,h3,h4,h5,h6 {
|
||||
}
|
||||
|
||||
pre {
|
||||
padding:15px;
|
||||
border:1px solid #ddd;
|
||||
display:block;
|
||||
border-radius:5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.aside-nav a,
|
||||
@@ -420,6 +422,7 @@ iframe.example {
|
||||
|
||||
.type-hint {
|
||||
display:inline-block;
|
||||
background: gray;
|
||||
}
|
||||
|
||||
.variables-matrix .type-hint {
|
||||
@@ -464,6 +467,14 @@ iframe.example {
|
||||
background:rgb(189, 63, 66);
|
||||
}
|
||||
|
||||
.type-hint-regexp {
|
||||
background: rgb(90, 84, 189);
|
||||
}
|
||||
|
||||
.type-hint-domelement {
|
||||
background: rgb(95, 158, 160);
|
||||
}
|
||||
|
||||
.runnable-example-frame {
|
||||
width:100%;
|
||||
height:300px;
|
||||
@@ -501,10 +512,6 @@ h4 {
|
||||
padding-top:20px;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color:#428bca;
|
||||
position: relative;
|
||||
@@ -538,10 +545,17 @@ h4 {
|
||||
background:white!important;
|
||||
}
|
||||
|
||||
.view-source, .improve-docs {
|
||||
position:relative;
|
||||
z-index:100;
|
||||
}
|
||||
|
||||
.view-source {
|
||||
margin-right:10px;
|
||||
padding-right:10px;
|
||||
border-right:1px solid #999;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.return-arguments,
|
||||
@@ -554,7 +568,7 @@ h4 {
|
||||
}
|
||||
|
||||
.return-arguments td:first-child {
|
||||
width:100px;
|
||||
width:100px;
|
||||
}
|
||||
|
||||
ul.methods > li,
|
||||
|
||||
@@ -2,6 +2,8 @@ angular.module('DocsController', [])
|
||||
|
||||
.controller('DocsController', function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, NG_VERSION) {
|
||||
|
||||
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
|
||||
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
@@ -87,7 +89,7 @@ angular.module('DocsController', [])
|
||||
breadcrumbPath += '/';
|
||||
});
|
||||
} else {
|
||||
$scope.currentArea = null;
|
||||
$scope.currentArea = NG_NAVIGATION['api'];
|
||||
$scope.breadcrumb = [];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,6 +45,10 @@ angular.module('search', [])
|
||||
};
|
||||
}])
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
|
||||
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
|
||||
}])
|
||||
|
||||
.factory('lunrSearch', function() {
|
||||
return function(properties) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
|
||||
|
||||
@@ -21,10 +21,10 @@ angular.module('tutorials', [])
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
@@ -25,6 +25,10 @@ module.exports = function(config) {
|
||||
require('./tag-defs/tutorial-step')
|
||||
]);
|
||||
|
||||
config.append('processing.inlineTagDefinitions', [
|
||||
require('./inline-tag-defs/type')
|
||||
]);
|
||||
|
||||
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
|
||||
|
||||
config.prepend('rendering.templateFolders', [
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
|
||||
var encoder = new require('node-html-encoder').Encoder();
|
||||
|
||||
module.exports = {
|
||||
name: 'type',
|
||||
description: 'Replace with markup that displays a nice type',
|
||||
handlerFactory: function() {
|
||||
return function(doc, tagName, tagDescription) {
|
||||
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -22,6 +22,12 @@ module.exports = {
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'error' ) {
|
||||
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
|
||||
|
||||
var namespaceDoc = errorNamespaces[doc.namespace];
|
||||
if ( !namespaceDoc ) {
|
||||
// First time we came across this namespace, so create a new one
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var gruntUtils = require('../../../lib/grunt/utils');
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
module.exports = {
|
||||
name: 'git-data',
|
||||
@@ -6,9 +7,9 @@ module.exports = {
|
||||
description: 'This processor adds information from the local git repository to the extraData injectable',
|
||||
init: function(config, injectables) {
|
||||
injectables.value('gitData', {
|
||||
version: gruntUtils.getVersion(),
|
||||
versions: gruntUtils.getPreviousVersions(),
|
||||
info: gruntUtils.getGitRepoInfo()
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
});
|
||||
},
|
||||
process: function(extraData, gitData) {
|
||||
|
||||
@@ -11,17 +11,17 @@ var AREA_NAMES = {
|
||||
};
|
||||
|
||||
function getNavGroup(pages, area, pageSorter, pageMapper) {
|
||||
|
||||
|
||||
var navItems = _(pages)
|
||||
// We don't want the child to include the index page as this is already catered for
|
||||
.omit(function(page) { return page.id === 'index'; })
|
||||
|
||||
|
||||
// Apply the supplied sorting function
|
||||
.sortBy(pageSorter)
|
||||
|
||||
|
||||
// Apply the supplied mapping function
|
||||
.map(pageMapper)
|
||||
|
||||
|
||||
.value();
|
||||
|
||||
return {
|
||||
@@ -145,6 +145,9 @@ module.exports = {
|
||||
_(docs)
|
||||
.filter(function(doc) { return doc.area === 'api'; })
|
||||
.filter(function(doc) { return doc.docType === 'module'; })
|
||||
.forEach(function(doc) { if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}})
|
||||
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
|
||||
.tap(function(docs) {
|
||||
log.debug(docs);
|
||||
@@ -173,7 +176,7 @@ module.exports = {
|
||||
// - ngView
|
||||
// - section "service"
|
||||
// - $route
|
||||
//
|
||||
//
|
||||
var areas = {};
|
||||
_(navPages)
|
||||
.groupBy('area')
|
||||
@@ -188,12 +191,6 @@ module.exports = {
|
||||
area.navGroups = navGroupMapper(pages, area);
|
||||
});
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Extract a list of basic page information for mapping paths to paritals and for client side searching
|
||||
var pages = _(docs)
|
||||
.map(function(doc) {
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
|
||||
<select ng-options="v as ('v' + v.full) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
ng-model="docs_version"
|
||||
ng-change="jumpToDocsVersion(docs_version)"
|
||||
class="docs-version-jump">
|
||||
@@ -219,7 +219,7 @@
|
||||
</div>
|
||||
<div class="grid-right">
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath" onload="afterPartialLoaded()" autoscroll></div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
<a ng-href="http://plnkr.co/edit/ngdoc:{$ doc.example.id $}@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank">
|
||||
<i class="glyphicon glyphicon-edit"> </i>
|
||||
Edit in Plunker</a>
|
||||
<div class="runnable-example"
|
||||
path="{$ doc.example.outputFolder $}"
|
||||
{%- for attrName, attrValue in doc.example.attributes %}
|
||||
{$ attrName $}="{$ attrValue $}"{% endfor %}>
|
||||
|
||||
{% for fileName, file in doc.example.files %}
|
||||
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
|
||||
{$ attrName $}="{$ attrValue $}"{% endfor %}>
|
||||
{% code -%}
|
||||
{$ file.fileContents $}
|
||||
{%- endcode %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
@@ -30,6 +30,9 @@ Following are invalid uses of this directive:
|
||||
|
||||
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
|
||||
<my-directive bind="myFn()">
|
||||
|
||||
<!-- ERROR because attribute bind wasn't provided -->
|
||||
<my-directive>
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -3,24 +3,31 @@
|
||||
@fullName Module Unavailable
|
||||
@description
|
||||
|
||||
This error occurs when trying to "re-open" a module that has not yet been defined.
|
||||
This error occurs when you declare a dependency on a module that isn't defined anywhere or hasn't
|
||||
been loaded in the current browser context.
|
||||
|
||||
When you receive this error, check that the name of the module in question is correct and that the
|
||||
file in which this module is defined has been loaded (either via `<script>` tag, loader like
|
||||
require.js, or testing harness like karma).
|
||||
|
||||
A less common reason for this error is trying to "re-open" a module that has not yet been defined.
|
||||
|
||||
To define a new module, call {@link angular.module angular.module} with a name
|
||||
and an array of dependent modules, like so:
|
||||
|
||||
```
|
||||
```js
|
||||
// When defining a module with no module dependencies,
|
||||
// the requires array should be defined and empty.
|
||||
// the array of dependencies should be defined and empty.
|
||||
var myApp = angular.module('myApp', []);
|
||||
```
|
||||
|
||||
To retrieve a reference to the same module for further configuration, call
|
||||
`angular.module` without the `requires` array.
|
||||
`angular.module` without the array argument.
|
||||
|
||||
```
|
||||
```js
|
||||
var myApp = angular.module('myApp');
|
||||
```
|
||||
|
||||
Calling `angular.module` without the `requires` array when the module has not yet
|
||||
been defined causes this error to be thrown. To fix it, define your module with
|
||||
a name and an empty array, as in the first example above.
|
||||
Calling `angular.module` without the array of dependencies when the module has not yet been defined
|
||||
causes this error to be thrown. To fix it, define your module with a name and an empty array, as in
|
||||
the first example above.
|
||||
|
||||
@@ -9,7 +9,7 @@ correctly. For example:
|
||||
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
.controller('myCtrl', ['myService', function (myService) {
|
||||
.controller('MyController', ['myService', function (myService) {
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
@@ -20,7 +20,7 @@ sure each dependency is defined will fix the problem.
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
.service('myService', function () { /* ... */ })
|
||||
.controller('myCtrl', ['myService', function (myService) {
|
||||
.controller('MyController', ['myService', function (myService) {
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
@@ -2,6 +2,8 @@
|
||||
@name Error Reference
|
||||
@description
|
||||
|
||||
# Error Reference
|
||||
|
||||
Use the Error Reference manual to find information about error conditions in
|
||||
your AngularJS app. Errors thrown in production builds of AngularJS will log
|
||||
links to this site on the console.
|
||||
|
||||
@@ -20,7 +20,7 @@ application.
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that for bootrapping purposes, the `<html>` element is the same as `document`, so the following
|
||||
Note that for bootstrapping purposes, the `<html>` element is the same as `document`, so the following
|
||||
will also throw an error.
|
||||
|
||||
```
|
||||
@@ -38,12 +38,12 @@ You can also get this error if you accidentally load AngularJS itself more than
|
||||
<html ng-app>
|
||||
<head>
|
||||
<script src="angular.js"></script>
|
||||
|
||||
|
||||
...
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
...
|
||||
|
||||
<script src="angular.js"></script>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.
|
||||
|
||||
|
||||
## Angular `<script>` Tag
|
||||
|
||||
This example shows the recommended path for integrating Angular with what we call automatic
|
||||
@@ -79,7 +80,6 @@ If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
|
||||
|
||||
## Manual Initialization
|
||||
|
||||
|
||||
If you need to have more control over the initialization process, you can use a manual
|
||||
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
|
||||
or the need to perform an operation before Angular compiles a page.
|
||||
@@ -88,24 +88,36 @@ Here is an example of manually initializing Angular:
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.module('myApp', []);
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
<html>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
|
||||
<script>
|
||||
angular.module('myApp', [])
|
||||
.controller('MyController', ['$scope', function ($scope) {
|
||||
$scope.greetMe = 'World';
|
||||
}]);
|
||||
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that we have provided the name of our application module to be loaded into the injector as the second
|
||||
Note that we provided the name of our application module to be loaded into the injector as the second
|
||||
parameter of the {@link angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
|
||||
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
|
||||
|
||||
You should call `angular.bootstrap()` *after* you've loaded or defined your modules.
|
||||
You cannot add controllers, services, directives, etc after an application bootstraps.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** You should not use the ng-app directive when manually bootstrapping your app.
|
||||
</div>
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
1. After the page and all of the code is loaded, find the root element of your AngularJS
|
||||
@@ -114,6 +126,7 @@ This is the sequence that your code should follow:
|
||||
2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
|
||||
## Deferred Bootstrap
|
||||
|
||||
This feature enables tools like Batarang and test runners to
|
||||
|
||||
@@ -325,7 +325,7 @@ This will not render properly, unless we do some scope magic.
|
||||
|
||||
The first issue we have to solve is that the dialog box template expects `title` to be defined.
|
||||
But we would like the template's scope property `title` to be the result of interpolating the
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
|
||||
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
@name Conceptual Overview
|
||||
@description
|
||||
|
||||
There are some concepts within Angular that you should understand before creating your first application.
|
||||
This section touches all important parts of Angular really quickly using a simple example.
|
||||
However, it won't explain all details.
|
||||
For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
|
||||
# Conceptual Overview
|
||||
|
||||
This section briefly touches on all of the important parts of AngularJS using a simple example.
|
||||
For a more in-depth explanation, see the {@link tutorial/ tutorial}.
|
||||
|
||||
| Concept | Description |
|
||||
|------------------|------------------------------------------|
|
||||
@@ -25,16 +25,16 @@ For a more in-depth explanation, have a look at the {@link tutorial/ tutorial}.
|
||||
|{@link concepts#service Service} | reusable business logic independent of views |
|
||||
|
||||
|
||||
# A first example: Data binding
|
||||
## A first example: Data binding
|
||||
|
||||
In the following example we will build a form to calculate the costs of an invoice in different currencies.
|
||||
|
||||
Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
|
||||
|
||||
|
||||
<example>
|
||||
<example name="guide-concepts-1" ng-app-included="true">
|
||||
<file name="index.html">
|
||||
<div ng-init="qty=1;cost=2">
|
||||
<div ng-app ng-init="qty=1;cost=2">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="qty" required >
|
||||
@@ -97,12 +97,12 @@ recalculated and the DOM is updated with their values.
|
||||
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
|
||||
|
||||
|
||||
# Adding UI logic: Controllers
|
||||
## Adding UI logic: Controllers
|
||||
|
||||
Let's add some more logic to the example that allows us to enter and calculate the costs in
|
||||
different currencies and also pay the invoice.
|
||||
|
||||
<example module="invoice1">
|
||||
<example name="guide-concepts-2" ng-app-included="true" >
|
||||
<file name="invoice1.js">
|
||||
angular.module('invoice1', [])
|
||||
.controller('InvoiceController', function() {
|
||||
@@ -128,7 +128,7 @@ different currencies and also pay the invoice.
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
@@ -181,7 +181,7 @@ The following graphic shows how everything works together after we introduced th
|
||||
|
||||
<img style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding2.png">
|
||||
|
||||
# View independent business logic: Services
|
||||
## View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
@@ -191,7 +191,7 @@ from the web, e.g. by calling the Yahoo Finance API, without changing the contro
|
||||
|
||||
Let's refactor our example and move the currency conversion into a service in another file:
|
||||
|
||||
<example module="invoice2">
|
||||
<example name="guide-concepts-2" ng-app-included="true">
|
||||
<file name="finance2.js">
|
||||
angular.module('finance2', [])
|
||||
.factory('currencyConverter', function() {
|
||||
@@ -228,7 +228,7 @@ Let's refactor our example and move the currency conversion into a service in an
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
@@ -297,12 +297,12 @@ Angular uses this array syntax to define the dependencies so that the DI also wo
|
||||
the code, which will most probably rename the argument name of the controller constructor function
|
||||
to something shorter like `a`.
|
||||
|
||||
# Accessing the backend
|
||||
## Accessing the backend
|
||||
|
||||
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
|
||||
The following example shows how this is done with Angular:
|
||||
|
||||
<example module="invoice3">
|
||||
<example name="guide-concepts-3" ng-app-included="true">
|
||||
<file name="invoice3.js">
|
||||
angular.module('invoice3', ['finance3'])
|
||||
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
|
||||
@@ -356,7 +356,7 @@ The following example shows how this is done with Angular:
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="InvoiceController as invoice">
|
||||
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
@@ -379,8 +379,8 @@ The following example shows how this is done with Angular:
|
||||
</example>
|
||||
|
||||
What changed?
|
||||
Our `currencyConverter` service of the `finance` module now uses the
|
||||
{@link ng.$http $http} service, a builtin service provided by Angular
|
||||
for accessing the backend. It is a wrapper around [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports. Details can be found in the api docs of that service.
|
||||
Our `currencyConverter` service of the `finance` module now uses the {@link ng.$http `$http`}, a
|
||||
built-in service provided by Angular for accessing a server backend. `$http` is a wrapper around
|
||||
[`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||
and [JSONP](http://en.wikipedia.org/wiki/JSONP) transports.
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
|
||||
|
||||
<example module="spicyApp1">
|
||||
<file name="index.html">
|
||||
<div ng-controller="SpicyCtrl">
|
||||
<div ng-controller="SpicyController">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
@@ -143,7 +143,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
|
||||
<file name="app.js">
|
||||
var myApp = angular.module('spicyApp1', []);
|
||||
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
myApp.controller('SpicyController', ['$scope', function($scope) {
|
||||
$scope.spice = 'very';
|
||||
|
||||
$scope.chiliSpicy = function() {
|
||||
@@ -160,9 +160,9 @@ string "very". Depending on which button is clicked, the `spice` model is set to
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` Controller.
|
||||
- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
scope is augmented (managed) by the `SpicyController` Controller.
|
||||
- `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Controller" or "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
@@ -175,7 +175,7 @@ previous example.
|
||||
|
||||
<example module="spicyApp2">
|
||||
<file name="index.html">
|
||||
<div ng-controller="SpicyCtrl">
|
||||
<div ng-controller="SpicyController">
|
||||
<input ng-model="customSpice">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
@@ -185,18 +185,18 @@ previous example.
|
||||
<file name="app.js">
|
||||
var myApp = angular.module('spicyApp2', []);
|
||||
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
myApp.controller('SpicyController', ['$scope', function($scope) {
|
||||
$scope.customSpice = "wasabi";
|
||||
$scope.spice = 'very';
|
||||
|
||||
$scope.spicy = function(spice){
|
||||
$scope.spicy = function(spice) {
|
||||
$scope.spice = spice;
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Notice that the `SpicyCtrl` Controller now defines just one method called `spicy`, which takes one
|
||||
Notice that the `SpicyController` Controller now defines just one method called `spicy`, which takes one
|
||||
argument called `spice`. The template then refers to this Controller method and passes in a string
|
||||
constant `'chili'` in the binding for the first button and a model property `customSpice` (bound to an
|
||||
input box) in the second button.
|
||||
@@ -213,13 +213,13 @@ more information about scope inheritance.
|
||||
<example module="scopeInheritance">
|
||||
<file name="index.html">
|
||||
<div class="spicy">
|
||||
<div ng-controller="MainCtrl">
|
||||
<div ng-controller="MainController">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
<div ng-controller="ChildCtrl">
|
||||
<div ng-controller="ChildController">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
<div ng-controller="GrandChildCtrl">
|
||||
<div ng-controller="GrandChildController">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -234,14 +234,14 @@ more information about scope inheritance.
|
||||
</file>
|
||||
<file name="app.js">
|
||||
var myApp = angular.module('scopeInheritance', []);
|
||||
myApp.controller('MainCtrl', ['$scope', function($scope){
|
||||
myApp.controller('MainController', ['$scope', function($scope) {
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}]);
|
||||
myApp.controller('ChildCtrl', ['$scope', function($scope){
|
||||
myApp.controller('ChildController', ['$scope', function($scope) {
|
||||
$scope.name = 'Mattie';
|
||||
}]);
|
||||
myApp.controller('GrandChildCtrl', ['$scope', function($scope){
|
||||
myApp.controller('GrandChildController', ['$scope', function($scope) {
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}]);
|
||||
@@ -252,11 +252,11 @@ Notice how we nested three `ng-controller` directives in our template. This will
|
||||
scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` properties
|
||||
- The `ChildCtrl` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
|
||||
- The `MainController` scope, which contains `timeOfDay` and `name` properties
|
||||
- The `ChildController` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
|
||||
property from the previous
|
||||
- The `GrandChildCtrl` scope, which overrides (hides) both the `timeOfDay` property defined in `MainCtrl`
|
||||
and the `name` property defined in `ChildCtrl`
|
||||
- The `GrandChildController` scope, which overrides (hides) both the `timeOfDay` property defined in `MainController`
|
||||
and the `name` property defined in `ChildController`
|
||||
|
||||
Inheritance works with methods in the same way as it does with properties. So in our previous
|
||||
examples, all of the properties could be replaced with methods that return string values.
|
||||
@@ -316,11 +316,11 @@ describe('state', function() {
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
$controller('MainCtrl', {$scope: mainScope});
|
||||
$controller('MainController', {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
$controller('ChildCtrl', {$scope: childScope});
|
||||
$controller('ChildController', {$scope: childScope});
|
||||
grandChildScope = childScope.$new();
|
||||
$controller('GrandChildCtrl', {$scope: grandChildScope});
|
||||
$controller('GrandChildController', {$scope: grandChildScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
|
||||
@@ -43,13 +43,13 @@ determines when to use a given directive.
|
||||
|
||||
In the following example, we say that the `<input>` element **matches** the `ngModel` directive.
|
||||
|
||||
```javascript
|
||||
```html
|
||||
<input ng-model="foo">
|
||||
```
|
||||
|
||||
The following also **matches** `ngModel`:
|
||||
|
||||
```javascript
|
||||
```html
|
||||
<input data-ng:model="foo">
|
||||
```
|
||||
|
||||
@@ -70,12 +70,12 @@ Here are some equivalent examples of elements that match `ngBind`:
|
||||
<example module="docsBindExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsBindExample', [])
|
||||
.controller('Ctrl1', function Ctrl1($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)';
|
||||
});
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl1">
|
||||
<div ng-controller="Controller">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng-bind="name"></span> <br/>
|
||||
<span ng:bind="name"></span> <br/>
|
||||
@@ -86,7 +86,7 @@ Here are some equivalent examples of elements that match `ngBind`:
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
it('should show off bindings', function() {
|
||||
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
|
||||
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
});
|
||||
</file>
|
||||
@@ -218,12 +218,12 @@ Let's create a directive that simply replaces its contents with a static templat
|
||||
<example module="docsSimpleDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsSimpleDirective', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.customer = {
|
||||
name: 'Naomi',
|
||||
address: '1600 Amphitheatre'
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
template: 'Name: {{customer.name}} Address: {{customer.address}}'
|
||||
@@ -231,7 +231,7 @@ Let's create a directive that simply replaces its contents with a static templat
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<div my-customer></div>
|
||||
</div>
|
||||
</file>
|
||||
@@ -257,12 +257,12 @@ using `templateUrl` instead:
|
||||
<example module="docsTemplateUrlDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsTemplateUrlDirective', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.customer = {
|
||||
name: 'Naomi',
|
||||
address: '1600 Amphitheatre'
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
templateUrl: 'my-customer.html'
|
||||
@@ -270,7 +270,7 @@ using `templateUrl` instead:
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<div my-customer></div>
|
||||
</div>
|
||||
</file>
|
||||
@@ -293,21 +293,21 @@ The `restrict` option is typically set to:
|
||||
* `'E'` - only matches element name
|
||||
* `'C'` - only matches class name
|
||||
|
||||
These restictions can all be combined as needed:
|
||||
These restrictions can all be combined as needed:
|
||||
|
||||
* `'AEC'` - matches either attribure or element or class name
|
||||
* `'AEC'` - matches either attribute or element or class name
|
||||
|
||||
Let's change our directive to use `restrict: 'E'`:
|
||||
|
||||
<example module="docsRestrictDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsRestrictDirective', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.customer = {
|
||||
name: 'Naomi',
|
||||
address: '1600 Amphitheatre'
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -317,7 +317,7 @@ Let's change our directive to use `restrict: 'E'`:
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-customer></my-customer>
|
||||
</div>
|
||||
</file>
|
||||
@@ -358,18 +358,18 @@ re-use such a directive:
|
||||
<example module="docsScopeProblemExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsScopeProblemExample', [])
|
||||
.controller('NaomiCtrl', function($scope) {
|
||||
.controller('NaomiController', ['$scope', function($scope) {
|
||||
$scope.customer = {
|
||||
name: 'Naomi',
|
||||
address: '1600 Amphitheatre'
|
||||
};
|
||||
})
|
||||
.controller('IgorCtrl', function($scope) {
|
||||
}])
|
||||
.controller('IgorController', ['$scope', function($scope) {
|
||||
$scope.customer = {
|
||||
name: 'Igor',
|
||||
address: '123 Somewhere'
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -378,11 +378,11 @@ re-use such a directive:
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="NaomiCtrl">
|
||||
<div ng-controller="NaomiController">
|
||||
<my-customer></my-customer>
|
||||
</div>
|
||||
<hr>
|
||||
<div ng-controller="IgorCtrl">
|
||||
<div ng-controller="IgorController">
|
||||
<my-customer></my-customer>
|
||||
</div>
|
||||
</file>
|
||||
@@ -400,10 +400,10 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
|
||||
<example module="docsIsolateScopeDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsIsolateScopeDirective', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
|
||||
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -415,7 +415,7 @@ we call an **isolate scope**. To do this, we can use a directive's `scope` optio
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-customer info="naomi"></my-customer>
|
||||
<hr>
|
||||
<my-customer info="igor"></my-customer>
|
||||
@@ -473,11 +473,11 @@ within our directive's template:
|
||||
<example module="docsIsolationExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsIsolationExample', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
|
||||
|
||||
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
|
||||
})
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -489,7 +489,7 @@ within our directive's template:
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-customer info="naomi"></my-customer>
|
||||
</div>
|
||||
</file>
|
||||
@@ -510,7 +510,7 @@ that you explicitly pass in.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
See the {@link guide/directive#isolating-the-scope-of-a-directive
|
||||
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
|
||||
</div>
|
||||
|
||||
@@ -543,10 +543,10 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
|
||||
<example module="docsTimeDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsTimeDirective', [])
|
||||
.controller('Ctrl2', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
})
|
||||
.directive('myCurrentTime', function($interval, dateFilter) {
|
||||
}])
|
||||
.directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) {
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
var format,
|
||||
@@ -574,10 +574,10 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
|
||||
return {
|
||||
link: link
|
||||
};
|
||||
});
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl2">
|
||||
<div ng-controller="Controller">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
</div>
|
||||
@@ -619,9 +619,9 @@ To do this, we need to use the `transclude` option.
|
||||
<example module="docsTransclusionDirective">
|
||||
<file name="script.js">
|
||||
angular.module('docsTransclusionDirective', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.name = 'Tobias';
|
||||
})
|
||||
}])
|
||||
.directive('myDialog', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -631,7 +631,7 @@ To do this, we need to use the `transclude` option.
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-dialog>Check out the contents, {{name}}!</my-dialog>
|
||||
</div>
|
||||
</file>
|
||||
@@ -650,9 +650,9 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
<example module="docsTransclusionExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsTransclusionExample', [])
|
||||
.controller('Ctrl', function($scope) {
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.name = 'Tobias';
|
||||
})
|
||||
}])
|
||||
.directive('myDialog', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -666,7 +666,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-dialog>Check out the contents, {{name}}!</my-dialog>
|
||||
</div>
|
||||
</file>
|
||||
@@ -701,7 +701,7 @@ own behavior to it.
|
||||
<example module="docsIsoFnBindExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsIsoFnBindExample', [])
|
||||
.controller('Ctrl', function($scope, $timeout) {
|
||||
.controller('Controller', ['$scope', '$timeout', function($scope, $timeout) {
|
||||
$scope.name = 'Tobias';
|
||||
$scope.hideDialog = function () {
|
||||
$scope.dialogIsHidden = true;
|
||||
@@ -709,7 +709,7 @@ own behavior to it.
|
||||
$scope.dialogIsHidden = false;
|
||||
}, 2000);
|
||||
};
|
||||
})
|
||||
}])
|
||||
.directive('myDialog', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -722,7 +722,7 @@ own behavior to it.
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()">
|
||||
Check out the contents, {{name}}!
|
||||
</my-dialog>
|
||||
@@ -737,7 +737,7 @@ own behavior to it.
|
||||
</example>
|
||||
|
||||
We want to run the function we pass by invoking it from the directive's scope, but have it run
|
||||
in the context of the scope where its registered.
|
||||
in the context of the scope where it's registered.
|
||||
|
||||
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
|
||||
`&attr` instead. The `&` binding allows a directive to trigger evaluation of an expression in
|
||||
@@ -747,7 +747,8 @@ callback functions to directive behaviors.
|
||||
|
||||
When the user clicks the `x` in the dialog, the directive's `close` function is called, thanks to
|
||||
`ng-click.` This call to `close` on the isolated scope actually evaluates the expression
|
||||
`hideDialog()` in the context of the original scope, thus running `Ctrl`'s `hideDialog` function.
|
||||
`hideDialog()` in the context of the original scope, thus running `Controller`'s `hideDialog`
|
||||
function.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
@@ -766,8 +767,8 @@ element?
|
||||
|
||||
<example module="dragModule">
|
||||
<file name="script.js">
|
||||
angular.module('dragModule', []).
|
||||
directive('myDraggable', function($document) {
|
||||
angular.module('dragModule', [])
|
||||
.directive('myDraggable', ['$document', function($document) {
|
||||
return function(scope, element, attr) {
|
||||
var startX = 0, startY = 0, x = 0, y = 0;
|
||||
|
||||
@@ -801,7 +802,7 @@ element?
|
||||
$document.unbind('mouseup', mouseup);
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<span my-draggable>Drag ME</span>
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
@name E2E Testing
|
||||
@description
|
||||
|
||||
**Angular Scenario Runner is in maintenance mode - If you're starting a new Angular project,
|
||||
consider using [Protractor](https://github.com/angular/protractor).**
|
||||
|
||||
<div class="alert alert-danger">
|
||||
**Note:** Angular Scenario Runner is depricated. If you're starting a new Angular project,
|
||||
consider using [Protractor](https://github.com/angular/protractor).
|
||||
</div>
|
||||
|
||||
# E2E Testing with the Angular Scenario Runner
|
||||
|
||||
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
verify the correctness of new features, catch bugs and notice regressions.
|
||||
@@ -14,16 +16,22 @@ verify the correctness of new features, catch bugs and notice regressions.
|
||||
To solve this problem, we have built an Angular Scenario Runner which simulates user interactions
|
||||
that will help you verify the health of your Angular application.
|
||||
|
||||
# Overview
|
||||
## Overview
|
||||
|
||||
You write scenario tests in JavaScript. These tests describe how your application should behave
|
||||
given a certain interaction in a specific state. A scenario is comprised of one or more `it` blocks
|
||||
(you can think of these as the requirements of your application), which in turn are made of
|
||||
**commands** and **expectations**. Commands tell the Runner to do something with the application
|
||||
(such as navigate to a page or click on a button), and expectations tell the Runner to assert
|
||||
something about the state (such as the value of a field or the current URL). If any expectation
|
||||
fails, the runner marks the `it` as "failed" and continues on to the next one. Scenarios may also
|
||||
have **beforeEach** and **afterEach** blocks, which will be run before (or after) each `it` block,
|
||||
regardless of whether they pass or fail.
|
||||
given a certain interaction in a specific state.
|
||||
|
||||
A scenario is comprised of one or more `it` blocks that describe the requirements of your
|
||||
application. `it` blocks are made of **commands** and **expectations**. Commands tell the Runner
|
||||
to do something with the application such as navigate to a page or click on a button. Expectations
|
||||
tell the Runner to assert something about the application's state, such as the value of a field or
|
||||
the current URL.
|
||||
|
||||
If any expectation within an `it` block fails, the runner marks the `it` as "failed" and continues
|
||||
on to the next block.
|
||||
|
||||
Scenarios may also have `beforeEach` and `afterEach` blocks, which will be run before or after
|
||||
each `it` block regardless of whether the block passes or fails.
|
||||
|
||||
<img src="img/guide/scenario_runner.png">
|
||||
|
||||
@@ -54,136 +62,136 @@ the only button on the page, and then it verifies that there are 10 items listed
|
||||
|
||||
The API section below lists the available commands and expectations for the Runner.
|
||||
|
||||
# API
|
||||
## API
|
||||
Source: https://github.com/angular/angular.js/blob/master/src/ngScenario/dsl.js
|
||||
|
||||
## pause()
|
||||
### `pause()`
|
||||
Pauses the execution of the tests until you call `resume()` in the console (or click the resume
|
||||
link in the Runner UI).
|
||||
|
||||
## sleep(seconds)
|
||||
### `sleep(seconds)`
|
||||
Pauses the execution of the tests for the specified number of `seconds`.
|
||||
|
||||
## browser().navigateTo(url)
|
||||
### `browser().navigateTo(url)`
|
||||
Loads the `url` into the test frame.
|
||||
|
||||
## browser().navigateTo(url, fn)
|
||||
### `browser().navigateTo(url, fn)`
|
||||
Loads the URL returned by `fn` into the testing frame. The given `url` is only used for the test
|
||||
output. Use this when the destination URL is dynamic (that is, the destination is unknown when you
|
||||
write the test).
|
||||
|
||||
## browser().reload()
|
||||
### `browser().reload()`
|
||||
Refreshes the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().href()
|
||||
### `browser().window().href()`
|
||||
Returns the window.location.href of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().path()
|
||||
### `browser().window().path()`
|
||||
Returns the window.location.pathname of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().search()
|
||||
### `browser().window().search()`
|
||||
Returns the window.location.search of the currently loaded page in the test frame.
|
||||
|
||||
## browser().window().hash()
|
||||
### `browser().window().hash()`
|
||||
Returns the window.location.hash (without `#`) of the currently loaded page in the test frame.
|
||||
|
||||
## browser().location().url()
|
||||
### `browser().location().url()`
|
||||
Returns the {@link ng.$location $location.url()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## browser().location().path()
|
||||
### `browser().location().path()`
|
||||
Returns the {@link ng.$location $location.path()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## browser().location().search()
|
||||
### `browser().location().search()`
|
||||
Returns the {@link ng.$location $location.search()} of the currently loaded page
|
||||
in the test frame.
|
||||
|
||||
## browser().location().hash()
|
||||
### `browser().location().hash()`
|
||||
Returns the {@link ng.$location $location.hash()} of the currently loaded page in
|
||||
the test frame.
|
||||
|
||||
## expect(future).{matcher}
|
||||
### `expect(future).{matcher}`
|
||||
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
|
||||
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
|
||||
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
|
||||
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
|
||||
are presented further down this document.
|
||||
|
||||
## expect(future).not().{matcher}
|
||||
### `expect(future).not().{matcher}`
|
||||
Asserts the value of the given `future` satisfies the negation of the `matcher`.
|
||||
|
||||
## using(selector, label)
|
||||
### `using(selector, label)`
|
||||
Scopes the next DSL element selection.
|
||||
|
||||
## binding(name)
|
||||
### `binding(name)`
|
||||
Returns the value of the first binding matching the given `name`.
|
||||
|
||||
## input(name).enter(value)
|
||||
### `input(name).enter(value)`
|
||||
Enters the given `value` in the text field with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).check()
|
||||
### `input(name).check()`
|
||||
Checks/unchecks the checkbox with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).select(value)
|
||||
### `input(name).select(value)`
|
||||
Selects the given `value` in the radio button with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).val()
|
||||
### `input(name).val()`
|
||||
Returns the current value of an input field with the corresponding ng-model `name`.
|
||||
|
||||
## repeater(selector, label).count()
|
||||
### `repeater(selector, label).count()`
|
||||
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
|
||||
used for test output.
|
||||
|
||||
## repeater(selector, label).row(index)
|
||||
### `repeater(selector, label).row(index)`
|
||||
Returns an array with the bindings in the row at the given `index` in the repeater matching the
|
||||
given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## repeater(selector, label).column(binding)
|
||||
### `repeater(selector, label).column(binding)`
|
||||
Returns an array with the values in the column with the given `binding` in the repeater matching
|
||||
the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## select(name).option(value)
|
||||
### `select(name).option(value)`
|
||||
Picks the option with the given `value` on the select with the given ng-model `name`.
|
||||
|
||||
## select(name).options(value1, value2...)
|
||||
### `select(name).options(value1, value2...)`
|
||||
Picks the options with the given `values` on the multi select with the given ng-model `name`.
|
||||
|
||||
## element(selector, label).count()
|
||||
### `element(selector, label).count()`
|
||||
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
|
||||
output.
|
||||
|
||||
## element(selector, label).click()
|
||||
### `element(selector, label).click()`
|
||||
Clicks on the element matching the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).query(fn)
|
||||
### `element(selector, label).query(fn)`
|
||||
Executes the function `fn(selectedElements, done)`, where selectedElements are the elements that
|
||||
match the given jQuery `selector` and `done` is a function that is called at the end of the `fn`
|
||||
function. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}()
|
||||
### `element(selector, label).{method}()`
|
||||
Returns the result of calling `method` on the element matching the given jQuery `selector`, where
|
||||
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
|
||||
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
|
||||
`scrollTop`, `offset`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(value)
|
||||
### `element(selector, label).{method}(value)`
|
||||
Executes the `method` passing in `value` on the element matching the given jQuery `selector`, where
|
||||
`method` can be any of the following jQuery methods: `val`, `text`, `html`, `height`,
|
||||
`innerHeight`, `outerHeight`, `width`, `innerWidth`, `outerWidth`, `position`, `scrollLeft`,
|
||||
`scrollTop`, `offset`. The `label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(key)
|
||||
### `element(selector, label).{method}(key)`
|
||||
Returns the result of calling `method` passing in `key` on the element matching the given jQuery
|
||||
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
|
||||
`label` is used for test output.
|
||||
|
||||
## element(selector, label).{method}(key, value)
|
||||
### `element(selector, label).{method}(key, value)`
|
||||
Executes the `method` passing in `key` and `value` on the element matching the given jQuery
|
||||
`selector`, where `method` can be any of the following jQuery methods: `attr`, `prop`, `css`. The
|
||||
`label` is used for test output.
|
||||
|
||||
# Matchers
|
||||
## Matchers
|
||||
|
||||
Matchers are used in combination with the `expect(...)` function as described above and can
|
||||
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
|
||||
@@ -221,10 +229,10 @@ expect(value).toBeLessThan(expected)
|
||||
expect(value).toBeGreaterThan(expected)
|
||||
```
|
||||
|
||||
# Example
|
||||
## Example
|
||||
See the [angular-seed](https://github.com/angular/angular-seed) project for more examples.
|
||||
|
||||
## Conditional actions with element(...).query(fn)
|
||||
### Conditional actions with element(...).query(fn)
|
||||
|
||||
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
|
||||
queueing actions and expectations that can handle futures. From time to time, you might need
|
||||
@@ -308,6 +316,6 @@ element('.btn-danger').click();
|
||||
element('.btn-danger').click();
|
||||
```
|
||||
|
||||
# Caveats
|
||||
## Caveats
|
||||
|
||||
`ngScenario` does not work with apps that manually bootstrap using `angular.bootstrap`. You must use the `ng-app` directive.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
@name Expressions
|
||||
@description
|
||||
|
||||
Expressions are JavaScript-like code snippets that are usually placed in bindings such as
|
||||
# Angular Expressions
|
||||
|
||||
Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as
|
||||
`{{ expression }}`.
|
||||
|
||||
For example, these are valid expressions in Angular:
|
||||
@@ -88,7 +90,7 @@ You can try evaluating different expressions here:
|
||||
</example>
|
||||
|
||||
|
||||
# Context
|
||||
## Context
|
||||
|
||||
Angular does not use JavaScript's `eval()` to evaluate expressions. Instead Angular's
|
||||
{@link ng.$parse $parse} service processes these expressions.
|
||||
@@ -154,3 +156,44 @@ You cannot write a control flow statement in an expression. The reason behind th
|
||||
Angular philosophy that application logic should be in controllers, not the views. If you need a
|
||||
conditional, loop, or to throw from a view expression, delegate to a JavaScript method instead.
|
||||
|
||||
## `$event`
|
||||
|
||||
Directives like {@link ng.directive:ngClick `ngClick`} and {@link ng.directive:ngFocus `ngFocus`}
|
||||
expose a `$event` object within the scope of that expression.
|
||||
|
||||
<example module="eventExampleApp">
|
||||
<file name="index.html">
|
||||
<div ng-controller="EventController">
|
||||
<button ng-click="clickMe($event)">Event</button>
|
||||
<p><code>$event</code>: <pre> {{$event | json}}</pre></p>
|
||||
<p><code>clickEvent</code>: <pre>{{clickEvent | json}}</pre></p>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('eventExampleApp', []).
|
||||
controller('EventController', ['$scope', function($scope) {
|
||||
/*
|
||||
* expose the event object to the scope
|
||||
*/
|
||||
$scope.clickMe = function(clickEvent) {
|
||||
$scope.clickEvent = simpleKeys(clickEvent);
|
||||
console.log(clickEvent);
|
||||
};
|
||||
|
||||
/*
|
||||
* return a copy of an object with only non-object keys
|
||||
* we need this to avoid circular references
|
||||
*/
|
||||
function simpleKeys (original) {
|
||||
return Object.keys(original).reduce(function (obj, key) {
|
||||
obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Note in the example above how we can pass in `$event` to `clickMe`, but how it does not show up
|
||||
in `{{$event}}`. This is because `$event` is outside the scope of that binding.
|
||||
|
||||
@@ -29,12 +29,12 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
|
||||
{@link ng.filter:number `number`} filter. The resulting value is `1,234.00`.
|
||||
|
||||
|
||||
## Using filters in controllers and services
|
||||
## Using filters in controllers, services, and directives
|
||||
|
||||
You can also use filters in controllers and services. For this, add a dependency with the name `<filterName>Filter`
|
||||
to your controller or service. E.g. using the dependency `numberFilter` will inject the number filter.
|
||||
The injected argument is a function that takes the value to format as first argument and filter parameters
|
||||
starting with the second argument.
|
||||
You can also use filters in controllers, services, and directives. For this, inject a dependency
|
||||
with the name `<filterName>Filter` to your controller/service/directive. E.g. using the dependency
|
||||
`numberFilter` will inject the number filter. The injected argument is a function that takes the
|
||||
value to format as first argument and filter parameters starting with the second argument.
|
||||
|
||||
The example below uses the filter called {@link ng.filter:filter `filter`}.
|
||||
This filter reduces arrays into sub arrays based on
|
||||
@@ -89,9 +89,9 @@ function.
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case.
|
||||
|
||||
<example module="MyReverseModule">
|
||||
<example module="myReverseModule">
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div ng-controller="Controller">
|
||||
<input ng-model="greeting" type="text"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
@@ -100,9 +100,10 @@ text upper-case.
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('MyReverseModule', []).
|
||||
filter('reverse', function() {
|
||||
angular.module('myReverseModule', [])
|
||||
.filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
input = input || '';
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
@@ -113,11 +114,10 @@ text upper-case.
|
||||
}
|
||||
return out;
|
||||
};
|
||||
});
|
||||
|
||||
function Ctrl($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}
|
||||
})
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Note that `novalidate` is used to disable browser's native form validation.
|
||||
|
||||
# Using CSS classes
|
||||
|
||||
To allow styling of form as well as controls, `ngModel` add these CSS classes:
|
||||
To allow styling of form as well as controls, `ngModel` adds these CSS classes:
|
||||
|
||||
- `ng-valid`
|
||||
- `ng-invalid`
|
||||
@@ -211,26 +211,24 @@ In the following example we create two directives.
|
||||
|
||||
<example module="form-example1">
|
||||
<file name="index.html">
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
|
||||
@@ -2,53 +2,54 @@
|
||||
@name i18n and l10n
|
||||
@description
|
||||
|
||||
# I18n and L10n in AngularJS
|
||||
# i18n and l10n
|
||||
|
||||
**What is i18n and l10n?**
|
||||
Internationalization (i18n) is the process of developing products in such a way that they can be
|
||||
localized for languages and cultures easily. Localization (l10n), is the process of adapting
|
||||
applications and text to enable their usability in a particular cultural or linguistic market. For
|
||||
application developers, internationalizing an application means abstracting all of the strings and
|
||||
other locale-specific bits (such as date or currency formats) out of the application. Localizing an
|
||||
application means providing translations and localized formats for the abstracted bits.
|
||||
|
||||
Internationalization, abbreviated i18n, is the process of developing products in such a way that
|
||||
they can be localized for languages and cultures easily. Localization, abbreviated l10n, is the
|
||||
process of adapting applications and text to enable their usability in a particular cultural or
|
||||
linguistic market. For application developers, internationalizing an application means abstracting
|
||||
all of the strings and other locale-specific bits (such as date or currency formats) out of the
|
||||
application. Localizing an application means providing translations and localized formats for the
|
||||
abstracted bits.
|
||||
|
||||
**What level of support for i18n/l10n is currently in Angular?**
|
||||
## How does Angular support i18n/l10n?
|
||||
|
||||
Currently, Angular supports i18n/l10n for
|
||||
[datetime](http://docs.angularjs.org/#!/api/ng.filter:date),
|
||||
[number](http://docs.angularjs.org/#!/api/ng.filter:number) and
|
||||
[currency](http://docs.angularjs.org/#!/api/ng.filter:currency) filters.
|
||||
Angular supports i18n/l10n for {@link ng.filter:date date}, {@link ng.filter:number number} and
|
||||
{@link ng.filter:currency currency} filters.
|
||||
|
||||
Additionally, Angular supports localizable pluralization support provided by the {@link
|
||||
ng.directive:ngPluralize ngPluralize directive}.
|
||||
Additionally, Angular supports localizable pluralization support through the {@link
|
||||
ng.directive:ngPluralize `ngPluralize` directive}.
|
||||
|
||||
All localizable Angular components depend on locale-specific rule sets managed by the {@link
|
||||
ng.$locale $locale service}.
|
||||
ng.$locale `$locale` service}.
|
||||
|
||||
For readers who want to jump straight into examples, we have a few web pages that showcase how to
|
||||
use Angular filters with various locale rule sets. You can find these examples either on
|
||||
[Github](https://github.com/angular/angular.js/tree/master/i18n/e2e) or in the i18n/e2e folder of
|
||||
Angular development package.
|
||||
There a few examples that showcase how to use Angular filters with various locale rule sets in the
|
||||
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
|
||||
source code.
|
||||
|
||||
**What is a locale id?**
|
||||
|
||||
## What is a locale ID?
|
||||
|
||||
A locale is a specific geographical, political, or cultural region. The most commonly used locale
|
||||
ID consists of two parts: language code and country code. For example, en-US, en-AU, zh-CN are all
|
||||
valid locale IDs that have both language codes and country codes. Because specifying a country code
|
||||
in locale ID is optional, locale IDs such as en, zh, and sk are also valid. See the
|
||||
[ICU ](http://userguide.icu-project.org/locale) website for more information about using locale IDs.
|
||||
ID consists of two parts: language code and country code. For example, `en-US`, `en-AU`, and
|
||||
`zh-CN` are all valid locale IDs that have both language codes and country codes. Because
|
||||
specifying a country code in locale ID is optional, locale IDs such as `en`, `zh`, and `sk` are
|
||||
also valid. See the [ICU](http://userguide.icu-project.org/locale) website for more information
|
||||
about using locale IDs.
|
||||
|
||||
|
||||
## Supported locales in Angular
|
||||
|
||||
**Supported locales in Angular**
|
||||
Angular separates number and datetime format rule sets into different files, each file for a
|
||||
particular locale. You can find a list of currently supported locales
|
||||
[here](https://github.com/angular/angular.js/tree/master/src/ngLocale)
|
||||
# Providing locale rules to Angular
|
||||
|
||||
|
||||
## Providing locale rules to Angular
|
||||
|
||||
There are two approaches to providing locale rules to Angular:
|
||||
|
||||
**1. Pre-bundled rule sets**
|
||||
### 1. Pre-bundled rule sets
|
||||
|
||||
You can pre-bundle the desired locale file with Angular by concatenating the content of the
|
||||
locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
||||
@@ -61,7 +62,7 @@ locale, you can do the following:
|
||||
When the application containing `angular_de-de.js` script instead of the generic angular.js script
|
||||
starts, Angular is automatically pre-configured with localization rules for the german locale.
|
||||
|
||||
**2. Including locale js script in index.html page**
|
||||
### 2. Including a locale script in `index.html`
|
||||
|
||||
You can also include the locale specific js file in the index.html page. For example, if one client
|
||||
requires German locale, you would serve index_de-de.html which will look something like this:
|
||||
@@ -77,48 +78,61 @@ requires German locale, you would serve index_de-de.html which will look somethi
|
||||
</html>
|
||||
```
|
||||
|
||||
**Comparison of the two approaches**
|
||||
Both approaches described above requires you to prepare different index.html pages or js files for
|
||||
each locale that your app may be localized into. You also need to configure your server to serve
|
||||
### Comparison of the two approaches
|
||||
|
||||
Both approaches described above require you to prepare different `index.html` pages or JavaScript
|
||||
files for each locale that your app may use. You also need to configure your server to serve
|
||||
the correct file that correspond to the desired locale.
|
||||
|
||||
However, the second approach (Including locale js script in index.html page) is likely to be slower
|
||||
because an extra script needs to be loaded.
|
||||
The second approach (including the locale JavaScript file in `index.html`) may be slower because
|
||||
an extra script needs to be loaded.
|
||||
|
||||
|
||||
# "Gotchas"
|
||||
## Caveats
|
||||
|
||||
**Currency symbol "gotcha"**
|
||||
Although Angular makes i18n convenient, there are several things you need to be conscious of as you
|
||||
develop your app.
|
||||
|
||||
Angular's [currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) allows
|
||||
you to use the default currency symbol from the {@link ng.$locale locale service},
|
||||
or you can provide the filter with a custom currency symbol. If your app will be used only in one
|
||||
locale, it is fine to rely on the default currency symbol. However, if you anticipate that viewers
|
||||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||
actual value is understood.
|
||||
### Currency symbol
|
||||
|
||||
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
Angular's {@link ng.filter:currency currency filter} allows you to use the default currency symbol
|
||||
from the {@link ng.$locale locale service}, or you can provide the filter with a custom currency
|
||||
symbol.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** If your app will be used only in one locale, it is fine to rely on the default
|
||||
currency symbol. If you anticipate that viewers in other locales might use your app, you should
|
||||
explicitly provide a currency symbol.
|
||||
</div>
|
||||
|
||||
Let's say you are writing a banking app and you want to display an account balance of 1000 dollars.
|
||||
You write the following binding using the currency filter:
|
||||
|
||||
```html
|
||||
{{ 1000 | currency }}
|
||||
```
|
||||
|
||||
If your app is currently in the `en-US` locale, the browser will show `$1000.00`. If someone in the
|
||||
Japanese locale (`ja`) views your app, their browser will show a balance of `¥1000.00` instead.
|
||||
This is problematinc because $1000 is not the same as ¥1000.
|
||||
|
||||
In this case, you need to override the default currency symbol by providing the
|
||||
[currency filter](http://docs.angularjs.org/#!/api/ng.filter:currency) with a currency symbol as
|
||||
a parameter when you configure the filter, for example, {{ 1000 | currency:"USD$"}}. This way,
|
||||
Angular will always show a balance of 'USD$1000' and disregard any locale changes.
|
||||
{@link ng.filter:currency} currency filter with a currency symbol as a parameter.
|
||||
|
||||
**Translation length "gotcha"**
|
||||
If we change the above to `{{ 1000 | currency:"USD$"}}`, Angular will always show a balance of
|
||||
`USD$1000` regardless of locale.
|
||||
|
||||
Keep in mind that translated strings/datetime formats can vary greatly in length. For example,
|
||||
`June 3, 1977` will be translated to Spanish as `3 de junio de 1977`. There are bound to be other
|
||||
more extreme cases. Hence, when internationalizing your apps, you need to apply CSS rules
|
||||
accordingly and do thorough testing to make sure UI components do not overlap.
|
||||
### Translation length
|
||||
|
||||
Translated strings/datetime formats can vary greatly in length. For example, `June 3, 1977` will be
|
||||
translated to Spanish as `3 de junio de 1977`.
|
||||
|
||||
**Timezones**
|
||||
When internationalizing your app, you need to do thorough testing to make sure UI components behave
|
||||
as expected even when their contents vary greatly in content size.
|
||||
|
||||
Keep in mind that Angular datetime filter uses the time zone settings of the browser. So the same
|
||||
### Timezones
|
||||
|
||||
The Angular datetime filter uses the time zone settings of the browser. The same
|
||||
application will show different time information depending on the time zone settings of the
|
||||
computer that the application is running on. Neither Javascript nor Angular currently supports
|
||||
computer that the application is running on. Neither JavaScript nor Angular currently supports
|
||||
displaying the date with a timezone specified by the developer.
|
||||
|
||||
+24
-19
@@ -1,32 +1,37 @@
|
||||
@ngdoc overview
|
||||
@name Internet Explorer Compatibility
|
||||
@name Internet Explorer Compatibility
|
||||
@description
|
||||
|
||||
# Overview
|
||||
# Internet Explorer Compatibility
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** AngularJS 1.3 is dropping support for IE8. Read more about it on
|
||||
[our blog](http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html).
|
||||
AngularJS 1.2 will continue to support IE8, but the core team does not plan to spend time
|
||||
addressing issues specific to IE8 or earlier.
|
||||
</div>
|
||||
|
||||
This document describes the Internet Explorer (IE) idiosyncrasies when dealing with custom HTML
|
||||
attributes and tags. Read this document if you are planning on deploying your Angular application
|
||||
on IE v8.0 or earlier.
|
||||
on IE8 or earlier.
|
||||
|
||||
The project currently supports and will attempt to fix bugs for IE8 and above. The continuous
|
||||
integration server runs all the tests against IE8. See http://ci.angularjs.org.
|
||||
The project currently supports and will attempt to fix bugs for IE9 and above. The continuous
|
||||
integration server runs all the tests against IE9, IE10, and IE11. See
|
||||
[Travis CI](https://travis-ci.org/angular/angular.js) and
|
||||
[ci.angularjs.org](http://ci.angularjs.org).
|
||||
|
||||
IE7 and below are not tested and the project makes no guarantee that Angular will work on it.
|
||||
A subset of the AngularJS functionality may work. It is up to you to test and decide whether
|
||||
it works for your particular app.
|
||||
|
||||
It is very unlikely that issues specific to IE7 or earlier will be given any time by the core team.
|
||||
[GitHub](https://github.com/angular/angular.js/issues/4974)
|
||||
We do not run tests on IE8 and below. A subset of the AngularJS functionality may work on these
|
||||
browesers, but it is up to you to test and decide whether it works for your particular app.
|
||||
|
||||
|
||||
# Short Version
|
||||
## Short Version
|
||||
|
||||
To make your Angular application work on IE please make sure that:
|
||||
|
||||
1. You polyfill JSON.stringify for IE7 and below. You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
@@ -42,7 +47,7 @@ To make your Angular application work on IE please make sure that:
|
||||
```
|
||||
|
||||
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
|
||||
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
@@ -54,7 +59,7 @@ To make your Angular application work on IE please make sure that:
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
|
||||
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
@@ -64,7 +69,7 @@ To make your Angular application work on IE please make sure that:
|
||||
document.createElement('ng-include');
|
||||
document.createElement('ng-pluralize');
|
||||
document.createElement('ng-view');
|
||||
|
||||
|
||||
// Optionally these for CSS
|
||||
document.createElement('ng:include');
|
||||
document.createElement('ng:pluralize');
|
||||
@@ -79,7 +84,7 @@ To make your Angular application work on IE please make sure that:
|
||||
```
|
||||
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
|
||||
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
|
||||
|
||||
|
||||
|
||||
The **important** parts are:
|
||||
|
||||
@@ -92,7 +97,7 @@ The **important** parts are:
|
||||
happy.
|
||||
|
||||
|
||||
# Long Version
|
||||
## Long Version
|
||||
|
||||
IE has issues with element tag names which are not standard HTML tag names. These fall into two
|
||||
categories, and each category has its own fix.
|
||||
@@ -165,7 +170,7 @@ In IE, the behavior is that the `BODY` element has three children:
|
||||
|
||||
## CSS Styling of Custom Tag Names
|
||||
|
||||
To make CSS selectors work with custom elements, the custom element name must be pre-created with
|
||||
To make CSS selectors work with custom elements, the custom element name must be pre-created with
|
||||
`document.createElement('my-tag')` regardless of XML namespace.
|
||||
|
||||
```html
|
||||
|
||||
@@ -57,6 +57,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||
* **Local Storage and session:** [ngStorage](https://github.com/gsklee/ngStorage)
|
||||
|
||||
## Tools
|
||||
|
||||
@@ -69,10 +70,11 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
|
||||
|
||||
## Deployment
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
@name Migrating from 1.0 to 1.2
|
||||
@description
|
||||
|
||||
# Migrating from 1.0 to 1.2
|
||||
|
||||
AngularJS version 1.2 introduces several breaking changes that may require changes to your
|
||||
application's source code.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
|
||||
AngularJS 1.2 has undergone a thorough security review to make applications safer by default,
|
||||
which has driven many of these changes. Several new features, especially animations, would not
|
||||
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
|
||||
an existing API.
|
||||
@@ -26,7 +27,7 @@ below should still apply, but you may want to consult the
|
||||
<li>{@link guide/migration#ngroute-has-been-moved-into-its-own-module ngRoute has been moved into its own module}</li>
|
||||
<li>{@link guide/migration#templates-no-longer-automatically-unwrap-promises Templates no longer automatically unwrap promises}</li>
|
||||
<li>{@link guide/migration#syntax-for-named-wildcard-parameters-changed-in Syntax for named wildcard parameters changed in <code>$route</code>}</li>
|
||||
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code> or <code>*[ng-src]</code>}</li>
|
||||
<li>{@link guide/migration#you-can-only-bind-one-expression-to You can only bind one expression to <code>*[src]</code>, <code>*[ng-src]</code> or <code>action</code>}</li>
|
||||
<li>{@link guide/migration#interpolations-inside-dom-event-handlers-are-now-disallowed Interpolations inside DOM event handlers are now disallowed}</li>
|
||||
<li>{@link guide/migration#directives-cannot-end-with--start-or--end Directives cannot end with -start or -end}</li>
|
||||
<li>{@link guide/migration#in-$q,-promisealways-has-been-renamed-promisefinally In $q, promise.always has been renamed promise.finally}</li>
|
||||
@@ -43,11 +44,13 @@ below should still apply, but you may want to consult the
|
||||
<li>{@link guide/migration#ngscenario ngScenario}</li>
|
||||
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
|
||||
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
|
||||
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
|
||||
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
|
||||
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
|
||||
<li>{@link guide/migration#services-can-now-return-functions Services can now return functions}</li>
|
||||
<li>{@link guide/migration#modifying-the-dom-outside-digest-cycle Modifying the DOM outside digest cycle}</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -137,17 +140,18 @@ $routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||||
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
|
||||
|
||||
|
||||
## You can only bind one expression to `*[src]` or `*[ng-src]`
|
||||
## You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||||
|
||||
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
|
||||
`src` attribute of elements.
|
||||
`src` or `action` attribute of elements.
|
||||
|
||||
This is one of several improvements to security introduces by Angular 1.2.
|
||||
|
||||
Concatenating expressions makes it hard to understand whether some combination of concatenated
|
||||
values are unsafe to use and potentially subject to XSS vulnerabilities. To simplify the task of
|
||||
auditing for XSS issues, we now require that a single expression be used for `*[src/ng-src]`
|
||||
bindings such as bindings for `iframe[src]`, `object[src]`, etc.
|
||||
bindings such as bindings for `iframe[src]`, `object[src]`, etc. In addition, this requirement is
|
||||
enforced for `form` tags with `action` attributes.
|
||||
|
||||
<table class="table table-bordered code-table">
|
||||
<thead>
|
||||
@@ -493,7 +497,7 @@ See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253
|
||||
|
||||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||||
|
||||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
|
||||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
|
||||
|
||||
|
||||
Directive | Old Priority | New Priority
|
||||
@@ -532,7 +536,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
|
||||
|
||||
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
|
||||
This change sholdn't impact apps that don't contain malicious image links.
|
||||
This change shouldn't impact apps that don't contain malicious image links.
|
||||
|
||||
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
|
||||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||||
@@ -540,9 +544,45 @@ See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d612868
|
||||
|
||||
## Isolate scope only exposed to directives with `scope` property
|
||||
|
||||
Directives without isolate scope do not get the isolate scope from an isolate directive on the
|
||||
same element. If your code depends on this behavior (non-isolate directive needs to access state
|
||||
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly.
|
||||
If you declare a scope option on a directive, that directive will have an
|
||||
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In Angular 1.0, if a
|
||||
directive with an isolate scope is used on an element, all directives on that same element have access
|
||||
to the same isolate scope. For example, say we have the following directives:
|
||||
|
||||
```
|
||||
// This directive declares an isolate scope.
|
||||
.directive('isolateScope', function() {
|
||||
return {
|
||||
scope: {},
|
||||
link: function($scope) {
|
||||
console.log('one = ' + $scope.$id);
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
// This directive does not.
|
||||
.directive('nonIsolateScope', function() {
|
||||
return {
|
||||
link: function($scope) {
|
||||
console.log('two = ' + $scope.$id);
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
Now what happens if we use both directives on the same element?
|
||||
|
||||
```
|
||||
<div isolate-scope non-isolate-scope></div>
|
||||
```
|
||||
|
||||
In Angular 1.0, the nonIsolateScope directive will have access to the isolateScope directive’s scope. The
|
||||
log statements will print the same id, because the scope is the same. But in Angular 1.2, the nonIsolateScope
|
||||
will not use the same scope as isolateScope. Instead, it will inherit the parent scope. The log statements
|
||||
will print different id’s.
|
||||
|
||||
If your code depends on the Angular 1.0 behavior (non-isolate directive needs to access state
|
||||
from within the isolate scope), change the isolate directive to use scope locals to pass these explicitly:
|
||||
|
||||
**Before**
|
||||
|
||||
@@ -613,7 +653,7 @@ controller.) That's easier said that done for two reasons:
|
||||
someone on the scope chain for JavaScript use, you also expose it to
|
||||
Angular expressions
|
||||
2. The new `controller as` syntax that's now in increased usage exposes the
|
||||
entire controller on the scope chain greatly increaing the exposed surface.
|
||||
entire controller on the scope chain greatly increasing the exposed surface.
|
||||
|
||||
Though Angular expressions are written and controlled by the developer, they:
|
||||
|
||||
@@ -653,3 +693,39 @@ load and use your copy of the locale file provided that you maintain it yourself
|
||||
|
||||
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
|
||||
|
||||
## Services can now return functions
|
||||
|
||||
Previously, the service constructor only returned objects regardless of whether a function was returned.
|
||||
|
||||
Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
|
||||
`new` operator and allows functions to be returned as a service.
|
||||
|
||||
If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
|
||||
Make sure your services return the correct type wanted.
|
||||
|
||||
**Coffeescript example**
|
||||
|
||||
```
|
||||
myApp.service 'applicationSrvc', ->
|
||||
@something = "value"
|
||||
@someFunct = ->
|
||||
"something else"
|
||||
```
|
||||
|
||||
pre 1.2 this service would return the whole object as the service.
|
||||
|
||||
post 1.2 this service returns `someFunct` as the value of the service
|
||||
|
||||
you would need to change this services to
|
||||
|
||||
```
|
||||
myApp.service 'applicationSrvc', ->
|
||||
@something = "value"
|
||||
@someFunct = ->
|
||||
"something else"
|
||||
return
|
||||
```
|
||||
|
||||
to continue to return the complete instance.
|
||||
|
||||
See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
|
||||
|
||||
@@ -66,8 +66,9 @@ that you break your application to multiple modules like this:
|
||||
* And an application level module which depends on the above modules and contains any
|
||||
initialization code.
|
||||
|
||||
We've also written a document on how we organize large apps at Google and on how to write
|
||||
reusable components.
|
||||
We've also
|
||||
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
|
||||
on how we organize large apps at Google.
|
||||
|
||||
The above is a suggestion. Tailor it to your needs.
|
||||
|
||||
@@ -172,7 +173,7 @@ angular.module('myModule', []).
|
||||
|
||||
<div class="alert alert-info">
|
||||
When bootstrapping, first Angular applies all constant definitions.
|
||||
Then Angular applies configuration blocks in the order same order they were registered.
|
||||
Then Angular applies configuration blocks in the same order they were registered.
|
||||
</div>
|
||||
|
||||
## Run Blocks
|
||||
|
||||
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
|
||||
## Service Recipe
|
||||
|
||||
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
|
||||
could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
|
||||
could launch a unicorn into space via our `unicornLauncher` service that is an instance of
|
||||
custom type:
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -388,7 +388,7 @@ user enters text into the text field.
|
||||
1. the {@link ng.directive:ngModel ng-model} and {@link
|
||||
ng.directive:input input} {@link guide/directive
|
||||
directive} set up a `keydown` listener on the `<input>` control.
|
||||
2. the {@link ng.$interpolate {{name}} } interpolation
|
||||
2. the {@link ng.$interpolate interpolation}
|
||||
sets up a {@link ng.$rootScope.Scope#$watch $watch} to be notified of
|
||||
`name` changes.
|
||||
2. During the runtime phase:
|
||||
@@ -400,8 +400,8 @@ user enters text into the text field.
|
||||
3. Angular applies the `name = 'X';` to the model.
|
||||
4. The {@link ng.$rootScope.Scope#$digest $digest} loop begins
|
||||
5. The {@link ng.$rootScope.Scope#$watch $watch} list detects a change
|
||||
on the `name` property and notifies the {@link ng.$interpolate
|
||||
{{name}} } interpolation, which in turn updates the DOM.
|
||||
on the `name` property and notifies the {@link ng.$interpolate interpolation},
|
||||
which in turn updates the DOM.
|
||||
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
|
||||
@@ -11,15 +11,15 @@ Angular services are:
|
||||
|
||||
* Lazily instantiated – Angular only instantiates a service when an application component depends
|
||||
on it.
|
||||
* Singletons – Each component is dependent on a service gets a reference to the single instance
|
||||
* Singletons – Each component dependent on a service gets a reference to the single instance
|
||||
generated by the service factory.
|
||||
|
||||
Angular offers several useful services (like {@link ng.$http `$http`}) but for most applications
|
||||
Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
|
||||
you'll also want to {@link services#creating-services create your own}.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Note:** Like other core Angular identifiers built-in services always start with `$`
|
||||
(i.e. `$http`).
|
||||
(e.g. `$http`).
|
||||
</div>
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ injection of `$window`, `$scope`, and our `notify` service:
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<div class="alert alert-error">
|
||||
<div class="alert alert-danger">
|
||||
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
|
||||
your variable names will get renamed unless you use one of the annotation techniques above.
|
||||
</div>
|
||||
@@ -215,8 +215,8 @@ In the example, note that:
|
||||
{@link ng.$log `$log`} services.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
|
||||
service and our custom `batchLog` service.
|
||||
* Both services use the and array notation to declare their dependencies.
|
||||
* That the order of identifiers in the array is the same as the order of argument
|
||||
* Both services use the array notation to declare their dependencies.
|
||||
* The order of identifiers in the array is the same as the order of argument
|
||||
names in the factory function.
|
||||
|
||||
### Registering a Service with `$provide`
|
||||
@@ -234,7 +234,7 @@ angular.module('myModule', []).config(function($provide) {
|
||||
});
|
||||
```
|
||||
|
||||
This is technique is often used in unit tests to mock out a service's dependencies.
|
||||
This technique is often used in unit tests to mock out a service's dependencies.
|
||||
|
||||
|
||||
## Unit Testing
|
||||
|
||||
@@ -258,7 +258,7 @@ expect($scope.strength).toEqual('weak');
|
||||
```
|
||||
|
||||
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.
|
||||
that such a test tells a story, rather than asserting random bits which don't seem to be related.
|
||||
|
||||
## Filters
|
||||
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
|
||||
@@ -337,6 +337,12 @@ We inject the $compile service and $rootScope before each jasmine test. The $com
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame, 2 times" is present.
|
||||
|
||||
### Testing Directives With External Templates
|
||||
|
||||
If your directive uses `templateUrl`, consider using
|
||||
[karma-ng-html2js-preprocessor](https://github.com/karma-runner/karma-ng-html2js-preprocessor)
|
||||
to pre-compile HTML templates and thus avoid having to load them over HTTP during test execution.
|
||||
Otherwise you may run into issues if the test directory hierarchy differs from the application's.
|
||||
|
||||
## Sample project
|
||||
See the [angular-seed](https://github.com/angular/angular-seed) project for an example.
|
||||
|
||||
@@ -11,12 +11,12 @@ See the [contributing guidelines](https://github.com/angular/angular.js/blob/mas
|
||||
for how to contribute your own code to AngularJS.
|
||||
|
||||
|
||||
1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
|
||||
2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
|
||||
3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
|
||||
4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
|
||||
5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
|
||||
6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
|
||||
1. {@link misc/contribute#installing-dependencies Installing Dependencies}
|
||||
2. {@link misc/contribute#forking-angular-on-github Forking Angular on Github}
|
||||
3. {@link misc/contribute#building-angularjs Building AngularJS}
|
||||
4. {@link misc/contribute#running-a-local-development-web-server Running a Local Development Web Server}
|
||||
5. {@link misc/contribute#running-the-unit-test-suite Running the Unit Test Suite}
|
||||
6. {@link misc/contribute#running-the-end-to-end-test-suite Running the End-to-end Test Suite}
|
||||
|
||||
## Installing Dependencies
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ The size of the file is < 36KB compressed and minified.
|
||||
|
||||
### Can I use the open-source Closure Library with Angular?
|
||||
|
||||
Yes, you can use widgets from the [Closure Library](http://code.google.com/closure/library)
|
||||
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
|
||||
in Angular.
|
||||
|
||||
### Does Angular use the jQuery library?
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
@ngdoc overview
|
||||
@name Miscellaneous
|
||||
@description
|
||||
|
||||
# Miscellaneous Links
|
||||
|
||||
- {@link misc/started Getting Started}
|
||||
- {@link misc/downloading Downloading AngularJS}
|
||||
- {@link misc/faq Frequently Asked Questions}
|
||||
- {@link misc/contribute Building AngularJS}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@name Getting Started
|
||||
@description
|
||||
|
||||
# Getting Started
|
||||
|
||||
We want you to have an easy time while starting to use Angular. We've put together the following steps on your path to
|
||||
becoming an Angular expert.
|
||||
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
@step -1
|
||||
@description
|
||||
|
||||
# PhoneCat Tutorial App
|
||||
|
||||
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
|
||||
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
|
||||
of Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413" alt="demo
|
||||
application running in the browser">
|
||||
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
|
||||
or plug-ins. As you work through the tutorial, you will:
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of native
|
||||
extensions or plug-ins. As you work through the tutorial, you will:
|
||||
|
||||
* See examples of how to use client-side data binding and dependency injection to build dynamic
|
||||
views of data that change immediately in response to user actions.
|
||||
@@ -21,15 +23,13 @@ views of data that change immediately in response to user actions.
|
||||
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
|
||||
easier.
|
||||
|
||||
And all of this works in any browser without modification to the browser!
|
||||
|
||||
When you finish the tutorial you will be able to:
|
||||
|
||||
* Create a dynamic application that works in any browser.
|
||||
* Create a dynamic application that works in all modern browsers.
|
||||
* Define the differences between Angular and common JavaScript frameworks.
|
||||
* Understand how data binding works in AngularJS.
|
||||
* Use the angular-seed project to quickly boot-strap your own projects.
|
||||
* Create and run tests.
|
||||
* Create and run unit tests.
|
||||
* Create and run end to end tests.
|
||||
* Identify resources for learning more about AngularJS.
|
||||
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
@@ -42,78 +42,152 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Working with the code
|
||||
|
||||
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
|
||||
environment. The tutorial relies on the use of Git versioning system for source code management.
|
||||
You can follow along with this tutorial and hack on the code in either the Mac/Linux or the Windows
|
||||
environment. The tutorial relies on the use of the [Git][git] versioning system for source code
|
||||
management.
|
||||
|
||||
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
|
||||
and follow the instructions for setting up your computer.
|
||||
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You'll need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at
|
||||
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions, from now on, assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p></li>
|
||||
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p></li>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||
don't have it already:</p>
|
||||
<pre>
|
||||
npm install
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
</li>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from Git bash.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
## Install Git
|
||||
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor
|
||||
installed. Now, let's get some cool stuff done!
|
||||
You'll need Git, which you can get from [the Git site][git].
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
Clone the [angular-phonecat repository][angular-phonecat] located at GitHub by running the following
|
||||
command:
|
||||
|
||||
```
|
||||
git clone https://github.com/angular/angular-phonecat.git
|
||||
```
|
||||
|
||||
This command creates the `angular-phonecat` directory in your current directory.
|
||||
|
||||
Change your current directory to `angular-phonecat`.
|
||||
|
||||
```
|
||||
cd angular-phonecat
|
||||
```
|
||||
|
||||
The tutorial instructions, from now on, assume you are running all commands from the
|
||||
`angular-phonecat` directory.
|
||||
|
||||
|
||||
## Install Node.js
|
||||
|
||||
If you want to run the built-in web-server and the test tools then you will also need
|
||||
Node.js v0.10, or later.
|
||||
|
||||
You can download Node.js from the [node.js website][node].
|
||||
|
||||
|
||||
You can check the version of Node.js that you have installed by running the following command:
|
||||
|
||||
```
|
||||
node --version
|
||||
```
|
||||
|
||||
<div class="alert alert-info"><strong>Helpful note:<strong> If you need to run a different versions of node.js
|
||||
in your local environment, consider installing
|
||||
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
|
||||
Node Version Manager (nvm)
|
||||
</a>.
|
||||
</div>
|
||||
|
||||
Once you have Node.js installed you can install the tool dependencies by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
This command will download the following tools, into the `node_modules` directive:
|
||||
|
||||
- [Bower][bower] - client-side code package manager
|
||||
- [http-server][http-server] - simple local static web server
|
||||
- [Karma][karma] - unit test runner
|
||||
- [protractor][protractor] - end 2 end test runner
|
||||
|
||||
Running `npm install` will also automatically run `bower install`, which will download the Angular
|
||||
framework into the `bower_components` directory.
|
||||
|
||||
The project is preconfigured with a number of npm helper scripts to make it easy to run the common
|
||||
tasks that you will need while developing.
|
||||
|
||||
## Running Development Web Server
|
||||
|
||||
The project is preconfigured to provide a simple static web server for hosting the application.
|
||||
Start the web server by running:
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
Now you can browse to the application at:
|
||||
|
||||
```
|
||||
http://localhost:8000/app/index.html
|
||||
```
|
||||
|
||||
## Running Unit Tests
|
||||
|
||||
The project is preconfigured to use [Karma][karma] to run the unit tests for the application. Start
|
||||
Karma by running:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
This will start the Karma unit test runner, open up a Chrome browser and execute all the unit tests
|
||||
in this browser. Karma will then sit and watch all the source and test JavaScript files.
|
||||
Whenever one of these files changes Karma re-runs all the unit tests.
|
||||
|
||||
It is good to leave this running all the time as you will get immediate feedback from Karma as you
|
||||
are working on the code.
|
||||
|
||||
|
||||
## Running End to End Tests
|
||||
|
||||
The project is preconfigured to use [Protractor][protractor] to run the end to end tests for the
|
||||
application. Set up the binaries protractor needs to run by running:
|
||||
|
||||
```
|
||||
npm run update-webdriver
|
||||
```
|
||||
(You will only need to do this once) Execute the Protractor test scripts against your application
|
||||
by running:
|
||||
|
||||
```
|
||||
npm run protractor
|
||||
```
|
||||
|
||||
This will start the Protractor end to end test runner, open up a Chrome browser and execute all the
|
||||
end to end test scripts in this browser. Once the test scripts finish, the browser will close down
|
||||
and Protractor will exit.
|
||||
|
||||
It is good to run the end to end tests whenever you make changes to the HTML views or want to check
|
||||
that the application as a whole is executing correctly.
|
||||
|
||||
<div class="alert alert-info"><strong>Helpful note:</strong> Protractor requires that a web server is running
|
||||
and serving up the application at the default URL: `http://localhost:8000/app/index.html`. You can
|
||||
start the provided development server by running `npm start`.
|
||||
</div>
|
||||
|
||||
|
||||
# Get Started
|
||||
|
||||
Now your environment is ready, it is time to get started developing the Angular PhoneCat
|
||||
application.
|
||||
|
||||
<a href="tutorial/step_00" title="Next Step"><span class="btn btn-primary">Step 0 - Bootstrapping</span></a>
|
||||
|
||||
|
||||
|
||||
[git]: http://git-scm.com/download
|
||||
[angular-phonecat]: https://github.com/angular/angular-phonecat
|
||||
[node]: http://nodejs.org/
|
||||
[protractor]: https://github.com/angular/protractor
|
||||
[bower]: http://bower.io/
|
||||
[http-server]: https://github.com/nodeapps/http-server
|
||||
[karma]: https://github.com/karma-runner/karma
|
||||
@@ -25,7 +25,7 @@ angular-seed, and run the application in the browser.
|
||||
<ul>
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||
<li>In a <i>separate</i> terminal tab or window, run <code>npm start</code> to start the web server.</li>
|
||||
<li>Open a browser window for the app and navigate to <a
|
||||
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||
</ol>
|
||||
|
||||
@@ -183,7 +183,7 @@ is available to be injected.
|
||||
### Writing and Running Tests
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://pivotal.github.com/jasmine/) and at the [Jasmine docs](http://pivotal.github.io/jasmine/).
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://jasmine.github.io/) and at the [Jasmine docs](http://jasmine.github.io/).
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
|
||||
You can do this by issuing `npm install` into your terminal.
|
||||
@@ -192,8 +192,8 @@ You can do this by issuing `npm install` into your terminal.
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
`npm test` to start the Karma server (the config file necessary to start the server is
|
||||
located at `./test/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
@@ -226,6 +226,10 @@ To run the test, do the following:
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Update the unit test for the controller in ./tests/unit/controllersSpec.js to reflect the previous change. For example by adding:
|
||||
|
||||
expect(scope.name).toBe('World');
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
<table>
|
||||
|
||||
@@ -96,18 +96,23 @@ describe('PhoneCat App', function() {
|
||||
describe('Phone list view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html');
|
||||
browser.get('app/index.html');
|
||||
});
|
||||
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(repeater('.phones li').count()).toBe(3);
|
||||
|
||||
input('query').enter('nexus');
|
||||
expect(repeater('.phones li').count()).toBe(1);
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
input('query').enter('motorola');
|
||||
expect(repeater('.phones li').count()).toBe(2);
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
query.sendKeys('motorola');
|
||||
expect(phoneList.count()).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -117,20 +122,14 @@ Even though the syntax of this test looks very much like our controller unit tes
|
||||
Jasmine, the end-to-end test uses APIs of {@link guide/dev_guide.e2e-testing Angular's end-to-end
|
||||
test runner}.
|
||||
|
||||
To run the end-to-end test, open one of the following in a new browser tab:
|
||||
Much like Karma is the test runner for unit tests, we use Protractor to run end-to-end tests.
|
||||
Try it with `npm run protractor`. End-to-end tests are slow, so unlike with unit tests, Protractor
|
||||
will exit after the test run and will not automatically rerun the test suite on every file change.
|
||||
To rerun the test suite, execute `npm run protractor` again.
|
||||
|
||||
* node.js users: http://localhost:8000/test/e2e/runner.html
|
||||
* users with other http servers:
|
||||
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
|
||||
* casual reader: http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html
|
||||
|
||||
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
|
||||
end-to-end tests! Use `./scripts/e2e-test.sh` (if you are on Windows, run `scripts\e2e-test.bat`) script for that. End-to-end tests are slow, so unlike
|
||||
with unit tests, Karma will exit after the test run and will not automatically rerun the test
|
||||
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` or `e2e-test.bat` script again.
|
||||
|
||||
Note: You must ensure you've installed the karma-ng-scenario framework plugin prior to running the
|
||||
`e2e-test.sh` script. You can do this by issuing `npm install` into your terminal.
|
||||
Note: You must ensure you've installed the protractor and updated webdriver prior to running the
|
||||
`npm run protractor`. You can do this by issuing `npm install` and `npm run update-webdriver` into
|
||||
your terminal.
|
||||
|
||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
|
||||
@@ -173,28 +172,24 @@ ngBindTemplate} directives, which are invisible to the user while the page is lo
|
||||
```js
|
||||
it('should display the current filter value within an element with id "status"',
|
||||
function() {
|
||||
expect(element('#status').text()).toMatch(/Current filter: \s*$/);
|
||||
var statusElement = element(by.id('status'));
|
||||
expect(statusElement.getText()).toMatch(/Current filter: \s*$/);
|
||||
|
||||
input('query').enter('nexus');
|
||||
element(by.model('query')).sendKeys('nexus');
|
||||
|
||||
expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);
|
||||
expect(statusElement.getText()).toMatch(/Current filter: nexus\s*$/);
|
||||
|
||||
//alternative version of the last assertion that tests just the value of the binding
|
||||
using('#status').expect(binding('query')).toBe('nexus');
|
||||
expect(statusElement.element(by.binding('query'))).toBe('nexus');
|
||||
});
|
||||
```
|
||||
|
||||
Refresh the browser tab with the end-to-end test runner to see the test fail. To make the test
|
||||
pass, edit the `index.html` template to add a `div` or `p` element with `id` `"status"` and content
|
||||
with the `query` binding, prefixed by "Current filter:". For instance:
|
||||
Re-run `npm run protractor` to see the test fail. To make the test pass, edit the `index.html`
|
||||
template to add a `div` or `p` element with `id` `"status"` and content with the `query` binding,
|
||||
prefixed by "Current filter:". For instance:
|
||||
|
||||
<div id="status">Current filter: {{query}}</div>
|
||||
|
||||
* Add a `pause()` statement inside of an end-to-end test and rerun it. You'll see the runner pause;
|
||||
this gives you the opportunity to explore the state of your application while it is displayed in
|
||||
the browser. The app is live! You can change the search query to prove it. Notice how useful this
|
||||
is for troubleshooting end-to-end tests.
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
@@ -117,7 +117,7 @@ describe('PhoneCat controllers', function() {
|
||||
var scope, ctrl;
|
||||
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
scope = {};
|
||||
ctrl = $controller('PhoneListCtrl', {$scope:scope});
|
||||
@@ -152,28 +152,36 @@ __`test/e2e/scenarios.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
it('should be possible to control phone order via the drop down select box',
|
||||
function() {
|
||||
//let's narrow the dataset to make the test assertions shorter
|
||||
input('query').enter('tablet');
|
||||
it('should be possible to control phone order via the drop down select box', function() {
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('phone.name')).
|
||||
toEqual(["Motorola XOOM\u2122 with Wi-Fi",
|
||||
"MOTOROLA XOOM\u2122"]);
|
||||
var phoneNameColumn = element.all(by.repeater('phone in phones').column('{{phone.name}}'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
select('orderProp').option('Alphabetical');
|
||||
function getNames() {
|
||||
return phoneNameColumn.map(function(elm) {
|
||||
return elm.getText();
|
||||
});
|
||||
}
|
||||
|
||||
expect(repeater('.phones li', 'Phone List').column('phone.name')).
|
||||
toEqual(["MOTOROLA XOOM\u2122",
|
||||
"Motorola XOOM\u2122 with Wi-Fi"]);
|
||||
});
|
||||
...
|
||||
query.sendKeys('tablet'); //let's narrow the dataset to make the test assertions shorter
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
"Motorola XOOM\u2122 with Wi-Fi",
|
||||
"MOTOROLA XOOM\u2122"
|
||||
]);
|
||||
|
||||
element(by.model('orderProp')).findElement(by.css('option[value="name"]')).click();
|
||||
|
||||
expect(getNames()).toEqual([
|
||||
"MOTOROLA XOOM\u2122",
|
||||
"Motorola XOOM\u2122 with Wi-Fi"
|
||||
]);
|
||||
});...
|
||||
```
|
||||
|
||||
The end-to-end test verifies that the ordering mechanism of the select box is working correctly.
|
||||
|
||||
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
|
||||
`runner.html` to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-4/test/e2e/runner.html).
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
|
||||
from our server using one of Angular's built-in {@link guide/dev_guide.services services} called {@link
|
||||
from our server using one of Angular's built-in {@link guide/services services} called {@link
|
||||
ng.$http $http}. We will use Angular's {@link guide/di dependency
|
||||
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
@@ -20,7 +20,6 @@ You should now see a list of 20 phones.
|
||||
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-4...step-5):
|
||||
|
||||
## Data
|
||||
|
||||
The `app/phones/phones.json` file in your project is a dataset that contains a larger list of phones
|
||||
stored in the JSON format.
|
||||
|
||||
@@ -44,7 +43,7 @@ Following is a sample of the file:
|
||||
|
||||
We'll use Angular's {@link ng.$http $http} service in our controller to make an HTTP
|
||||
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
|
||||
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
|
||||
one of several built-in {@link guide/services Angular services} that handle common operations
|
||||
in web apps. Angular injects these services for you where you need them.
|
||||
|
||||
Services are managed by Angular's {@link guide/di DI subsystem}. Dependency injection
|
||||
@@ -74,10 +73,10 @@ tutorial.)
|
||||
|
||||
The `$http` service returns a {@link ng.$q promise object} with a `success`
|
||||
method. We call this method to handle the asynchronous response and assign the phone data to the
|
||||
scope controlled by this controller, as a model called `phones`. Notice that angular detected the
|
||||
scope controlled by this controller, as a model called `phones`. Notice that Angular detected the
|
||||
json response and parsed it for us!
|
||||
|
||||
To use a service in angular, you simply declare the names of the dependencies you need as arguments
|
||||
To use a service in Angular, you simply declare the names of the dependencies you need as arguments
|
||||
to the controller's constructor function, as follows:
|
||||
|
||||
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
|
||||
@@ -96,7 +95,7 @@ dependencies.
|
||||
### `$` Prefix Naming Convention
|
||||
|
||||
You can create your own services, and in fact we will do exactly that in step 11. As a naming
|
||||
convention, angular's built-in services, Scope methods and a few other Angular APIs have a `$`
|
||||
convention, Angular's built-in services, Scope methods and a few other Angular APIs have a `$`
|
||||
prefix in front of the name.
|
||||
|
||||
The `$` prefix is there to namespace Angular-provided services.
|
||||
@@ -167,7 +166,7 @@ __`test/unit/controllersSpec.js`:__
|
||||
Because we started using dependency injection and our controller has dependencies, constructing the
|
||||
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
|
||||
constructor with some kind of fake `$http` implementation. However, the recommended (and easier) way
|
||||
is to create a controller in the test environment in the same way that angular does it in the
|
||||
is to create a controller in the test environment in the same way that Angular does it in the
|
||||
production code behind the scenes, as follows:
|
||||
|
||||
```js
|
||||
@@ -269,7 +268,7 @@ to the first 5 in the list. Use the following code in the `$http` callback:
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
|
||||
Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency
|
||||
injection), go to {@link step_06 step 6}, where you will add some
|
||||
thumbnail images of phones and some links.
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ the element attribute.
|
||||
|
||||
We also added phone images next to each record using an image tag with the {@link
|
||||
ng.directive:ngSrc ngSrc} directive. That directive prevents the
|
||||
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
|
||||
browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
|
||||
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
|
||||
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
|
||||
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
|
||||
@@ -76,9 +76,12 @@ __`test/e2e/scenarios.js`__:
|
||||
```js
|
||||
...
|
||||
it('should render phone specific links', function() {
|
||||
input('query').enter('nexus');
|
||||
element('.phones li a').click();
|
||||
expect(browser().location().url()).toBe('/phones/nexus-s');
|
||||
var query = element(by.model('query'));
|
||||
query.sendKeys('nexus');
|
||||
element(by.css('.phones li a')).click();
|
||||
browser.getLocationAbsUrl().then(function(url) {
|
||||
expect(url.split('#')[1]).toBe('/phones/nexus-s');
|
||||
});
|
||||
});
|
||||
...
|
||||
```
|
||||
@@ -86,8 +89,7 @@ __`test/e2e/scenarios.js`__:
|
||||
We added a new end-to-end test to verify that the app is generating correct links to the phone
|
||||
views that we will implement in the upcoming steps.
|
||||
|
||||
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
|
||||
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-6/test/e2e/runner.html).
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -114,7 +114,7 @@ Our application routes are defined as follows:
|
||||
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
|
||||
|
||||
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
|
||||
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
|
||||
`:phoneId` is a variable part of the URL. To construct the phone details view, Angular will use the
|
||||
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
|
||||
|
||||
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
|
||||
@@ -267,22 +267,21 @@ to various URLs and verify that the correct view was rendered.
|
||||
});
|
||||
...
|
||||
|
||||
describe('Phone detail view', function() {
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('app/index.html#/phones/nexus-s');
|
||||
browser.get('app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
it('should display placeholder page with phoneId', function() {
|
||||
expect(binding('phoneId')).toBe('nexus-s');
|
||||
expect(element(by.binding('phoneId')).getText()).toBe('nexus-s');
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
|
||||
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-7/test/e2e/runner.html).
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -79,7 +79,7 @@ route by the `$route` service.
|
||||
## Template
|
||||
|
||||
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
|
||||
Note where we use the angular `{{expression}}` markup and `ngRepeat` to project phone data from
|
||||
Note where we use the Angular `{{expression}}` markup and `ngRepeat` to project phone data from
|
||||
our model into the view.
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ __`app/partials/phone-detail.html`:__
|
||||
</dl>
|
||||
</li>
|
||||
...
|
||||
</li>
|
||||
<li>
|
||||
<span>Additional Features</span>
|
||||
<dd>{{phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
@@ -166,20 +166,19 @@ __`test/e2e/scenarios.js`:__
|
||||
describe('Phone detail view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
||||
browser.get('app/index.html#/phones/nexus-s');
|
||||
});
|
||||
|
||||
|
||||
it('should display nexus-s page', function() {
|
||||
expect(binding('phone.name')).toBe('Nexus S');
|
||||
expect(element(by.binding('phone.name')).getText()).toBe('Nexus S');
|
||||
});
|
||||
});
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
|
||||
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html).
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -40,13 +40,13 @@ The name of our filter is "checkmark". The `input` evaluates to either `true` or
|
||||
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
|
||||
|
||||
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
|
||||
our main `phonecat` module.
|
||||
our main `phonecatApp` module.
|
||||
|
||||
__`app/js/app.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
angular.module('phonecatApp', ['phonecatFilters']).
|
||||
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']).
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
@@ -90,23 +90,22 @@ __`test/e2e/scenarios.js`:__
|
||||
...
|
||||
|
||||
it('should display the first phone image as the main phone image', function() {
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
|
||||
|
||||
it('should swap main image if a thumbnail image is clicked on', function() {
|
||||
element('.phone-thumbs li:nth-child(3) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg');
|
||||
element(by.css('.phone-thumbs li:nth-child(3) img')).click();
|
||||
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
|
||||
|
||||
element('.phone-thumbs li:nth-child(1) img').click();
|
||||
expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg');
|
||||
element(by.css('.phone-thumbs li:nth-child(1) img')).click();
|
||||
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
You can now rerun `./scripts/e2e-test.sh` or refresh the browser tab with the end-to-end test
|
||||
runner to see the tests run, or you can see them running on [Angular's server](http://angular.github.com/angular-phonecat/step-10/test/e2e/runner.html).
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ The most important changes are listed below. You can see the full diff on [GitHu
|
||||
## Template
|
||||
|
||||
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
|
||||
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
|
||||
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
|
||||
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
|
||||
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
|
||||
service, that we'll soon use:
|
||||
|
||||
__`app/index.html`.__
|
||||
|
||||
@@ -20,7 +20,8 @@ a dependency with the application module, will enable animations throughout the
|
||||
|
||||
Common `ng` directives automatically trigger hooks for animations to tap into. When an animation is found
|
||||
then the animation will run in between the standard DOM operation that is being issued on the element at
|
||||
the given time (e.g. inserting and removing nodes on ngRepeat or adding and removing classes on ngClass).
|
||||
the given time (e.g. inserting and removing nodes on {@link api/ng.directive:ngRepeat `ngRepeat`} or adding
|
||||
and removing classes on {@link api/ng.directive:ngClass `ngClass`}).
|
||||
|
||||
The most important changes are listed below. You can see the full diff on
|
||||
[GitHub](https://github.com/angular/angular-phonecat/compare/step-11...step-12):
|
||||
@@ -34,9 +35,10 @@ To get an idea of how animations work with AngularJS, please read the
|
||||
|
||||
## Template
|
||||
|
||||
The changes required within the HTML template code is to link the asset files which define the animations as well
|
||||
as the `angular-animate.js` file. The animation module, known as `ngAnimate`, is defined within
|
||||
`angular-animate.js` and contains the code necessary to make your application become animation aware.
|
||||
The changes required within the HTML template code is to link the asset files which define the animations as
|
||||
well as the `angular-animate.js` file. The animation module, known as {@link api/ngAnimate `ngAnimate`}, is
|
||||
defined within `angular-animate.js` and contains the code necessary to make your application become animation
|
||||
aware.
|
||||
|
||||
Here's what needs to changed in the index file:
|
||||
|
||||
@@ -83,7 +85,7 @@ __`app/js/app.js`.__
|
||||
|
||||
```js
|
||||
// ...
|
||||
angular.module('phonecat', [
|
||||
angular.module('phonecatApp', [
|
||||
'ngRoute',
|
||||
|
||||
'phonecatAnimations',
|
||||
@@ -197,7 +199,7 @@ which are described in detail below.
|
||||
|
||||
## Animating `ngView` with CSS Keyframe Animations
|
||||
|
||||
Next let's add an animation for transitions between route changes in `ngView`.
|
||||
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
|
||||
|
||||
To start, let's add a new CSS class to our HTML like we did in the example above.
|
||||
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
|
||||
|
||||
+13
-11
@@ -1,13 +1,12 @@
|
||||
var path = require('canonical-path');
|
||||
var gruntUtils = require('../lib/grunt/utils');
|
||||
var versionInfo = require('../lib/versions/version-info');
|
||||
var basePath = __dirname;
|
||||
|
||||
var basePackage = require('./config');
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
var version = gruntUtils.getVersion();
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + version.cdn;
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
|
||||
|
||||
var getVersion = function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = sourceFolder || '../bower_components';
|
||||
@@ -25,9 +24,12 @@ module.exports = function(config) {
|
||||
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
|
||||
]);
|
||||
|
||||
config.set('processing.stopOnError', true);
|
||||
|
||||
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
|
||||
|
||||
config.set('rendering.outputFolder', '../build/docs');
|
||||
config.set('rendering.contentsFolder', 'partials');
|
||||
|
||||
config.set('logging.level', 'info');
|
||||
|
||||
@@ -38,7 +40,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.js' ]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.js',
|
||||
@@ -60,7 +62,7 @@ module.exports = function(config) {
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.css',
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
@@ -73,7 +75,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.min.js' ]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.min.js',
|
||||
@@ -95,7 +97,7 @@ module.exports = function(config) {
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
@@ -111,7 +113,7 @@ module.exports = function(config) {
|
||||
'../../../angular.js'
|
||||
]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
@@ -134,7 +136,7 @@ module.exports = function(config) {
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
@@ -147,7 +149,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ cdnUrl + '/angular.min.js' ]
|
||||
},
|
||||
dependencyPath: cdnUrl
|
||||
dependencyPath: cdnUrl + '/'
|
||||
},
|
||||
scripts: [
|
||||
cdnUrl + '/angular.min.js',
|
||||
@@ -169,7 +171,7 @@ module.exports = function(config) {
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/dist/css/bootstrap.min.css',
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
|
||||
+7
-3
@@ -38,18 +38,22 @@ gulp.task('build-app', function() {
|
||||
gulp.task('assets', ['bower'], function() {
|
||||
return merge(
|
||||
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
|
||||
copyComponent('bootstrap'),
|
||||
copyComponent('bootstrap', '/dist/**/*'),
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr.js','/*.js'),
|
||||
copyComponent('google-code-prettify'),
|
||||
copyComponent('jquery'),
|
||||
copyComponent('jquery', '/jquery.*'),
|
||||
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('doc-gen', function() {
|
||||
return docGenerator('docs.config.js').generateDocs();
|
||||
return docGenerator('docs.config.js')
|
||||
.generateDocs()
|
||||
.catch(function(error) {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// JSHint the example and protractor test files
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
|
||||
x="0" y="0" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -33,7 +33,7 @@ describe("convertDatetimeData", function() {
|
||||
AMPMS: ['AM', 'PM'],
|
||||
DATEFORMATS: ['a', 'b', 'c', 'd'],
|
||||
TIMEFORMATS: ['e', 'f', 'g', 'h'] };
|
||||
|
||||
|
||||
it('should convert empty datetime obj', function() {
|
||||
var processedData = convert(dataObj);
|
||||
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ set -xe
|
||||
# Define reasonable set of browsers in case we are running manually from commandline
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
|
||||
fi
|
||||
|
||||
# CLEAN #
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = function(grunt) {
|
||||
|
||||
|
||||
grunt.registerTask('docs', 'create angular docs', function(){
|
||||
var gruntProc = shelljs.exec('node_modules/gulp/bin/gulp.js --gulpfile docs/gulpfile.js');
|
||||
var gruntProc = shelljs.exec('"node_modules/.bin/gulp" --gulpfile docs/gulpfile.js');
|
||||
if (gruntProc.code !== 0) {
|
||||
throw new Error('doc generation failed');
|
||||
}
|
||||
|
||||
+6
-176
@@ -4,8 +4,9 @@ var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var semver = require('semver');
|
||||
|
||||
var _ = require('lodash');
|
||||
var version, pkg;
|
||||
|
||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||
|
||||
var PORT_MIN = 8000;
|
||||
@@ -23,23 +24,6 @@ var getRandomPorts = function() {
|
||||
];
|
||||
};
|
||||
|
||||
var getPackage = function() {
|
||||
if ( !pkg ) {
|
||||
|
||||
// Search up the folder hierarchy for the first package.json
|
||||
var packageFolder = path.resolve('.');
|
||||
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
|
||||
var parent = path.dirname(packageFolder);
|
||||
if ( parent === packageFolder) { break; }
|
||||
packageFolder = parent;
|
||||
}
|
||||
pkg = JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
|
||||
|
||||
}
|
||||
|
||||
return pkg;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -50,160 +34,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
getGitRepoInfo: function() {
|
||||
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
|
||||
var match = GITURL_REGEX.exec(getPackage().repository.url);
|
||||
var git = {
|
||||
owner: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
return git;
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
try {
|
||||
|
||||
var gitTag = getTagOfCurrentCommit();
|
||||
var semVerVersion, codeName, fullVersion;
|
||||
if (gitTag) {
|
||||
// tagged release
|
||||
fullVersion = semVerVersion = semver.valid(gitTag);
|
||||
codeName = getTaggedReleaseCodeName(gitTag);
|
||||
} else {
|
||||
// snapshot release
|
||||
semVerVersion = getSnapshotVersion();
|
||||
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
|
||||
codeName = 'snapshot';
|
||||
}
|
||||
|
||||
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: versionParts[1],
|
||||
minor: versionParts[2],
|
||||
dot: versionParts[3],
|
||||
codename: codeName,
|
||||
cdn: getPackage().cdnVersion
|
||||
};
|
||||
|
||||
// Stable versions have an even minor version
|
||||
version.isStable = version.minor%2 === 0;
|
||||
|
||||
return version;
|
||||
|
||||
} catch (e) {
|
||||
grunt.fail.warn(e);
|
||||
}
|
||||
|
||||
function getTagOfCurrentCommit() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
var gitTagOutput = gitTagResult.output.trim();
|
||||
var branchVersionPattern = new RegExp(getPackage().branchVersion.replace('.', '\\.').replace('*', '\\d+'));
|
||||
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
|
||||
return gitTagOutput;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getTaggedReleaseCodeName(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
}
|
||||
|
||||
function getSnapshotVersion() {
|
||||
var oldTags = shell.exec('git tag -l v'+getPackage().branchVersion, {silent:true}).output.trim().split('\n');
|
||||
// ignore non semver versions.
|
||||
oldTags = oldTags.filter(function(version) {
|
||||
return version && semver.valid(version);
|
||||
});
|
||||
if (oldTags.length) {
|
||||
oldTags.sort(semver.compare);
|
||||
semVerVersion = oldTags[oldTags.length-1];
|
||||
if (semVerVersion.indexOf('-') !== -1) {
|
||||
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||
} else {
|
||||
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||
}
|
||||
} else {
|
||||
semVerVersion = semver.valid(getPackage().branchVersion.replace(/\*/g, '0'));
|
||||
}
|
||||
return semVerVersion;
|
||||
}
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
},
|
||||
|
||||
getPreviousVersions: function() {
|
||||
var VERSION_REGEX = /([1-9]\d*)\.(\d+)\.(\d+)(?:-?rc\.?(\d+)|-(snapshot))?/;
|
||||
|
||||
// Pad out a number with zeros at the front to make it `digits` characters long
|
||||
function pad(num, digits) {
|
||||
var zeros = Array(digits+1).join('0');
|
||||
return (zeros+num).slice(-digits);
|
||||
}
|
||||
|
||||
function padVersion(version) {
|
||||
// We pad out the version numbers with 0s so they sort nicely
|
||||
// - Non-Release Candidates get 9999 for their release candidate section to make them appear earlier
|
||||
// - Snapshots get 9 added to the front to move them to the top of the list
|
||||
var maxLength = 4;
|
||||
var padded = (version.snapshot ? '9' : '0') + pad(version.major, maxLength) +
|
||||
pad(version.minor, maxLength) + pad(version.dot, maxLength) +
|
||||
pad(version.rc || 9999, maxLength);
|
||||
return padded;
|
||||
}
|
||||
|
||||
function getVersionFromTag(tag) {
|
||||
var match = VERSION_REGEX.exec(tag);
|
||||
if ( match ) {
|
||||
var version = {
|
||||
tag: tag,
|
||||
major: match[1], minor: match[2], dot: match[3], rc: match[4],
|
||||
snapshot: !!match[5] && getSnapshotSuffix()
|
||||
};
|
||||
|
||||
if(version.snapshot) {
|
||||
version.full = version.major + '.' + version.minor + '.x (edge)';
|
||||
} else {
|
||||
version.full = version.major + '.' + version.minor + '.' + version.dot +
|
||||
(version.rc ? '-rc.' + version.rc : '');
|
||||
}
|
||||
|
||||
// Stable versions have an even minor version and are not a release candidate
|
||||
version.isStable = !(version.minor%2 || version.rc);
|
||||
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.full + '/docs';
|
||||
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
|
||||
version.docsUrl += '-' + version.full;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
var tags = shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/);
|
||||
return _(tags)
|
||||
.map(getVersionFromTag)
|
||||
.filter() // getVersion can map to undefined - this clears those out
|
||||
.sortBy(padVersion)
|
||||
.value();
|
||||
},
|
||||
|
||||
startKarma: function(config, singleRun, done){
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
@@ -225,7 +55,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
updateWebdriver: function(done){
|
||||
updateWebdriver: function(done){
|
||||
if (process.env.TRAVIS) {
|
||||
// Skip the webdriver-manager update on Travis, since the browsers will
|
||||
// be provided remotely.
|
||||
@@ -319,9 +149,9 @@ module.exports = {
|
||||
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
|
||||
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
|
||||
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
|
||||
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
|
||||
.replace(/"NG_VERSION_DOT"/, NG_VERSION.patch)
|
||||
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
|
||||
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
|
||||
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codeName);
|
||||
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
|
||||
return processed;
|
||||
},
|
||||
@@ -374,7 +204,7 @@ module.exports = {
|
||||
var mapFile = minFile + '.map';
|
||||
var mapFileName = mapFile.match(/[^\/]+$/)[0];
|
||||
var errorFileName = file.replace(/\.js$/, '-errors.json');
|
||||
var versionNumber = this.getVersion().full;
|
||||
var versionNumber = grunt.config('NG_VERSION').full;
|
||||
shell.exec(
|
||||
'java ' +
|
||||
this.java32flags() + ' ' +
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var semver = require('semver');
|
||||
var _ = require('lodash');
|
||||
|
||||
var currentPackage, previousVersions, cdnVersion;
|
||||
|
||||
|
||||
/**
|
||||
* Load information about this project from the package.json
|
||||
* @return {Object} The package information
|
||||
*/
|
||||
var getPackage = function() {
|
||||
// Search up the folder hierarchy for the first package.json
|
||||
var packageFolder = path.resolve('.');
|
||||
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
|
||||
var parent = path.dirname(packageFolder);
|
||||
if ( parent === packageFolder) { break; }
|
||||
packageFolder = parent;
|
||||
}
|
||||
return JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse the github URL for useful information
|
||||
* @return {Object} An object containing the github owner and repository name
|
||||
*/
|
||||
var getGitRepoInfo = function() {
|
||||
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
|
||||
var match = GITURL_REGEX.exec(currentPackage.repository.url);
|
||||
var git = {
|
||||
owner: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
return git;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extract the code name from the tagged commit's message - it should contain the text of the form:
|
||||
* "codename(some-code-name)"
|
||||
* @param {String} tagName Name of the tag to look in for the codename
|
||||
* @return {String} The codename if found, otherwise null/undefined
|
||||
*/
|
||||
var getCodeName = function(tagName) {
|
||||
var gitCatOutput = shell.exec('git cat-file -p '+ tagName, {silent:true}).output;
|
||||
var tagMessage = gitCatOutput.match(/^.*codename.*$/mg)[0];
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compute a build segment for the version, from the Jenkins build number and current commit SHA
|
||||
* @return {String} The build segment of the version
|
||||
*/
|
||||
function getBuild() {
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'sha.'+hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the current commit is tagged as a version get that version
|
||||
* @return {SemVer} The version or null
|
||||
*/
|
||||
var getTaggedVersion = function() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
|
||||
if ( gitTagResult.code === 0 ) {
|
||||
var tag = gitTagResult.output.trim();
|
||||
var version = semver.parse(tag);
|
||||
|
||||
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
|
||||
version.codeName = getCodeName(tag);
|
||||
version.full = version.version;
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stable versions have an even minor version and have no prerelease
|
||||
* @param {SemVer} version The version to test
|
||||
* @return {Boolean} True if the version is stable
|
||||
*/
|
||||
var isStable = function(version) {
|
||||
return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a collection of all the previous versions sorted by semantic version
|
||||
* @return {Array.<SemVer>} The collection of previous versions
|
||||
*/
|
||||
var getPreviousVersions = function() {
|
||||
// always use the remote tags as the local clone might
|
||||
// not contain all commits when cloned with git clone --depth=...
|
||||
// Needed e.g. for Travis
|
||||
var repo_url = currentPackage.repository.url;
|
||||
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
|
||||
{silent: true});
|
||||
if ( tagResults.code === 0 ) {
|
||||
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
|
||||
.map(function(tag) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
version.isStable = isStable(version);
|
||||
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
var getCdnVersion = function() {
|
||||
return _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
// Note: need to use shell.exec and curl here
|
||||
// as version-infos returns its result synchronously...
|
||||
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/'+version+'/angular.min.js '+
|
||||
'--head --write-out "%{http_code}" -o /dev/null -silent',
|
||||
{silent: true});
|
||||
if ( cdnResult.code === 0 ) {
|
||||
var statusCode = cdnResult.output.trim();
|
||||
if (statusCode === '200') {
|
||||
cdnVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cdnVersion;
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unstable snapshot version
|
||||
* @return {SemVer} The snapshot version
|
||||
*/
|
||||
var getSnapshotVersion = function() {
|
||||
version = _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.last();
|
||||
|
||||
if ( !version ) {
|
||||
// a snapshot version before the first tag on the branch
|
||||
version = semver(currentPackage.branchVersion.replace('*','0-beta.1'));
|
||||
}
|
||||
|
||||
// We need to clone to ensure that we are not modifying another version
|
||||
version = semver(version.raw);
|
||||
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER;
|
||||
if (!version.prerelease || !version.prerelease.length) {
|
||||
// last release was a non beta release. Increment the patch level to
|
||||
// indicate the next release that we will be doing.
|
||||
// E.g. last release was 1.3.0, then the snapshot will be
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
|
||||
|
||||
// If the last release was a beta release we don't update the
|
||||
// beta number by purpose, as otherwise the semver comparison
|
||||
// does not work any more when the next beta is released.
|
||||
// E.g. don't generate 1.3.0-beta.2.build.1
|
||||
// as this is bigger than 1.3.0-beta.2 according to semver
|
||||
version.patch++;
|
||||
}
|
||||
version.prerelease = jenkinsBuild ? ['build', jenkinsBuild] : ['local'];
|
||||
version.build = getBuild();
|
||||
version.codeName = 'snapshot';
|
||||
version.isSnapshot = true;
|
||||
version.format();
|
||||
version.full = version.version + '+' + version.build;
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
|
||||
exports.currentPackage = currentPackage = getPackage();
|
||||
exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
|
||||
exports.previousVersions = previousVersions = getPreviousVersions();
|
||||
exports.cdnVersion = cdnVersion = getCdnVersion();
|
||||
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
|
||||
Generated
+2808
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.14",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -16,7 +15,7 @@
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs-checker": "~0.3.2",
|
||||
"grunt-jscs-checker": "~0.4.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
@@ -27,7 +26,7 @@
|
||||
"q-io": "~1.10.6",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
"karma": "0.11.12",
|
||||
"karma": "^0.12.0",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-chrome-launcher": "0.1.2",
|
||||
"karma-firefox-launcher": "0.1.3",
|
||||
@@ -50,10 +49,12 @@
|
||||
"gulp-concat": "~2.1.7",
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "~0.2.0",
|
||||
"dgeni-packages": "^0.3.0",
|
||||
"dgeni": "^0.2.2",
|
||||
"dgeni-packages": "^0.8.1",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5"
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"sorted-object": "^1.0.0"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular-phonecat repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular-phonecat ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/angular-phonecat
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angular-phonecat"
|
||||
git clone git@github.com:angular/angular-phonecat.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating angular-phonecat"
|
||||
cd $REPO_DIR
|
||||
./scripts/private/update-angular.sh $BUILD_DIR
|
||||
|
||||
# Test
|
||||
if [[ $NO_TEST != "true" ]]; then
|
||||
./scripts/private/test-all.sh
|
||||
fi
|
||||
|
||||
# Generate demo
|
||||
./scripts/private/snapshot-web.sh
|
||||
git checkout gh-pages
|
||||
git pull
|
||||
rm -r step*
|
||||
mv angular-phonecat-snapshots-web/step* .
|
||||
git add step*
|
||||
git commit -am "Angular $NEW_VERSION release"
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angular-phonecat"
|
||||
git push origin master -f --tags
|
||||
git push origin gh-pages -f
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular-seed repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular-seed ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/angular-seed
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angular-seed"
|
||||
git clone git@github.com:angular/angular-seed.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating angular-seed"
|
||||
cd $REPO_DIR
|
||||
./scripts/update-angular.sh $BUILD_DIR
|
||||
|
||||
# Test
|
||||
if [[ $NO_TEST != "true" ]]; then
|
||||
./scripts/test-all.sh
|
||||
fi
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angular-seed"
|
||||
git push origin master
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Script for updating cdnVersion of angular.js
|
||||
|
||||
echo "###################################"
|
||||
echo "## Update angular.js cdnVersion ###"
|
||||
echo "###################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--cdn-version=(.*)"
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
function init {
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function prepare {
|
||||
replaceJsonProp "package.json" "cdnVersion" "(.*)" "$CDN_VERSION"
|
||||
git add package.json
|
||||
git commit -m "chore(release): update cdn version"
|
||||
}
|
||||
|
||||
function publish {
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
# push the commits to github
|
||||
git push origin $BRANCH
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Script for updating angularjs.org repo
|
||||
|
||||
echo "#################################"
|
||||
echo "##### Update angularjs.org ######"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"--cdn-version=(.*)"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
REPO_DIR=$TMP_DIR/angularjs.org
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angularjs.org"
|
||||
git clone git@github.com:angular/angularjs.org.git $REPO_DIR
|
||||
|
||||
#
|
||||
# update files
|
||||
#
|
||||
echo "-- Updating angularjs.org"
|
||||
cd $REPO_DIR
|
||||
VERSION_REGEX="[a-z0-9\-\.\+]+"
|
||||
|
||||
replaceInFile "index.html" "(ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "index.html" "(<span class=\"version\">[^<]*<span>)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "index.html" "(code.angularjs.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
|
||||
replaceInFile "js/homepage.js" "($scope.CURRENT_STABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "js/homepage.js" "($scope.CURRENT_UNSTABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
|
||||
git add index.html
|
||||
git add js/homepage.js
|
||||
git commit -m "update(version): update angular version to $CDN_VERSION"
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angularjs.org"
|
||||
git push origin master
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -50,12 +50,6 @@ function prepare {
|
||||
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
echo "-- Updating files in bower-$repo"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* this script is just a temporary solution to deal with the issue of npm outputting the npm
|
||||
* shrinkwrap file in an unstable manner.
|
||||
*
|
||||
* See: https://github.com/npm/npm/issues/3581
|
||||
*/
|
||||
|
||||
var _ = require('lodash');
|
||||
var sorted = require('sorted-object');
|
||||
var fs = require('fs');
|
||||
|
||||
|
||||
function cleanModule(module, name) {
|
||||
|
||||
// keep `from` and `resolve` properties for git dependencies, delete otherwise
|
||||
if (!(module.resolved && module.resolved.match(/^git:\/\//))) {
|
||||
delete module.from;
|
||||
delete module.resolved;
|
||||
}
|
||||
|
||||
if (name === 'chokidar') {
|
||||
if (module.version === '0.8.1') {
|
||||
delete module.dependencies;
|
||||
} else {
|
||||
throw new Error("Unfamiliar chokidar version (v" + module.version +
|
||||
") , please check status of https://github.com/paulmillr/chokidar/pull/106");
|
||||
}
|
||||
}
|
||||
|
||||
_.forEach(module.dependencies, function(mod, name) {
|
||||
cleanModule(mod, name);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
console.log('Reading npm-shrinkwrap.json');
|
||||
var shrinkwrap = require('./../npm-shrinkwrap.json');
|
||||
|
||||
console.log('Cleaning shrinkwrap object');
|
||||
cleanModule(shrinkwrap, shrinkwrap.name);
|
||||
|
||||
console.log('Writing cleaned npm-shrinkwrap.json');
|
||||
fs.writeFileSync('./npm-shrinkwrap.json', JSON.stringify(sorted(shrinkwrap), null, 2) + "\n");
|
||||
@@ -38,12 +38,6 @@ function prepare {
|
||||
#
|
||||
echo "-- Updating code.angularjs.org"
|
||||
mkdir $REPO_DIR/$NEW_VERSION
|
||||
cd $REPO_DIR
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||
|
||||
#
|
||||
@@ -55,19 +49,37 @@ function prepare {
|
||||
git commit -m "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
function publish {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
echo "-- Updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
function _update_snapshot() {
|
||||
for backend in "$@" ; do
|
||||
echo "-- Updating snapshot version: backend=$backend"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://$backend:8003/fetchLatestSnapshot.php
|
||||
done
|
||||
}
|
||||
|
||||
function _update_code() {
|
||||
cd $REPO_DIR
|
||||
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
echo "-- Refreshing code.angularjs.org"
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
for backend in "$@" ; do
|
||||
echo "-- Refreshing code.angularjs.org: backend=$backend"
|
||||
curl http://$backend:8003/gitFetchSite.php
|
||||
done
|
||||
}
|
||||
|
||||
function publish {
|
||||
# The TXT record for backends.angularjs.org is a CSV of the IP addresses for
|
||||
# the currently serving Compute Engine backends.
|
||||
# code.angularjs.org is served out of port 8003 on these backends.
|
||||
backends=("$(dig backends.angularjs.org +short TXT | python -c 'print raw_input()[1:-1].replace(",", "\n")')")
|
||||
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
_update_snapshot ${backends[@]}
|
||||
else
|
||||
_update_code ${backends[@]}
|
||||
fi
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
ARG_DEFS=(
|
||||
# require the git dryrun flag so the script can't be run without
|
||||
# thinking about this!
|
||||
"--git-push-dryrun=(true|false)"
|
||||
"--cdn-version=(.*)"
|
||||
)
|
||||
|
||||
function init {
|
||||
NG_ARGS=("$@")
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
CDN_VERSION_ARG="--cdn-version=$CDN_VERSION"
|
||||
./scripts/angular.js/publish-cdn-version.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||
./scripts/angularjs.org/publish.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||
}
|
||||
|
||||
function checkCdn {
|
||||
STATUS=$(curl http://ajax.googleapis.com/ajax/libs/angularjs/$CDN_VERSION/angular.min.js --write-out '%{http_code}' -o /dev/null -silent)
|
||||
if [[ $STATUS != 200 ]]; then
|
||||
echo "Could not find release $CDN_VERSION on CDN"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
checkCdn
|
||||
|
||||
phase prepare
|
||||
phase publish
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -23,7 +23,7 @@ ARG_DEFS=(
|
||||
)
|
||||
|
||||
function init {
|
||||
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||
if [[ $(git rev-parse HEAD) != $(git rev-parse $COMMIT_SHA) ]]; then
|
||||
echo "HEAD is not at $COMMIT_SHA"
|
||||
usage
|
||||
fi
|
||||
@@ -56,12 +56,10 @@ function phase {
|
||||
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
}
|
||||
|
||||
function run {
|
||||
# First prepare all scripts (build, test, commit, tag, ...),
|
||||
# First prepare all scripts (build, commit, tag, ...),
|
||||
# so we are sure everything is all right
|
||||
phase prepare
|
||||
# only then publish to github
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
# 0. Set the current directory to the directory of the script. By this
|
||||
# the script can be called from anywhere.
|
||||
# 1. Parse the named arguments
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls to `git push` in this script
|
||||
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
|
||||
@@ -36,7 +36,7 @@
|
||||
# with the name of the parameter in upper case (with dash converted to underscore).
|
||||
#
|
||||
# Special arguments that are always available:
|
||||
# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
|
||||
# - "--action=.*": This parameter will be used to execute a function with that name when the
|
||||
# script is started
|
||||
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
|
||||
# or in child scripts so that the `--dry-run` and `--porcelain` is added
|
||||
@@ -195,7 +195,7 @@ function isFunction {
|
||||
}
|
||||
|
||||
# readJsonProp(jsonFile, property)
|
||||
# - restriction: property needs to be on an own line!
|
||||
# - restriction: property needs to be on a single line!
|
||||
function readJsonProp {
|
||||
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"isWindow": false,
|
||||
"isScope": false,
|
||||
"isFile": false,
|
||||
"isBlob": false,
|
||||
"isBoolean": false,
|
||||
"trim": false,
|
||||
"isElement": false,
|
||||
@@ -134,6 +135,7 @@
|
||||
"JQLitePrototype": false,
|
||||
"addEventListenerFn": false,
|
||||
"removeEventListenerFn": false,
|
||||
"jqLiteIsTextNode": false,
|
||||
|
||||
/* apis.js */
|
||||
"hashKey": false,
|
||||
|
||||
+42
-1
@@ -45,6 +45,7 @@
|
||||
-isWindow,
|
||||
-isScope,
|
||||
-isFile,
|
||||
-isBlob,
|
||||
-isBoolean,
|
||||
-trim,
|
||||
-isElement,
|
||||
@@ -566,6 +567,11 @@ function isFile(obj) {
|
||||
}
|
||||
|
||||
|
||||
function isBlob(obj) {
|
||||
return toString.call(obj) === '[object Blob]';
|
||||
}
|
||||
|
||||
|
||||
function isBoolean(value) {
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
@@ -1235,7 +1241,42 @@ function angularInit(element, bootstrap) {
|
||||
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
|
||||
* They must use {@link ng.directive:ngApp ngApp}.
|
||||
*
|
||||
* @param {Element} element DOM element which is the root of angular application.
|
||||
* Angular will detect if it has been loaded into the browser more than once and only allow the
|
||||
* first loaded script to be bootstrapped and will report a warning to the browser console for
|
||||
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
|
||||
* multiple instances of Angular try to work on the DOM.
|
||||
*
|
||||
* <example name="multi-bootstrap" module="multi-bootstrap">
|
||||
* <file name="index.html">
|
||||
* <script src="../../../angular.js"></script>
|
||||
* <div ng-controller="BrokenTable">
|
||||
* <table>
|
||||
* <tr>
|
||||
* <th ng-repeat="heading in headings">{{heading}}</th>
|
||||
* </tr>
|
||||
* <tr ng-repeat="filling in fillings">
|
||||
* <td ng-repeat="fill in filling">{{fill}}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="controller.js">
|
||||
* var app = angular.module('multi-bootstrap', [])
|
||||
*
|
||||
* .controller('BrokenTable', function($scope) {
|
||||
* $scope.headings = ['One', 'Two', 'Three'];
|
||||
* $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
|
||||
* });
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it('should only insert one table cell for each item in $scope.fillings', function() {
|
||||
* expect(element.all(by.css('td')).count())
|
||||
* .toBe(9);
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* @param {DOMElement} element DOM element which is the root of angular application.
|
||||
* @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
|
||||
* Each item in the array should be the name of a predefined module or a (DI annotated)
|
||||
* function that will be invoked by the injector as a run block.
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
if (window.angular.bootstrap) {
|
||||
//AngularJS is already loaded, so we can return here...
|
||||
console.log('WARNING: Tried to load angular more than once.');
|
||||
return;
|
||||
}
|
||||
|
||||
//try to bind to jquery now so that one can write angular.element().read()
|
||||
//but we will rebind on bootstrap again.
|
||||
bindJQuery();
|
||||
|
||||
+78
-10
@@ -179,6 +179,75 @@ function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArgu
|
||||
}
|
||||
}
|
||||
|
||||
var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
|
||||
var HTML_REGEXP = /<|&#?\w+;/;
|
||||
var TAG_NAME_REGEXP = /<([\w:]+)/;
|
||||
var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
|
||||
|
||||
var wrapMap = {
|
||||
'option': [1, '<select multiple="multiple">', '</select>'],
|
||||
|
||||
'thead': [1, '<table>', '</table>'],
|
||||
'col': [2, '<table><colgroup>', '</colgroup></table>'],
|
||||
'tr': [2, '<table><tbody>', '</tbody></table>'],
|
||||
'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
|
||||
'_default': [0, "", ""]
|
||||
};
|
||||
|
||||
wrapMap.optgroup = wrapMap.option;
|
||||
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
|
||||
wrapMap.th = wrapMap.td;
|
||||
|
||||
function jqLiteIsTextNode(html) {
|
||||
return !HTML_REGEXP.test(html);
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var elem, tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
nodes = [], i, j, jj;
|
||||
|
||||
if (jqLiteIsTextNode(html)) {
|
||||
// Convert non-html into a text node
|
||||
nodes.push(context.createTextNode(html));
|
||||
} else {
|
||||
tmp = fragment.appendChild(context.createElement('div'));
|
||||
// Convert html into DOM nodes
|
||||
tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
|
||||
wrap = wrapMap[tag] || wrapMap._default;
|
||||
tmp.innerHTML = '<div> </div>' +
|
||||
wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
|
||||
tmp.removeChild(tmp.firstChild);
|
||||
|
||||
// Descend through wrappers to the right content
|
||||
i = wrap[0];
|
||||
while (i--) {
|
||||
tmp = tmp.lastChild;
|
||||
}
|
||||
|
||||
for (j=0, jj=tmp.childNodes.length; j<jj; ++j) nodes.push(tmp.childNodes[j]);
|
||||
|
||||
tmp = fragment.firstChild;
|
||||
tmp.textContent = "";
|
||||
}
|
||||
|
||||
// Remove wrapper from fragment
|
||||
fragment.textContent = "";
|
||||
fragment.innerHTML = ""; // Clear inner HTML
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function jqLiteParseHTML(html, context) {
|
||||
context = context || document;
|
||||
var parsed;
|
||||
|
||||
if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
|
||||
return [context.createElement(parsed[1])];
|
||||
}
|
||||
|
||||
return jqLiteBuildFragment(html, context);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
function JQLite(element) {
|
||||
if (element instanceof JQLite) {
|
||||
@@ -195,14 +264,9 @@ function JQLite(element) {
|
||||
}
|
||||
|
||||
if (isString(element)) {
|
||||
var div = document.createElement('div');
|
||||
// Read about the NoScope elements here:
|
||||
// http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
|
||||
div.innerHTML = '<div> </div>' + element; // IE insanity to make NoScope elements work!
|
||||
div.removeChild(div.firstChild); // remove the superfluous div
|
||||
jqLiteAddNodes(this, div.childNodes);
|
||||
jqLiteAddNodes(this, jqLiteParseHTML(element));
|
||||
var fragment = jqLite(document.createDocumentFragment());
|
||||
fragment.append(this); // detach the elements from the temporary DOM div.
|
||||
fragment.append(this);
|
||||
} else {
|
||||
jqLiteAddNodes(this, element);
|
||||
}
|
||||
@@ -364,11 +428,15 @@ function jqLiteInheritedData(element, name, value) {
|
||||
var names = isArray(name) ? name : [name];
|
||||
|
||||
while (element.length) {
|
||||
|
||||
var node = element[0];
|
||||
for (var i = 0, ii = names.length; i < ii; i++) {
|
||||
if ((value = element.data(names[i])) !== undefined) return value;
|
||||
}
|
||||
element = element.parent();
|
||||
|
||||
// If dealing with a document fragment node with a host element, and no parent, use the host
|
||||
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
|
||||
// to lookup parent controllers.
|
||||
element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,7 +525,7 @@ forEach({
|
||||
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
|
||||
},
|
||||
|
||||
controller: jqLiteController ,
|
||||
controller: jqLiteController,
|
||||
|
||||
injector: function(element) {
|
||||
return jqLiteInheritedData(element, '$injector');
|
||||
|
||||
+4
-6
@@ -55,10 +55,10 @@ function setupModuleLoader(window) {
|
||||
* myModule.value('appName', 'MyCoolApp');
|
||||
*
|
||||
* // configure existing services inside initialization blocks.
|
||||
* myModule.config(function($locationProvider) {
|
||||
* myModule.config(['$locationProvider', function($locationProvider) {
|
||||
* // Configure existing providers
|
||||
* $locationProvider.hashPrefix('!');
|
||||
* });
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* Then you can create an injector and load your modules like this:
|
||||
@@ -72,8 +72,8 @@ function setupModuleLoader(window) {
|
||||
* {@link angular.bootstrap} to simplify this process for you.
|
||||
*
|
||||
* @param {!string} name The name of the module to create or retrieve.
|
||||
* @param {Array.<string>=} requires If specified then new module is being created. If
|
||||
* unspecified then the module is being retrieved for further configuration.
|
||||
<<<<<* @param {!Array.<string>=} requires If specified then new module is being created. If
|
||||
>>>>>* unspecified then the module is being retrieved for further configuration.
|
||||
* @param {Function} configFn Optional configuration function for the module. Same as
|
||||
* {@link angular.Module#config Module#config()}.
|
||||
* @returns {module} new module with the {@link angular.Module} api.
|
||||
@@ -114,7 +114,6 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @module ng
|
||||
* @propertyOf angular.Module
|
||||
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||
* @description
|
||||
* Holds the list of modules which the injector will load before the current module is
|
||||
@@ -126,7 +125,6 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @module ng
|
||||
* @propertyOf angular.Module
|
||||
* @returns {string} Name of the module.
|
||||
* @description
|
||||
*/
|
||||
|
||||
+1
-1
@@ -194,7 +194,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
|
||||
/**
|
||||
* @name $browser#onUrlChange
|
||||
* @TODO(vojta): refactor to use node's syntax for events
|
||||
*
|
||||
* @description
|
||||
* Register callback function that will be called, when url changes.
|
||||
@@ -215,6 +214,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
|
||||
*/
|
||||
self.onUrlChange = function(callback) {
|
||||
// TODO(vojta): refactor to use node's syntax for events
|
||||
if (!urlChangeInit) {
|
||||
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
|
||||
// don't fire popstate when user change the address bar and don't fire hashchange when url
|
||||
|
||||
+157
-10
@@ -5,7 +5,8 @@
|
||||
* @name $cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Factory that constructs cache objects and gives access to them.
|
||||
* Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
|
||||
* them.
|
||||
*
|
||||
* ```js
|
||||
*
|
||||
@@ -37,6 +38,46 @@
|
||||
* - `{void}` `removeAll()` — Removes all cached values.
|
||||
* - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
|
||||
*
|
||||
* @example
|
||||
<example module="cacheExampleApp">
|
||||
<file name="index.html">
|
||||
<div ng-controller="CacheController">
|
||||
<input ng-model="newCacheKey" placeholder="Key">
|
||||
<input ng-model="newCacheValue" placeholder="Value">
|
||||
<button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
|
||||
|
||||
<p ng-if="keys.length">Cached Values</p>
|
||||
<div ng-repeat="key in keys">
|
||||
<span ng-bind="key"></span>
|
||||
<span>: </span>
|
||||
<b ng-bind="cache.get(key)"></b>
|
||||
</div>
|
||||
|
||||
<p>Cache Info</p>
|
||||
<div ng-repeat="(key, value) in cache.info()">
|
||||
<span ng-bind="key"></span>
|
||||
<span>: </span>
|
||||
<b ng-bind="value"></b>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('cacheExampleApp', []).
|
||||
controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
|
||||
$scope.keys = [];
|
||||
$scope.cache = $cacheFactory('cacheId');
|
||||
$scope.put = function(key, value) {
|
||||
$scope.cache.put(key, value);
|
||||
$scope.keys.push(key);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
p {
|
||||
margin: 10px 0 3px;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $CacheFactoryProvider() {
|
||||
|
||||
@@ -56,8 +97,65 @@ function $CacheFactoryProvider() {
|
||||
freshEnd = null,
|
||||
staleEnd = null;
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name $cacheFactory.Cache
|
||||
*
|
||||
* @description
|
||||
* A cache object used to store and retrieve data, primarily used by
|
||||
* {@link $http $http} and the {@link ng.directive:script script} directive to cache
|
||||
* templates and other data.
|
||||
*
|
||||
* ```js
|
||||
* angular.module('superCache')
|
||||
* .factory('superCache', ['$cacheFactory', function($cacheFactory) {
|
||||
* return $cacheFactory('super-cache');
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* Example test:
|
||||
*
|
||||
* ```js
|
||||
* it('should behave like a cache', inject(function(superCache) {
|
||||
* superCache.put('key', 'value');
|
||||
* superCache.put('another key', 'another value');
|
||||
*
|
||||
* expect(superCache.info()).toEqual({
|
||||
* id: 'super-cache',
|
||||
* size: 2
|
||||
* });
|
||||
*
|
||||
* superCache.remove('another key');
|
||||
* expect(superCache.get('another key')).toBeUndefined();
|
||||
*
|
||||
* superCache.removeAll();
|
||||
* expect(superCache.info()).toEqual({
|
||||
* id: 'super-cache',
|
||||
* size: 0
|
||||
* });
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
return caches[cacheId] = {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#put
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
|
||||
* retrieved later, and incrementing the size of the cache if the key was not already
|
||||
* present in the cache. If behaving like an LRU cache, it will also remove stale
|
||||
* entries from the set.
|
||||
*
|
||||
* It will not insert undefined values into the cache.
|
||||
*
|
||||
* @param {string} key the key under which the cached data is stored.
|
||||
* @param {*} value the value to store alongside the key. If it is undefined, the key
|
||||
* will not be stored.
|
||||
* @returns {*} the value stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
if (capacity < Number.MAX_VALUE) {
|
||||
var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
|
||||
@@ -76,7 +174,17 @@ function $CacheFactoryProvider() {
|
||||
return value;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#get
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
|
||||
*
|
||||
* @param {string} key the key of the data to be retrieved
|
||||
* @returns {*} the value stored.
|
||||
*/
|
||||
get: function(key) {
|
||||
if (capacity < Number.MAX_VALUE) {
|
||||
var lruEntry = lruHash[key];
|
||||
@@ -90,6 +198,16 @@ function $CacheFactoryProvider() {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#remove
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Removes an entry from the {@link $cacheFactory.Cache Cache} object.
|
||||
*
|
||||
* @param {string} key the key of the entry to be removed
|
||||
*/
|
||||
remove: function(key) {
|
||||
if (capacity < Number.MAX_VALUE) {
|
||||
var lruEntry = lruHash[key];
|
||||
@@ -108,6 +226,14 @@ function $CacheFactoryProvider() {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#removeAll
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Clears the cache object of any entries.
|
||||
*/
|
||||
removeAll: function() {
|
||||
data = {};
|
||||
size = 0;
|
||||
@@ -116,6 +242,15 @@ function $CacheFactoryProvider() {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#destroy
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Destroys the {@link $cacheFactory.Cache Cache} object entirely,
|
||||
* removing it from the {@link $cacheFactory $cacheFactory} set.
|
||||
*/
|
||||
destroy: function() {
|
||||
data = null;
|
||||
stats = null;
|
||||
@@ -124,6 +259,22 @@ function $CacheFactoryProvider() {
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cacheFactory.Cache#info
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
|
||||
*
|
||||
* @returns {object} an object with the following properties:
|
||||
* <ul>
|
||||
* <li>**id**: the id of the cache instance</li>
|
||||
* <li>**size**: the number of entries kept in the cache instance</li>
|
||||
* <li>**...**: any additional properties from the options object when creating the
|
||||
* cache.</li>
|
||||
* </ul>
|
||||
*/
|
||||
info: function() {
|
||||
return extend({}, stats, {size: size});
|
||||
}
|
||||
@@ -208,15 +359,11 @@ function $CacheFactoryProvider() {
|
||||
* `$templateCache` service directly.
|
||||
*
|
||||
* Adding via the `script` tag:
|
||||
*
|
||||
* ```html
|
||||
* <html ng-app>
|
||||
* <head>
|
||||
* <script type="text/ng-template" id="templateId.html">
|
||||
* This is the content of the template
|
||||
* </script>
|
||||
* </head>
|
||||
* ...
|
||||
* </html>
|
||||
* <script type="text/ng-template" id="templateId.html">
|
||||
* <p>This is the content of the template</p>
|
||||
* </script>
|
||||
* ```
|
||||
*
|
||||
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
|
||||
|
||||
+22
-26
@@ -64,6 +64,7 @@
|
||||
* restrict: 'A',
|
||||
* scope: false,
|
||||
* controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
|
||||
* controllerAs: 'stringAlias',
|
||||
* require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
|
||||
* compile: function compile(tElement, tAttrs, transclude) {
|
||||
* return {
|
||||
@@ -281,6 +282,16 @@
|
||||
* apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
|
||||
* should be done in a linking function rather than in a compile function.
|
||||
* </div>
|
||||
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** The compile function cannot handle directives that recursively use themselves in their
|
||||
* own templates or compile functions. Compiling these directives results in an infinite loop and a
|
||||
* stack overflow errors.
|
||||
*
|
||||
* This can be avoided by manually using $compile in the postLink function to imperatively compile
|
||||
* a directive's template instead of relying on automatic template compilation via `template` or
|
||||
* `templateUrl` declaration or manual compilation inside the compile function.
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-error">
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
|
||||
@@ -502,8 +513,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var hasDirectives = {},
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
@@ -1245,7 +1255,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
if (directive.replace) {
|
||||
replaceDirective = directive;
|
||||
$template = directiveTemplateContents(directiveValue);
|
||||
if (jqLiteIsTextNode(directiveValue)) {
|
||||
$template = [];
|
||||
} else {
|
||||
$template = jqLite(directiveValue);
|
||||
}
|
||||
compileNode = $template[0];
|
||||
|
||||
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
||||
@@ -1644,28 +1658,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
|
||||
function directiveTemplateContents(template) {
|
||||
var type;
|
||||
template = trim(template);
|
||||
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
|
||||
type = type[1].toLowerCase();
|
||||
var table = jqLite('<table>' + template + '</table>'),
|
||||
tbody = table.children('tbody'),
|
||||
leaf = /(td|th)/.test(type) && table.find('tr');
|
||||
if (tbody.length && type !== 'tbody') {
|
||||
table = tbody;
|
||||
}
|
||||
if (leaf && leaf.length) {
|
||||
table = leaf;
|
||||
}
|
||||
return table.contents();
|
||||
}
|
||||
return jqLite('<div>' +
|
||||
template +
|
||||
'</div>').contents();
|
||||
}
|
||||
|
||||
|
||||
function compileTemplateUrl(directives, $compileNode, tAttrs,
|
||||
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
|
||||
var linkQueue = [],
|
||||
@@ -1690,7 +1682,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
content = denormalizeTemplate(content);
|
||||
|
||||
if (origAsyncDirective.replace) {
|
||||
$template = directiveTemplateContents(content);
|
||||
if (jqLiteIsTextNode(content)) {
|
||||
$template = [];
|
||||
} else {
|
||||
$template = jqLite(content);
|
||||
}
|
||||
compileNode = $template[0];
|
||||
|
||||
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
||||
|
||||
@@ -277,7 +277,7 @@
|
||||
* such as selected. (Their presence means true and their absence means false.)
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
* binding information would be lost when the browser removes the attribute.
|
||||
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
||||
* The `ngSelected` directive solves this problem for the `selected` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
*
|
||||
|
||||
@@ -215,6 +215,10 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
* does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
|
||||
* sub-group of controls needs to be determined.
|
||||
*
|
||||
* Note: the purpose of `ngForm` is to group controls,
|
||||
* but not to be a replacement for the `<form>` tag with all of its capabilities
|
||||
* (e.g. posting to the server, ...).
|
||||
*
|
||||
* @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
|
||||
* related scope, under this name.
|
||||
*
|
||||
@@ -360,8 +364,6 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* @param {string=} name Name of the form. If specified, the form controller will be published into
|
||||
* related scope, under this name.
|
||||
*/
|
||||
var formDirectiveFactory = function(isNgForm) {
|
||||
return ['$timeout', function($timeout) {
|
||||
|
||||
@@ -450,7 +450,6 @@ function addNativeHtml5Validators(ctrl, validatorName, element) {
|
||||
return value;
|
||||
};
|
||||
ctrl.$parsers.push(validator);
|
||||
ctrl.$formatters.push(validator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
||||
* `{{ expression }}` which is similar but less verbose.
|
||||
*
|
||||
* It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
|
||||
* It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
|
||||
* displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
|
||||
* element attribute, it makes the bindings invisible to the user while the page is loading.
|
||||
*
|
||||
|
||||
+81
-27
@@ -2,7 +2,7 @@
|
||||
|
||||
function classDirective(name, selector) {
|
||||
name = 'ngClass' + name;
|
||||
return function() {
|
||||
return ['$animate', function($animate) {
|
||||
return {
|
||||
restrict: 'AC',
|
||||
link: function(scope, element, attr) {
|
||||
@@ -20,46 +20,100 @@ function classDirective(name, selector) {
|
||||
// jshint bitwise: false
|
||||
var mod = $index & 1;
|
||||
if (mod !== old$index & 1) {
|
||||
var classes = flattenClasses(scope.$eval(attr[name]));
|
||||
var classes = arrayClasses(scope.$eval(attr[name]));
|
||||
mod === selector ?
|
||||
attr.$addClass(classes) :
|
||||
attr.$removeClass(classes);
|
||||
addClasses(classes) :
|
||||
removeClasses(classes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, 1);
|
||||
attr.$addClass(newClasses);
|
||||
}
|
||||
|
||||
function removeClasses(classes) {
|
||||
var newClasses = digestClassCounts(classes, -1);
|
||||
attr.$removeClass(newClasses);
|
||||
}
|
||||
|
||||
function digestClassCounts (classes, count) {
|
||||
var classCounts = element.data('$classCounts') || {};
|
||||
var classesToUpdate = [];
|
||||
forEach(classes, function (className) {
|
||||
if (count > 0 || classCounts[className]) {
|
||||
classCounts[className] = (classCounts[className] || 0) + count;
|
||||
if (classCounts[className] === +(count > 0)) {
|
||||
classesToUpdate.push(className);
|
||||
}
|
||||
}
|
||||
});
|
||||
element.data('$classCounts', classCounts);
|
||||
return classesToUpdate.join(' ');
|
||||
}
|
||||
|
||||
function updateClasses (oldClasses, newClasses) {
|
||||
var toAdd = arrayDifference(newClasses, oldClasses);
|
||||
var toRemove = arrayDifference(oldClasses, newClasses);
|
||||
toRemove = digestClassCounts(toRemove, -1);
|
||||
toAdd = digestClassCounts(toAdd, 1);
|
||||
|
||||
if (toAdd.length === 0) {
|
||||
$animate.removeClass(element, toRemove);
|
||||
} else if (toRemove.length === 0) {
|
||||
$animate.addClass(element, toAdd);
|
||||
} else {
|
||||
$animate.setClass(element, toAdd, toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
function ngClassWatchAction(newVal) {
|
||||
if (selector === true || scope.$index % 2 === selector) {
|
||||
var newClasses = flattenClasses(newVal || '');
|
||||
if(!oldVal) {
|
||||
attr.$addClass(newClasses);
|
||||
} else if(!equals(newVal,oldVal)) {
|
||||
attr.$updateClass(newClasses, flattenClasses(oldVal));
|
||||
var newClasses = arrayClasses(newVal || []);
|
||||
if (!oldVal) {
|
||||
addClasses(newClasses);
|
||||
} else if (!equals(newVal,oldVal)) {
|
||||
var oldClasses = arrayClasses(oldVal);
|
||||
updateClasses(oldClasses, newClasses);
|
||||
}
|
||||
}
|
||||
oldVal = copy(newVal);
|
||||
}
|
||||
|
||||
|
||||
function flattenClasses(classVal) {
|
||||
if(isArray(classVal)) {
|
||||
return classVal.join(' ');
|
||||
} else if (isObject(classVal)) {
|
||||
var classes = [], i = 0;
|
||||
forEach(classVal, function(v, k) {
|
||||
if (v) {
|
||||
classes.push(k);
|
||||
}
|
||||
});
|
||||
return classes.join(' ');
|
||||
}
|
||||
|
||||
return classVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function arrayDifference(tokens1, tokens2) {
|
||||
var values = [];
|
||||
|
||||
outer:
|
||||
for(var i = 0; i < tokens1.length; i++) {
|
||||
var token = tokens1[i];
|
||||
for(var j = 0; j < tokens2.length; j++) {
|
||||
if(token == tokens2[j]) continue outer;
|
||||
}
|
||||
values.push(token);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function arrayClasses (classVal) {
|
||||
if (isArray(classVal)) {
|
||||
return classVal;
|
||||
} else if (isString(classVal)) {
|
||||
return classVal.split(' ');
|
||||
} else if (isObject(classVal)) {
|
||||
var classes = [], i = 0;
|
||||
forEach(classVal, function(v, k) {
|
||||
if (v) {
|
||||
classes.push(k);
|
||||
}
|
||||
});
|
||||
return classes;
|
||||
}
|
||||
return classVal;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
|
||||
* click. (Event object is available as `$event`)
|
||||
* click. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -92,7 +92,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
|
||||
* mousedown. (Event object is available as `$event`)
|
||||
* mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -116,7 +116,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
|
||||
* mouseup. (Event object is available as `$event`)
|
||||
* mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -139,7 +139,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
|
||||
* mouseover. (Event object is available as `$event`)
|
||||
* mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -163,7 +163,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
|
||||
* mouseenter. (Event object is available as `$event`)
|
||||
* mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -187,7 +187,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
|
||||
* mouseleave. (Event object is available as `$event`)
|
||||
* mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -211,7 +211,7 @@ forEach(
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
|
||||
* mousemove. (Event object is available as `$event`)
|
||||
* mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -278,7 +278,8 @@ forEach(
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
|
||||
* keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
* keypress. ({@link guide/expression#-event- Event object is available as `$event`}
|
||||
* and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -303,7 +304,8 @@ forEach(
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
|
||||
* ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -354,7 +356,7 @@ forEach(
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
|
||||
* focus. (Event object is available as `$event`)
|
||||
* focus. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
@@ -370,7 +372,7 @@ forEach(
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
|
||||
* blur. (Event object is available as `$event`)
|
||||
* blur. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
@@ -386,7 +388,7 @@ forEach(
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
|
||||
* copy. (Event object is available as `$event`)
|
||||
* copy. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -407,7 +409,7 @@ forEach(
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
|
||||
* cut. (Event object is available as `$event`)
|
||||
* cut. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
@@ -428,7 +430,7 @@ forEach(
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
|
||||
* paste. (Event object is available as `$event`)
|
||||
* paste. ({@link guide/expression#-event- Event object is available as `$event`})
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
* @priority 400
|
||||
*
|
||||
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
|
||||
* make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
|
||||
* make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
|
||||
* @param {string=} onload Expression to evaluate when a new partial is loaded.
|
||||
*
|
||||
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
|
||||
@@ -146,7 +146,6 @@
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngInclude#$includeContentRequested
|
||||
* @eventOf ng.directive:ngInclude
|
||||
* @eventType emit on the scope ngInclude was declared in
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is requested.
|
||||
@@ -156,7 +155,6 @@
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngInclude#$includeContentLoaded
|
||||
* @eventOf ng.directive:ngInclude
|
||||
* @eventType emit on the current ngInclude scope
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is reloaded.
|
||||
|
||||
@@ -68,9 +68,11 @@
|
||||
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
|
||||
*
|
||||
* @animations
|
||||
* enter - when a new item is added to the list or when an item is revealed after a filter
|
||||
* leave - when an item is removed from the list or when an item is filtered out
|
||||
* move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
* **.enter** - when a new item is added to the list or when an item is revealed after a filter
|
||||
*
|
||||
* **.leave** - when an item is removed from the list or when an item is filtered out
|
||||
*
|
||||
* **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
|
||||
@@ -171,7 +171,7 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* in AngularJS and sets the display style to none (using an !important flag).
|
||||
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
|
||||
*
|
||||
* ```hrml
|
||||
* ```html
|
||||
* <!-- when $scope.myValue is truthy (element is hidden) -->
|
||||
* <div ng-hide="myValue"></div>
|
||||
*
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
* @scope
|
||||
* @priority 800
|
||||
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
||||
* @paramDescription
|
||||
* On child elements add:
|
||||
*
|
||||
* * `ngSwitchWhen`: the case statement to match against. If match then this
|
||||
|
||||
@@ -394,6 +394,12 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
value = valueFn(scope, locals);
|
||||
}
|
||||
}
|
||||
// Update the null option's selected property here so $render cleans it up correctly
|
||||
if (optionGroupsCache[0].length > 1) {
|
||||
if (optionGroupsCache[0][1].id !== key) {
|
||||
optionGroupsCache[0][1].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
@@ -531,7 +537,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
// lastElement.prop('selected') provided by jQuery has side-effects
|
||||
if (lastElement[0].selected !== option.selected) {
|
||||
if (existingOption.selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -7,6 +7,22 @@
|
||||
*
|
||||
* @description
|
||||
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCtrl">
|
||||
<p>$document title: <b ng-bind="title"></b></p>
|
||||
<p>window.document title: <b ng-bind="windowTitle"></b></p>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function MainCtrl($scope, $document) {
|
||||
$scope.title = $document[0].title;
|
||||
$scope.windowTitle = angular.element(window.document)[0].title;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $DocumentProvider(){
|
||||
this.$get = ['$window', function(window){
|
||||
|
||||
@@ -441,7 +441,7 @@ function dateFilter($locale) {
|
||||
* @returns {string} JSON string.
|
||||
*
|
||||
*
|
||||
* @example:
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<pre>{{ {'name':'value'} | json }}</pre>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user