Compare commits
254 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f031127160 | |||
| 00b44d8e12 | |||
| ca865d29a3 | |||
| 3ccec13aa7 | |||
| 43d49013d1 | |||
| 116fac0562 | |||
| c3024254b6 | |||
| 770353df19 | |||
| 85b7d24357 | |||
| 8469779a8e | |||
| 3374e35953 | |||
| 90ff8a98d8 | |||
| a4dc21ebf5 | |||
| ec93f94cc9 | |||
| 7665497a53 | |||
| 2acadc4216 | |||
| c7658d9457 | |||
| b92c650e05 | |||
| f7a0f9d841 | |||
| b17d40b4a5 | |||
| d745df7e5f | |||
| 53b444419c | |||
| 9a21050b43 | |||
| 8473b9d558 | |||
| 679d418a50 | |||
| 16d247b386 | |||
| 4767d34ae8 | |||
| 5efc2ed5ac | |||
| f9bf194439 | |||
| 3c4460b513 | |||
| a98931de0e | |||
| 7e5154e755 | |||
| ec6b1cfaba | |||
| 8d8801f1ae | |||
| 301647bf1b | |||
| 1c03a1b9c0 | |||
| fd797cdb7e | |||
| ed1dbf2554 | |||
| 022cb3dc4e | |||
| 4f107acfcf | |||
| f363bcb437 | |||
| 7b2259f32c | |||
| 9e88fa18b9 | |||
| 094580c3da | |||
| cc4d08c5f0 | |||
| d0ae241afd | |||
| e1f103a8e4 | |||
| d17aa84be1 | |||
| e87c88914f | |||
| b5d48ee1f0 | |||
| 1c010b33aa | |||
| 16c7ab1ba0 | |||
| 9ef5d8f318 | |||
| 6a634e309b | |||
| 13f58447e2 | |||
| bc72211e7b | |||
| 230e124ddb | |||
| 10016ab3fd | |||
| 69dc003a0b | |||
| ae2fd55575 | |||
| f102fb75b6 | |||
| 8a7240ddfd | |||
| ac70ec0340 | |||
| dbd90a4d78 | |||
| 5b1f9b3c2b | |||
| 08a07f2d30 | |||
| b1143c9481 | |||
| 73e1d0054c | |||
| 33ab261817 | |||
| 230ff0576a | |||
| e3371d7c53 | |||
| 9b2b93d9bd | |||
| d7fb721b4d | |||
| e7cfa5c2bf | |||
| 2a3212a0a3 | |||
| 7a08a76875 | |||
| 22a09dddc6 | |||
| 8c72549cc2 | |||
| bba5214930 | |||
| fb194b9488 | |||
| 56817e9faa | |||
| e87fb8a8e1 | |||
| 5be0fc40ed | |||
| a98337b359 | |||
| 143d016899 | |||
| 3d70e55d72 | |||
| dbcc44dc80 | |||
| db87fd52ca | |||
| 792509e987 | |||
| 166e0d63d0 | |||
| 0d7f19bb62 | |||
| 607045d592 | |||
| 51d32243fe | |||
| 1c1a1bc9ed | |||
| 553fdb318f | |||
| 607ed4ee46 | |||
| ec1cece270 | |||
| 4656e386fb | |||
| f29f2f99b1 | |||
| cc27f08588 | |||
| 99fe398b59 | |||
| cb89e02432 | |||
| ac69392cd7 | |||
| a5fb372e1e | |||
| da720712f3 | |||
| 0cb3dc8782 | |||
| dfd95f0115 | |||
| 7636670a77 | |||
| fe6247a7f8 | |||
| 2b90ef1694 | |||
| 099138fb9a | |||
| cbe31d8dfd | |||
| 06b0930b6a | |||
| 7b7be341b6 | |||
| 751c77f87b | |||
| 634ac03c5e | |||
| 8cab53c64d | |||
| edef295b11 | |||
| 64e447354e | |||
| da1f7c762d | |||
| 89366bdbf9 | |||
| 78efa0e36c | |||
| 3a8b3db174 | |||
| 1a01e80b9c | |||
| d59027c40e | |||
| c197c2aa27 | |||
| 01cd34957e | |||
| 864517e5a2 | |||
| 1dd5d2ec1f | |||
| 6da835f4bc | |||
| 5cca077e4a | |||
| e290aa8c13 | |||
| 9c51d50318 | |||
| 1c3a46adda | |||
| 4407e81c61 | |||
| ad76e77fce | |||
| ac5b9055f6 | |||
| c18074a310 | |||
| ed703d8e2c | |||
| 9c53d0769e | |||
| 90532f5e3c | |||
| 2bc04d23fb | |||
| 7f6da764e1 | |||
| 6926ef8f67 | |||
| bba2b7cfce | |||
| dc1e55ce1a | |||
| 408e868237 | |||
| 97abb12473 | |||
| d26bffbc3f | |||
| 2f3bd9dae7 | |||
| 256e5dff55 | |||
| acb6b75fe9 | |||
| 683fd713c4 | |||
| 3591ae0103 | |||
| 5fedfd79a5 | |||
| bdde40e755 | |||
| 88c4963328 | |||
| 67a81eff42 | |||
| add43e91dc | |||
| b3c7a6d566 | |||
| 424bd49ede | |||
| 6a58404507 | |||
| d4ce8362b1 | |||
| caa12dbc57 | |||
| 1122b3c14d | |||
| a357649da5 | |||
| 332a3c7984 | |||
| fcd761b9d7 | |||
| b0d5f062e3 | |||
| 2c0753225a | |||
| 3b898664ee | |||
| 61fb5863df | |||
| a4ec297925 | |||
| 93d7e60d43 | |||
| 338264b5f6 | |||
| 19b51caa2c | |||
| 8c08b4373c | |||
| 0b38882a91 | |||
| 3364d69a3b | |||
| e6c9bfa4a9 | |||
| 7a3e182e9c | |||
| 2471f6b01c | |||
| 1fefafd09f | |||
| 8a63dc3151 | |||
| 403008816c | |||
| 62d552ffe2 | |||
| bcaa4217bc | |||
| 0823f6dfab | |||
| 87bb554aec | |||
| 36447cb2b5 | |||
| 3b2c6f09cb | |||
| 63414b9653 | |||
| 5f24bb0267 | |||
| 52519d45b9 | |||
| 78728df099 | |||
| 732db27cd6 | |||
| 3cad63fbd8 | |||
| d2be5939dc | |||
| 9a77d03047 | |||
| 8efcec67cc | |||
| 4fbd4bbd8d | |||
| 2fae296cbc | |||
| cef8466419 | |||
| 083159ebbe | |||
| 32e440cffc | |||
| 89c8c93b9a | |||
| 296074f548 | |||
| 2ccfaffa74 | |||
| 192672a162 | |||
| 7f4e658d3d | |||
| ff57695855 | |||
| ae8deb1246 | |||
| b9dcb35e9b | |||
| 25d9f5a804 | |||
| 1b234cb7af | |||
| d219442945 | |||
| 2b33be47cb | |||
| 499baced12 | |||
| 7aa9fecab8 | |||
| 9b6c82d804 | |||
| c3117b7544 | |||
| 67744384e8 | |||
| 17c401d09a | |||
| 488aea15f4 | |||
| 43df853ee3 | |||
| 28d5dcb578 | |||
| 7eb15c46a2 | |||
| 1fac36e2cb | |||
| 4b6c87b6e7 | |||
| ce6c2b2072 | |||
| 3e94a2c54d | |||
| b1e488f5d7 | |||
| 03d867160f | |||
| 9870e65c5f | |||
| aa839b9ff0 | |||
| fdb66aa237 | |||
| e0ca5fdd51 | |||
| 681c1c53e4 | |||
| 631c4863d8 | |||
| 944bda12c7 | |||
| bc36c4dea4 | |||
| 722766958b | |||
| 73fd3ca2eb | |||
| efe8ad51ed | |||
| d5b62465f0 | |||
| bc76e7255b | |||
| 8dd23ad2f2 | |||
| bce75d7c68 | |||
| 815053e403 | |||
| 6173abe20b | |||
| 6fcf0afa35 | |||
| 05521e276f | |||
| 38ffbbd7dd | |||
| 47e1878e4c |
@@ -12,3 +12,5 @@ angular.js.tmproj
|
||||
node_modules
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
|
||||
+12
-6
@@ -1,13 +1,19 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.10
|
||||
|
||||
env:
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt package
|
||||
- grunt webserver > /dev/null &
|
||||
- grunt ci-checks package
|
||||
- ./lib/sauce/sauce_connect_block.sh
|
||||
|
||||
script:
|
||||
- grunt test --browsers Firefox --reporters=dots
|
||||
- grunt parallel:travis --reporters dots --browsers SL_Chrome
|
||||
|
||||
+702
-39
@@ -1,3 +1,658 @@
|
||||
<a name="1.0.8"></a>
|
||||
# 1.0.8 bubble-burst (2013-08-22)
|
||||
|
||||
Contains only these fixes cherry-picked from [v1.2.0rc1](#1.2.0rc1).
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- don't check attr.specified on non-ie7
|
||||
([78efa0e3](https://github.com/angular/angular.js/commit/78efa0e36c1cb9fe293190381baa5a3fe5b3d1cb),
|
||||
[#3231](https://github.com/angular/angular.js/issues/3231), [#2160](https://github.com/angular/angular.js/issues/2160))
|
||||
- empty normalized href should pass sanitation check
|
||||
([3b2c6f09](https://github.com/angular/angular.js/commit/3b2c6f09cb857b86641cefde5b92d84d58c1118d),
|
||||
[#2219](https://github.com/angular/angular.js/issues/2219))
|
||||
- **$http:** ensure case-insensitive header overriding
|
||||
([25d9f5a8](https://github.com/angular/angular.js/commit/25d9f5a804b7a6a61db6e84e594b1b5fe7ea14bf))
|
||||
- **$location:**
|
||||
- default to / for the url base if no base[href]
|
||||
([cbe31d8d](https://github.com/angular/angular.js/commit/cbe31d8dfd12ce973c574bfc825ffc0ffb8eb7c4),
|
||||
[#2762](https://github.com/angular/angular.js/issues/2762))
|
||||
- prevent infinite digest error due to IE bug
|
||||
([97abb124](https://github.com/angular/angular.js/commit/97abb124738e0ca5d00d807d65c482f7890feadd),
|
||||
[#2802](https://github.com/angular/angular.js/issues/2802))
|
||||
- don't crash on invalid query parameters
|
||||
([b9dcb35e](https://github.com/angular/angular.js/commit/b9dcb35e9bc64cb2f48f3a349ead66c501cbdc48))
|
||||
- **$parse:** move global getter out of parse.js
|
||||
([099138fb](https://github.com/angular/angular.js/commit/099138fb9a94178d3d82568fbda28d0c87443de9))
|
||||
- **$q:** call `reject()` even if `$exceptionHandler` rethrows
|
||||
([d59027c4](https://github.com/angular/angular.js/commit/d59027c40ed73fa9e114706d0c5a885785311dec))
|
||||
- **$timeout:** clean deferreds immediately after callback exec/cancel
|
||||
([ac69392c](https://github.com/angular/angular.js/commit/ac69392cd7f939ebbd37765e377051d4c05df4a5))
|
||||
- **$sanitize:** match URI schemes case-insensitively
|
||||
([fcd761b9](https://github.com/angular/angular.js/commit/fcd761b9d7c3c91673efce9b980ac5e7973adf3d),
|
||||
[#3210](https://github.com/angular/angular.js/issues/3210))
|
||||
- **Scope:** watches can be safely unregistered inside watch handlers
|
||||
([a4ec2979](https://github.com/angular/angular.js/commit/a4ec297925f052bf9ea1aba9f584eaaf7472fb93),
|
||||
[#2915](https://github.com/angular/angular.js/issues/2915))
|
||||
|
||||
- **ngMock**
|
||||
- $timeout should forward delay argument
|
||||
([a5fb372e](https://github.com/angular/angular.js/commit/a5fb372e1e6aed8cdb1f572f1df3d6fe89388f3e))
|
||||
|
||||
- **jqLite:**
|
||||
- return array from multi select in val()
|
||||
([01cd3495](https://github.com/angular/angular.js/commit/01cd34957e778a2fa8d26e2805c2dd5a7f986465))
|
||||
- forgive unregistration of a non-registered handler
|
||||
([ac5b9055](https://github.com/angular/angular.js/commit/ac5b9055f6d7224e5e8e49941c0fc9cb16c64a7e))
|
||||
- prepend array in correct order
|
||||
([63414b96](https://github.com/angular/angular.js/commit/63414b965397a9fd7d2f49e8dea4b848e0d6707e))
|
||||
- correctly monkey-patch core jQuery methods
|
||||
([815053e4](https://github.com/angular/angular.js/commit/815053e403ace666b2383643227ecde5f36742c5))
|
||||
|
||||
- **Directives:**
|
||||
- **form:** pick the right attribute name for ngForm
|
||||
([dc1e55ce](https://github.com/angular/angular.js/commit/dc1e55ce1a314b6c1ad4b9d5b4a31226e1fa1e18),
|
||||
[#2997](https://github.com/angular/angular.js/issues/2997))
|
||||
- **input:** fix the email regex to accept TLDs up to 6 characters long
|
||||
([ad76e77f](https://github.com/angular/angular.js/commit/ad76e77fce09d0aee28b5ca1a328d5df8596b935))
|
||||
- **ngCloak:** hide element even when CSS 'display' is set
|
||||
([06b0930b](https://github.com/angular/angular.js/commit/06b0930b6a821bdfed78875f821baf1b8ede2442))
|
||||
- **ngSubmit:** expose $event to ngSubmit callback
|
||||
([b0d5f062](https://github.com/angular/angular.js/commit/b0d5f062e316370c7ac57cfd628d085015a8187d))
|
||||
- **ngValue:** made ngValue to write value attribute to element
|
||||
([3b898664](https://github.com/angular/angular.js/commit/3b898664eea9913b6b25261d7310a61de476d173))
|
||||
|
||||
- **Filters:**
|
||||
- **number:** always convert scientific notation to decimal
|
||||
([408e8682](https://github.com/angular/angular.js/commit/408e868237d80f9332f2c540f91b2809d9938fbc))
|
||||
- **orderBy:** remove redundant if statement
|
||||
([ec1cece2](https://github.com/angular/angular.js/commit/ec1cece270e293e7c55556fc68afee9a2ad40641))
|
||||
|
||||
- **i18n:** Do not transform arrays into objects
|
||||
([751c77f8](https://github.com/angular/angular.js/commit/751c77f87b34389c5b85a23c71080d367c42d31b))
|
||||
|
||||
- **jqLite:**
|
||||
- return array from multi select in val()
|
||||
([01cd3495](https://github.com/angular/angular.js/commit/01cd34957e778a2fa8d26e2805c2dd5a7f986465))
|
||||
- forgive unregistration of a non-registered handler
|
||||
([ac5b9055](https://github.com/angular/angular.js/commit/ac5b9055f6d7224e5e8e49941c0fc9cb16c64a7e))
|
||||
- prepend array in correct order
|
||||
([63414b96](https://github.com/angular/angular.js/commit/63414b965397a9fd7d2f49e8dea4b848e0d6707e))
|
||||
- correctly monkey-patch core jQuery methods
|
||||
([815053e4](https://github.com/angular/angular.js/commit/815053e403ace666b2383643227ecde5f36742c5))
|
||||
|
||||
- **Misc:**
|
||||
- **angular.copy:** change angular.copy to correctly clone RegExp
|
||||
([5cca077e](https://github.com/angular/angular.js/commit/5cca077e4a40a26cc2deee2a86a215f575f25b22),
|
||||
[#3473](https://github.com/angular/angular.js/issues/3473), [#3474](https://github.com/angular/angular.js/issues/3474))
|
||||
- **angular.equals:**
|
||||
- add support for regular expressions
|
||||
([a357649d](https://github.com/angular/angular.js/commit/a357649da5d9f0633fa8e8a249f58dfc1105698e),
|
||||
[#2685](https://github.com/angular/angular.js/issues/2685))
|
||||
- {} and [] should not be considered equivalent
|
||||
([da1f7c76](https://github.com/angular/angular.js/commit/da1f7c762d36b646c107260f74daf3a0ab5f91f5))
|
||||
- **angular.toJson:** skip JSON.stringify for undefined
|
||||
([332a3c79](https://github.com/angular/angular.js/commit/332a3c7984229a7e3a9a8a277f92942299616fdb))
|
||||
|
||||
|
||||
|
||||
<a name="1.2.0-rc1"></a>
|
||||
# 1.2.0-rc1 spooky-giraffe (2013-08-13)
|
||||
|
||||
[Full Commit Log](https://github.com/angular/angular.js/compare/v1.1.5...master)
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **ngAnimate:** complete rewrite of animations
|
||||
([81923f1e](https://github.com/angular/angular.js/commit/81923f1e41560327f7de6e8fddfda0d2612658f3))
|
||||
|
||||
- **$sce:** new $sce service for Strict Contextual Escaping and lots of other security enhancements
|
||||
([bea9422e](https://github.com/angular/angular.js/commit/bea9422ebfc8e80ee28ad81afc62d2e432c85cbb))
|
||||
|
||||
- **minErr:** add error message minification and better error messages
|
||||
([c8fcf3b3](https://github.com/angular/angular.js/commit/c8fcf3b369dbe866815e18e0fa4d71f3e679bc5f),
|
||||
[09fa0656](https://github.com/angular/angular.js/commit/09fa0656b49321681f28453abef566d0cbe0eb22),
|
||||
[b8ea7f6a](https://github.com/angular/angular.js/commit/b8ea7f6aba2e675b85826b0bee1f21ddd7b866a5))
|
||||
|
||||
- **$compile:**
|
||||
- support animation hooks bindings to class attributes
|
||||
([f2dfa891](https://github.com/angular/angular.js/commit/f2dfa8916f8ed855d55187f5400c4c2566ce9a1b))
|
||||
- support multi-element directive
|
||||
([e46100f7](https://github.com/angular/angular.js/commit/e46100f7097d9a8f174bdb9e15d4c6098395c3f2))
|
||||
- support "Controller as" instance syntax for directives
|
||||
([b3777f27](https://github.com/angular/angular.js/commit/b3777f275c6bd2bd4a88963fd03828eb7cf3aca8))
|
||||
|
||||
- **$http:** accept function as headers value
|
||||
([a7150f12](https://github.com/angular/angular.js/commit/a7150f1256f2a97a931b3c0d16eab70f45e81cae))
|
||||
|
||||
- **$q:**
|
||||
- add `.catch()` as shorthand for defining promise error handlers
|
||||
([a207665d](https://github.com/angular/angular.js/commit/a207665dad69248139b150cd3fe8ba13059bffb4),
|
||||
[#2048](https://github.com/angular/angular.js/issues/2048),
|
||||
[#3476](https://github.com/angular/angular.js/issues/3476))
|
||||
- added support for promise notification
|
||||
([2a5c3555](https://github.com/angular/angular.js/commit/2a5c3555829da51f55abd810a828c73b420316d3))
|
||||
|
||||
- **$resource:**
|
||||
- support an unescaped URL port in the url template
|
||||
([b94ca12f](https://github.com/angular/angular.js/commit/b94ca12fa0b027d8592f5717e038b7b116c59384),
|
||||
[#2778](https://github.com/angular/angular.js/issues/2778))
|
||||
- expose promise as `$promise` instead of only `$then`
|
||||
([05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d))
|
||||
|
||||
- **$route:** express style route matching (support for optional params and new wildcard syntax)
|
||||
([04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b))
|
||||
|
||||
- **jqLite:** switch bind/unbind to more recent jQuery on/off
|
||||
([f1b94b4b](https://github.com/angular/angular.js/commit/f1b94b4b599ab701bc75b55bbbbb73c5ef329a93))
|
||||
|
||||
- **Misc:**
|
||||
- add source maps to all min files
|
||||
([908071af](https://github.com/angular/angular.js/commit/908071afbf32c46fe9110e4a67e104bbd4b3a56b),
|
||||
[#1714](https://github.com/angular/angular.js/issues/1714))
|
||||
|
||||
- **Directives:**
|
||||
- add `ngFocus` and `ngBlur` directives
|
||||
([2bb27d49](https://github.com/angular/angular.js/commit/2bb27d4998805fd89db25192f53d26d259ae615f),
|
||||
[#1277](https://github.com/angular/angular.js/issues/1277))
|
||||
|
||||
- **ngRepeat:** add $even and $odd props to iterator
|
||||
([52b8211f](https://github.com/angular/angular.js/commit/52b8211fd0154b9d6b771a83573a161f5580d92c))
|
||||
|
||||
- **ngForm:** supports namespaces in form names
|
||||
([8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7))
|
||||
|
||||
- **ngBindHtml:** combine ng-bind-html and ng-bind-html-unsafe
|
||||
([dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55))
|
||||
|
||||
- **ngPluralize:** add alternative mapping using attributes
|
||||
([a170fc1a](https://github.com/angular/angular.js/commit/a170fc1a749effa98bfd1c2e1b30297ed47b451b),
|
||||
[#2454](https://github.com/angular/angular.js/issues/2454))
|
||||
|
||||
- **ngMobile/ngTouch:**
|
||||
- emit `swipeleft` and `swiperight` events
|
||||
([ab189142](https://github.com/angular/angular.js/commit/ab189142988043d0513bb796c3b54ca7d07f242d))
|
||||
- refactor swipe logic from `ngSwipe` directive to `$swipe` service.
|
||||
([f4c6b2c7](https://github.com/angular/angular.js/commit/f4c6b2c7894cb2d82ac69a1500a27785360b81c3))
|
||||
|
||||
- **ngMock:**
|
||||
- $timeout.flushNext can expect specific timeout delays
|
||||
([462ed033](https://github.com/angular/angular.js/commit/462ed033d512ae94cb188efc9453de84ace4e17e))
|
||||
- support delay limit for $timeout.flush
|
||||
([b7fdabc4](https://github.com/angular/angular.js/commit/b7fdabc4bf2a9dd11a57f98c5229d834c4589bab))
|
||||
- support a matching function for data param
|
||||
([08daa779](https://github.com/angular/angular.js/commit/08daa7797bce5207916251d4a0ab3d5c93e5529a))
|
||||
|
||||
|
||||
|
||||
|
||||
- **scenario:** expose jQuery for usage outside of angular scenario
|
||||
([3fdbe81a](https://github.com/angular/angular.js/commit/3fdbe81a337c39027929c415e719493755cd8583))
|
||||
|
||||
- **ngDocs:**
|
||||
- provide support for user to jump between different versions of the angularjs doc
|
||||
([46dfb92a](https://github.com/angular/angular.js/commit/46dfb92afd185c93f60ca90a72653f33d7cb18e8))
|
||||
- add links to source for API
|
||||
([52d6a599](https://github.com/angular/angular.js/commit/52d6a5990225439ac9141398d83e0d4e6134b576))
|
||||
- support popover, foldouts and foldover annotations
|
||||
([ef229688](https://github.com/angular/angular.js/commit/ef22968810d555f78d3bbf7b5428757690c8cc70))
|
||||
- provide documentation for the new ngRepeat repeater syntax
|
||||
([b3650457](https://github.com/angular/angular.js/commit/b36504577c538b745e6270e77d86af90285e2ae6))
|
||||
- provide support for inline variable hinting
|
||||
([21c70729](https://github.com/angular/angular.js/commit/21c70729d9269de85df3434c431c2f18995b0f7b))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- correct controller instantiation for async directives
|
||||
([c173ca41](https://github.com/angular/angular.js/commit/c173ca412878d537b18df01f39e400ea48a4b398),
|
||||
[#3493](https://github.com/angular/angular.js/issues/3493),
|
||||
[#3482](https://github.com/angular/angular.js/issues/3482),
|
||||
[#3537](https://github.com/angular/angular.js/issues/3537),
|
||||
[#3540](https://github.com/angular/angular.js/issues/3540))
|
||||
- always instantiate controllers before pre-link fns run
|
||||
([5c560117](https://github.com/angular/angular.js/commit/5c560117425e7b3f7270389274476e843d6f69ec),
|
||||
[#3493](https://github.com/angular/angular.js/issues/3493),
|
||||
[#3482](https://github.com/angular/angular.js/issues/3482),
|
||||
[#3514](https://github.com/angular/angular.js/issues/3514))
|
||||
- always instantiate controllers in parent->child order
|
||||
([45f9f623](https://github.com/angular/angular.js/commit/45f9f62367221b2aa097ba1d87d744e50140ddc7),
|
||||
[#2738](https://github.com/angular/angular.js/issues/2738))
|
||||
- don't check attr.specified on non-ie7
|
||||
([f9ea69f6](https://github.com/angular/angular.js/commit/f9ea69f6567c22ff328fd1f7b07847883757bfa6),
|
||||
[#3231](https://github.com/angular/angular.js/issues/3231),
|
||||
[#2160](https://github.com/angular/angular.js/issues/2160))
|
||||
- allow `data:` image URIs in `img[src]` bindings
|
||||
([3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b))
|
||||
- empty normalized href url should pass sanitation check
|
||||
([fc8c9baa](https://github.com/angular/angular.js/commit/fc8c9baa399c33956133cdb6892fc7007430d299),
|
||||
[#2219](https://github.com/angular/angular.js/issues/2219))
|
||||
- prevent infinite loop w/ replace+transclude directives
|
||||
([69f42b76](https://github.com/angular/angular.js/commit/69f42b76548d00f52b231ec91150e4f0b008c730),
|
||||
[#2155](https://github.com/angular/angular.js/issues/2155))
|
||||
- reject multi-expression interpolations for `src` attribute
|
||||
([38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739))
|
||||
- disallow interpolations for DOM event handlers
|
||||
([39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56))
|
||||
- sanitize values bound to `img[src]`
|
||||
([1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97))
|
||||
- support multi-element group over text nodes
|
||||
([b28f9694](https://github.com/angular/angular.js/commit/b28f96949ac477b1fe43c81df7cedc21c7ab184c))
|
||||
- correct component transclusion on compilation root.
|
||||
([15e1a29c](https://github.com/angular/angular.js/commit/15e1a29cd08993b599f390e83a249ec17f753972))
|
||||
|
||||
- **$http:**
|
||||
- allow interceptors to completely override headers
|
||||
([514dc0eb](https://github.com/angular/angular.js/commit/514dc0eb16a8fe3fa7c44094d743714f73754321),
|
||||
[#2770](https://github.com/angular/angular.js/issues/2770))
|
||||
- treat headers as case-insensitive when overriding defaults
|
||||
([53359d54](https://github.com/angular/angular.js/commit/53359d549e364759d5b382c229f7d326799bf418))
|
||||
|
||||
- **$location:**
|
||||
- don't initialize url hash in hashbang mode unnecessarily
|
||||
([d4d34aba](https://github.com/angular/angular.js/commit/d4d34aba6efbd98050235f5b264899bb788117df))
|
||||
- prevent infinite digest error due to IE bug
|
||||
([dca23173](https://github.com/angular/angular.js/commit/dca23173e25a32cb740245ca7f7b01a84805f43f),
|
||||
[#2802](https://github.com/angular/angular.js/issues/2802))
|
||||
- in html5 mode, default to / for the url base if no `base[href]`
|
||||
([aef09800](https://github.com/angular/angular.js/commit/aef098006302689d2d75673be828e31903ee7c3c),
|
||||
[#2762](https://github.com/angular/angular.js/issues/2762))
|
||||
- fix parameter handling on search()
|
||||
([705c9d95](https://github.com/angular/angular.js/commit/705c9d95bc3157547ac6008d2f0a6a0c0e0ca60a))
|
||||
|
||||
- **$parse:**
|
||||
- unwrap promise when setting a field
|
||||
([61906d35](https://github.com/angular/angular.js/commit/61906d3517428b6d52d3284b8d26d1a46e01dad7),
|
||||
[#1827](https://github.com/angular/angular.js/issues/1827))
|
||||
- disallow access to Function constructor
|
||||
([5349b200](https://github.com/angular/angular.js/commit/5349b20097dc5cdff0216ee219ac5f6e6ef8c219))
|
||||
|
||||
- **$q:** call `reject()` even if `$exceptionHandler` rethrows
|
||||
([664526d6](https://github.com/angular/angular.js/commit/664526d69c927370c93a06745ca38de7cd03a7be))
|
||||
|
||||
- **$resource:** check whether response matches action.isArray
|
||||
([a644ca7b](https://github.com/angular/angular.js/commit/a644ca7b4e6ba84a467bcabed8f99386eda7fb14),
|
||||
[#2255](https://github.com/angular/angular.js/issues/2255))
|
||||
|
||||
- **$sanitize:** match URI schemes case-insensitively
|
||||
([7fef06fe](https://github.com/angular/angular.js/commit/7fef06fef9b6af4436f9fed10bd29d0a63707614),
|
||||
[#3210](https://github.com/angular/angular.js/issues/3210))
|
||||
|
||||
- **Scope:**
|
||||
- ensure that isolate scopes use the main evalAsync queue
|
||||
([3967f5f7](https://github.com/angular/angular.js/commit/3967f5f7d6c8aa7b41a5352b12f457e2fbaa251a))
|
||||
- watches can now be safely unregistered inside watch handlers
|
||||
([8bd6619b](https://github.com/angular/angular.js/commit/8bd6619b7efa485b020fec96c76047e480469871),
|
||||
[#2915](https://github.com/angular/angular.js/issues/2915))
|
||||
|
||||
- **jqLite:**
|
||||
- properly detect unsupported calls for on()/off()
|
||||
([3824e400](https://github.com/angular/angular.js/commit/3824e40011df1c0fdf5964d78776f1a12a29c144),
|
||||
[4f5dfbc3](https://github.com/angular/angular.js/commit/4f5dfbc362d9683177708ebcc00c98cf594d1287),
|
||||
[#3501](https://github.com/angular/angular.js/issues/3501))
|
||||
- return array from multi select in val()
|
||||
([306a6134](https://github.com/angular/angular.js/commit/306a613440175c7fd61d1d6eb249d1e53a46322e))
|
||||
- forgive unregistration of a non-registered handler
|
||||
([ab59cc6c](https://github.com/angular/angular.js/commit/ab59cc6c44705b1244a77eba999d736f9eb3c6ae))
|
||||
- support space-separated events in off
|
||||
([bdd4e982](https://github.com/angular/angular.js/commit/bdd4e982b7fee9811b40b545c21a74711686875c),
|
||||
[#3256](https://github.com/angular/angular.js/issues/3256))
|
||||
- prepend array in correct order
|
||||
([fd87eb0c](https://github.com/angular/angular.js/commit/fd87eb0ca5e14f213d8b31280d444dbc29c20c50))
|
||||
- allow override of jqLite.triggerHandler event object
|
||||
([0cac8729](https://github.com/angular/angular.js/commit/0cac8729fb3824ebb07cee84ef78b43900c7e75d))
|
||||
- added optional name arg in removeData
|
||||
([e1a050e6](https://github.com/angular/angular.js/commit/e1a050e6b26aca4d0e6e7125d3f6c1c8fc1d92cb))
|
||||
- correctly monkey-patch core jQuery methods
|
||||
([da5f537c](https://github.com/angular/angular.js/commit/da5f537ccdb0a7b4155f13f7a70ca7981ad6f689))
|
||||
|
||||
|
||||
- **i18n:** Do not transform arrays into objects
|
||||
([b3d7a038](https://github.com/angular/angular.js/commit/b3d7a038d774d823ef861b76fb8bfa22e60a3df5))
|
||||
|
||||
- **ngMobile/ngTouch:**
|
||||
- emit click event for touchy clicks
|
||||
([fb7d891d](https://github.com/angular/angular.js/commit/fb7d891dacdcb9f799061d5fbb96cdd2dd912196),
|
||||
[#3219](https://github.com/angular/angular.js/issues/3219),
|
||||
[#3218](https://github.com/angular/angular.js/issues/3218),
|
||||
[#3137](https://github.com/angular/angular.js/issues/3137))
|
||||
- prevent ngClick when item disabled
|
||||
([e0340243](https://github.com/angular/angular.js/commit/e03402433d2524fd3a74bbfce984f843794996ce),
|
||||
[#3124](https://github.com/angular/angular.js/issues/3124),
|
||||
[#3132](https://github.com/angular/angular.js/issues/3132))
|
||||
- ngClick should prevent unwanted opening of the soft keyboard
|
||||
([0bbd20f2](https://github.com/angular/angular.js/commit/0bbd20f255b2954b5c41617fe718cf6eca36a972))
|
||||
|
||||
- **ngMock:**
|
||||
- keep withCredentials on passThrough
|
||||
([3079a6f4](https://github.com/angular/angular.js/commit/3079a6f4e097a777414b8c3a8a87b8e1e20b55b5))
|
||||
- keep mock.$log the api in sync with $log
|
||||
([f274c0a6](https://github.com/angular/angular.js/commit/f274c0a66b28711d3b9cc7b0775e97755dd971e8),
|
||||
[#2343](https://github.com/angular/angular.js/issues/2343))
|
||||
|
||||
- **ngScenario:** select().option(val) should prefer exact value match
|
||||
([22a9b1ac](https://github.com/angular/angular.js/commit/22a9b1ac07f98d07e1e5d71ce961411b5fa9b42d),
|
||||
[#2856](https://github.com/angular/angular.js/issues/2856))
|
||||
|
||||
- **Directives:**
|
||||
- **ngRepeat:**
|
||||
- handle iteration over identical obj values
|
||||
([47a2a982](https://github.com/angular/angular.js/commit/47a2a9829f0a847bbee61cd142c43000d73ea98b),
|
||||
[#2787](https://github.com/angular/angular.js/issues/2787),
|
||||
[#2806](https://github.com/angular/angular.js/issues/2806))
|
||||
- support growing over multi-element groups
|
||||
([4953b497](https://github.com/angular/angular.js/commit/4953b49761a791d9ea74bcbe78769fec15d91083))
|
||||
|
||||
- **ngShowHide:** change the .ng-hide CSS class to use an !important flag
|
||||
([246c1439](https://github.com/angular/angular.js/commit/246c1439b502b06823650505cbe4a3848b6fa5a3))
|
||||
|
||||
- **ngSubmit:** expose $event to ngSubmit callback
|
||||
([3371fc25](https://github.com/angular/angular.js/commit/3371fc254a9698eae35bb6f8f1ee9c434ae761e2))
|
||||
|
||||
- **ngValue:** made ngValue to write value attribute to element
|
||||
([09a1e7af](https://github.com/angular/angular.js/commit/09a1e7af129880cab89a2f709f22a7286f52371e))
|
||||
|
||||
- **ngView:** ensure ngView is terminal and uses its own manual transclusion system
|
||||
([87405e25](https://github.com/angular/angular.js/commit/87405e25ae935eefd673e70ffd6144a5f455b662))
|
||||
|
||||
- **ngCloak:** hide ngCloak-ed element even when CSS 'display' is set
|
||||
([3ffddad1](https://github.com/angular/angular.js/commit/3ffddad100e993403d13137387d0685466b46b2b))
|
||||
|
||||
- **`input[email]`:** fix the email regex to accept TLDs up to 6 characters long
|
||||
([af731354](https://github.com/angular/angular.js/commit/af731354b0b600f87f15e1573e64a7f7acc70f3d))
|
||||
|
||||
- **form:** pick the right attribute name for ngForm
|
||||
([0fcd1e3b](https://github.com/angular/angular.js/commit/0fcd1e3b1fa6244d02f08631d9ef81bf79996fab),
|
||||
[#2997](https://github.com/angular/angular.js/issues/2997))
|
||||
|
||||
- **select:** don't support binding to `select[multiple]`
|
||||
([d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114),
|
||||
[#3230](https://github.com/angular/angular.js/issues/3230))
|
||||
|
||||
- **Filters:**
|
||||
- **numberFilter:** always convert scientific notation to decimal
|
||||
([a13c01a8](https://github.com/angular/angular.js/commit/a13c01a8e48ea4a0d59394eb94f1b12c50cfef61))
|
||||
|
||||
- **Misc:**
|
||||
- detect transition/animation on older Android browsers
|
||||
([ef5bc6c7](https://github.com/angular/angular.js/commit/ef5bc6c7c3336a64bae64fe9739cb1789907c906))
|
||||
- handle duplicate params in parseKeyValue/toKeyValue
|
||||
([80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe))
|
||||
- don't crash on invalid query parameters
|
||||
([8264d080](https://github.com/angular/angular.js/commit/8264d08085adc2ab57f6598b9fc9f6e263c8b4f3))
|
||||
- change angular.copy to correctly clone RegExp
|
||||
([f80730f4](https://github.com/angular/angular.js/commit/f80730f497cb1ecb78a814f01df79b69223ad633),
|
||||
[#3473](https://github.com/angular/angular.js/issues/3473),
|
||||
[#3474](https://github.com/angular/angular.js/issues/3474))
|
||||
- angular.equals now supports for regular expressions
|
||||
([724819e3](https://github.com/angular/angular.js/commit/724819e3cfd8aeda1f724fb527db2b57494be9b7),
|
||||
[#2685](https://github.com/angular/angular.js/issues/2685))
|
||||
- angular.equals should not match keys defined in the prototype chain
|
||||
([7829c50f](https://github.com/angular/angular.js/commit/7829c50f9e89e779980f6d60a397aedfc7eaec61))
|
||||
- angular.equals should not consider {} and [] to be equivalent
|
||||
([1dcafd18](https://github.com/angular/angular.js/commit/1dcafd18afed4465ee13db91cedc8fecc3aa2c96))
|
||||
- angular.bootstrap should throw an error when bootstrapping a bootstrapped element
|
||||
([3ee744cc](https://github.com/angular/angular.js/commit/3ee744cc63a24b127d6a5f632934bb6ed2de275a))
|
||||
- angular.toJson should skip JSON.stringify for undefined
|
||||
([5a294c86](https://github.com/angular/angular.js/commit/5a294c8646452d6e49339d145faeae4f31dcd0fc))
|
||||
- change css wrapping in grunt to prepend styles to the top of the head tag
|
||||
([fbad068a](https://github.com/angular/angular.js/commit/fbad068aeb229fd3dd2a3004879584c728fed735))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **ngAnimate:** due to [81923f1e](https://github.com/angular/angular.js/commit/81923f1e41560327f7de6e8fddfda0d2612658f3),
|
||||
too many things changed, we'll write up a separate doc with migration instructions and will publish it at <http://yearofmoo.com>. Please check out the [ngAnimate module docs](http://ci.angularjs.org/job/angular.js-angular-master/lastSuccessfulBuild/artifact/build/docs/api/ngAnimate) and [$animate api docs](http://ci.angularjs.org/job/angular.js-angular-master/lastSuccessfulBuild/artifact/build/docs/api/ng.$animate) in the meantime.
|
||||
|
||||
- **$compile:**
|
||||
- due to [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97) and [3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b),
|
||||
`img[src]` URLs are now being sanitized and a whitelist configured via `$compileProvider` can be used to configure what safe urls look like.
|
||||
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`. Therefore this change is expected to have no impact on apps that don't contain malicious image links.
|
||||
|
||||
- due to [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739),
|
||||
binding more than a single expression to `*[src]` or `*[ng-src]` with the exception of `<a>` and `<img>` elements is not supported.
|
||||
|
||||
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. (but not `img[src/ng-src]` since that value is sanitized).
|
||||
|
||||
This change ensures that the possible pool of values that are used for data-binding is easier to trace down.
|
||||
|
||||
To migrate your code, follow the example below:
|
||||
|
||||
Before:
|
||||
JS:
|
||||
scope.baseUrl = 'page';
|
||||
scope.a = 1;
|
||||
scope.b = 2;
|
||||
HTML:
|
||||
<!-- Are a and b properly escaped here? Is baseUrl
|
||||
controlled by user? -->
|
||||
<iframe src="{{baseUrl}}?a={{a}&b={{b}}">
|
||||
|
||||
After:
|
||||
JS:
|
||||
var baseUrl = "page";
|
||||
scope.getIframeSrc = function() {
|
||||
// There are obviously better ways to do this. The
|
||||
// key point is that one will think about this and do
|
||||
// it the right way.
|
||||
var qs = ["a", "b"].map(function(value, name) {
|
||||
return encodeURIComponent(name) + "=" +
|
||||
encodeURIComponent(value);
|
||||
}).join("&");
|
||||
// baseUrl isn't on scope so it isn't bound to a user
|
||||
// controlled value.
|
||||
return baseUrl + "?" + qs;
|
||||
}
|
||||
HTML: <iframe src="{{getIframeSrc()}}">
|
||||
|
||||
- due to [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56),
|
||||
Interpolations inside DOM event handlers are disallowed.
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
|
||||
|
||||
To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
JS: scope.foo = 'alert(1)';
|
||||
HTML: <div onclick="{{foo}}">
|
||||
|
||||
After:
|
||||
|
||||
JS: scope.foo = function() { alert(1); }
|
||||
HTML: <div ng-click="foo()">
|
||||
|
||||
- **$q:** due to [f078762d](https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c),
|
||||
the `always` method is now exposed as `finally`.
|
||||
|
||||
The reason for this change is to align `$q` with the Q promise library, despite the fact that this makes it a bit more difficult to use with non-ES5 browsers, like IE8.
|
||||
|
||||
`finally` also goes well together with `catch` api that was added to $q recently and is part of the DOM promises standard.
|
||||
|
||||
To migrate the code follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
$http.get('/foo').always(doSomething);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
$http.get('/foo').finally(doSomething);
|
||||
```
|
||||
|
||||
or for IE8 compatible code:
|
||||
|
||||
```
|
||||
$http.get('/foo')['finally'](doSomething);
|
||||
```
|
||||
|
||||
- **$resource:**
|
||||
- due to [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d),
|
||||
resource instance does not have a `$then` function anymore. Use the `$promise.then` instead.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
Resource.query().$then(callback);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
Resource.query().$promise.then(callback);
|
||||
```
|
||||
|
||||
- due to [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d), instance methods return the promise rather than the instance itself.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
resource.$save().chaining = true;
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
resource.$save();
|
||||
resource.chaining = true;
|
||||
```
|
||||
|
||||
- due to [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d), on success, the resource promise is resolved with the resource instance rather than http response object.
|
||||
|
||||
Use interceptor api to access the http response object.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
Resource.query().$then(function(response) {...});
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
var Resource = $resource('/url', {}, {
|
||||
get: {
|
||||
method: 'get',
|
||||
interceptor: {
|
||||
response: function(response) {
|
||||
// expose response
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
- **$route:**
|
||||
- due to [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b),
|
||||
the syntax for named wildcard parameters in routes has changed from `*wildcard` to `:wildcard*`
|
||||
|
||||
To migrate the code, follow the example below. Here, `*highlight` becomes
|
||||
`:highlight*`:
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
|
||||
{controller: noop, templateUrl: 'Chapter.html'});
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
$routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||||
{controller: noop, templateUrl: 'Chapter.html'});
|
||||
```
|
||||
|
||||
- due to [5599b55b](https://github.com/angular/angular.js/commit/5599b55b04788c2e327d7551a4a699d75516dd21),
|
||||
applications that use `$route` will now need to load an angular-route.js file and define a dependency on the ngRoute module.
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
...
|
||||
<script src="angular.js"></script>
|
||||
...
|
||||
var myApp = angular.module('myApp', ['someOtherModule']);
|
||||
...
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
...
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular-route.js"></script>
|
||||
...
|
||||
var myApp = angular.module('myApp', ['ngRoute', 'someOtherModule']);
|
||||
...
|
||||
```
|
||||
|
||||
- **$location:** due to [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe),
|
||||
`$location.search` now supports multiple keys with the same value provided that the values are stored in an array in `$location.search`.
|
||||
|
||||
Before this change:
|
||||
- `parseKeyValue` only took the last key overwriting all the previous keys;
|
||||
- `toKeyValue` joined the keys together in a comma delimited string.
|
||||
|
||||
This was deemed buggy behavior. If your server relied on this behavior then either the server should be fixed, or a simple serialization of the array should be done on the client before passing it to $location.
|
||||
|
||||
- **ngBindHtml, sce:** due to [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55),
|
||||
|
||||
`ngHtmlBindUnsafe` has been removed and replaced by `ngHtmlBind` (which has been moved from `ngSanitize` module to the core `ng` module). `ngBindHtml` provides `ngHtmlBindUnsafe` like behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via `$sanitize` before being innerHTML'd. If the `$sanitize` service isn't available (`ngSanitize` module is not loaded) and the bound expression evaluates to a value that is not trusted an exception is thrown.
|
||||
|
||||
- **ngForm:** due to [8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7),
|
||||
|
||||
If you have form names that will evaluate as an expression:
|
||||
|
||||
```
|
||||
<form name="ctrl.form">
|
||||
```
|
||||
|
||||
And if you are accessing the form from your controller:
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
function($scope) {
|
||||
$scope['ctrl.form'] // form controller instance
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
function($scope) {
|
||||
$scope.ctrl.form // form controller instance
|
||||
}
|
||||
```
|
||||
|
||||
This makes it possible to access a form from a controller using the new "controller as" syntax. Supporting the previous behavior offers no benefit.
|
||||
|
||||
- **ngView:** due to [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d6fe57a6c45561a05b182),
|
||||
previously ngView only updated its content, after this change ngView will recreate itself every time a new content is included. This ensures that a single rootElement for all the included contents always exists, which makes definition of css styles for animations much easier.
|
||||
|
||||
- **ngInclude:** due to [aa2133ad](https://github.com/angular/angular.js/commit/aa2133ad818d2e5c27cbd3933061797096356c8a),
|
||||
previously ngInclude only updated its content, after this change ngInclude will recreate itself every time a new content is included. This ensures that a single rootElement for all the included contents always exists, which makes definition of css styles for animations much easier.
|
||||
|
||||
- **select:** due to [d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114),
|
||||
binding to `select[multiple]` directly or via ngMultiple (ng-multiple) directive is not supported. This feature never worked with two-way data-binding, so it's not expected that anybody actually depends on it.
|
||||
|
||||
- **ngMobile:** due to [94ec84e7](https://github.com/angular/angular.js/commit/94ec84e7b9c89358dc00e4039009af9e287bbd05),
|
||||
since all the code in the ngMobile module is touch related, we are renaming the module to ngTouch.
|
||||
|
||||
To migrate, please replace all references to "ngMobile" with "ngTouch" and "angular-mobile.js" to "angular-touch.js".
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.1.5"></a>
|
||||
# 1.1.5 triangle-squarification (2013-05-22)
|
||||
|
||||
@@ -83,54 +738,52 @@ _Note: This release also contains all bug fixes available in [1.0.7](#1.0.7)._
|
||||
([de2cdb06](https://github.com/angular/angular.js/commit/de2cdb0658b8b8cff5a59e26c5ec1c9b470efb9b))
|
||||
|
||||
- **$location:**
|
||||
- prevent navigation when event isDefaultPrevented
|
||||
([2c69a673](https://github.com/angular/angular.js/commit/2c69a6735e8af5d1b9b73fd221274d374e8efdea))
|
||||
- compare against actual instead of current URL
|
||||
([a348e90a](https://github.com/angular/angular.js/commit/a348e90aa141921b914f87ec930cd6ebf481a446))
|
||||
- prevent navigation if already on the URL
|
||||
([4bd7bedf](https://github.com/angular/angular.js/commit/4bd7bedf48c0c1ebb62f6bd8c85e8ea00f94502b))
|
||||
- fix URL interception in hash-bang mode
|
||||
([58ef3230](https://github.com/angular/angular.js/commit/58ef32308f45141c8f7f7cc32a6156cd328ba692),
|
||||
[#1051](https://github.com/angular/angular.js/issues/1051))
|
||||
- correctly rewrite Html5 urls
|
||||
([77ff1085](https://github.com/angular/angular.js/commit/77ff1085554675f1a8375642996e5b1e51f9ed2d))
|
||||
- prevent navigation when event isDefaultPrevented
|
||||
([2c69a673](https://github.com/angular/angular.js/commit/2c69a6735e8af5d1b9b73fd221274d374e8efdea))
|
||||
- compare against actual instead of current URL
|
||||
([a348e90a](https://github.com/angular/angular.js/commit/a348e90aa141921b914f87ec930cd6ebf481a446))
|
||||
- prevent navigation if already on the URL
|
||||
([4bd7bedf](https://github.com/angular/angular.js/commit/4bd7bedf48c0c1ebb62f6bd8c85e8ea00f94502b))
|
||||
- fix URL interception in hash-bang mode
|
||||
([58ef3230](https://github.com/angular/angular.js/commit/58ef32308f45141c8f7f7cc32a6156cd328ba692),
|
||||
[#1051](https://github.com/angular/angular.js/issues/1051))
|
||||
- correctly rewrite Html5 urls
|
||||
([77ff1085](https://github.com/angular/angular.js/commit/77ff1085554675f1a8375642996e5b1e51f9ed2d))
|
||||
|
||||
- **$resource:**
|
||||
- null default param results in TypeError
|
||||
([cefbcd47](https://github.com/angular/angular.js/commit/cefbcd470d4c9020cc3487b2326d45058ef831e2))
|
||||
- collapse empty suffix parameters correctly
|
||||
([53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336))
|
||||
- null default param results in TypeError
|
||||
([cefbcd47](https://github.com/angular/angular.js/commit/cefbcd470d4c9020cc3487b2326d45058ef831e2))
|
||||
- collapse empty suffix parameters correctly
|
||||
([53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336))
|
||||
|
||||
- **$rootScope:**
|
||||
- ensure $watchCollection correctly handles arrayLike objects
|
||||
- **$rootScope:** ensure $watchCollection correctly handles arrayLike objects
|
||||
([6452707d](https://github.com/angular/angular.js/commit/6452707d4098235bdbde34e790aee05a1b091218))
|
||||
|
||||
- **date filter:** correctly format dates with more than 3 sub-second digits
|
||||
([4f2e3606](https://github.com/angular/angular.js/commit/4f2e36068502f18814fee0abd26951124881f951))
|
||||
|
||||
- **jqLite:**
|
||||
- pass a dummy event into triggerHandler
|
||||
- **jqLite:** pass a dummy event into triggerHandler
|
||||
([0401a7f5](https://github.com/angular/angular.js/commit/0401a7f598ef9a36ffe1f217e1a98961046fa551))
|
||||
|
||||
- **Directives:**
|
||||
- **ngAnimate:**
|
||||
- eval ng-animate expression on each animation
|
||||
([fd21c750](https://github.com/angular/angular.js/commit/fd21c7502f0a25364a810c26ebeecb678e5783c5))
|
||||
- prevent animation on initial page load
|
||||
([570463a4](https://github.com/angular/angular.js/commit/570463a465fae02efc33e5a1fa963437cdc275dd))
|
||||
- skip animation on first render
|
||||
([1351ba26](https://github.com/angular/angular.js/commit/1351ba2632b5011ad6eaddf004a7f0411bea8453))
|
||||
- **ngPattern:** allow modifiers on inline ng-pattern
|
||||
([12b6deb1](https://github.com/angular/angular.js/commit/12b6deb1ce99df64e2fc91a06bf05cd7f4a3a475),
|
||||
[#1437](https://github.com/angular/angular.js/issues/1437))
|
||||
- **ngRepeat:**
|
||||
- correctly iterate over array-like objects
|
||||
([1d8e11dd](https://github.com/angular/angular.js/commit/1d8e11ddfbd6b08ff02df4331f6df125f49da3dc),
|
||||
[#2546](https://github.com/angular/angular.js/issues/2546))
|
||||
- prevent initial duplicates
|
||||
([a0bc71e2](https://github.com/angular/angular.js/commit/a0bc71e27107c58282e71415c4e8d89e916ae99c))
|
||||
- **ngView:** accidentally compiling leaving content
|
||||
([9956baed](https://github.com/angular/angular.js/commit/9956baedd73d5e8d0edd04c9eed368bd3988444b))
|
||||
- **ngAnimate:**
|
||||
- eval ng-animate expression on each animation
|
||||
([fd21c750](https://github.com/angular/angular.js/commit/fd21c7502f0a25364a810c26ebeecb678e5783c5))
|
||||
- prevent animation on initial page load
|
||||
([570463a4](https://github.com/angular/angular.js/commit/570463a465fae02efc33e5a1fa963437cdc275dd))
|
||||
- skip animation on first render
|
||||
([1351ba26](https://github.com/angular/angular.js/commit/1351ba2632b5011ad6eaddf004a7f0411bea8453))
|
||||
- **ngPattern:** allow modifiers on inline ng-pattern
|
||||
([12b6deb1](https://github.com/angular/angular.js/commit/12b6deb1ce99df64e2fc91a06bf05cd7f4a3a475),
|
||||
[#1437](https://github.com/angular/angular.js/issues/1437))
|
||||
- **ngRepeat:**
|
||||
- correctly iterate over array-like objects
|
||||
([1d8e11dd](https://github.com/angular/angular.js/commit/1d8e11ddfbd6b08ff02df4331f6df125f49da3dc),
|
||||
[#2546](https://github.com/angular/angular.js/issues/2546))
|
||||
- prevent initial duplicates
|
||||
([a0bc71e2](https://github.com/angular/angular.js/commit/a0bc71e27107c58282e71415c4e8d89e916ae99c))
|
||||
- **ngView:** accidentally compiling leaving content
|
||||
([9956baed](https://github.com/angular/angular.js/commit/9956baedd73d5e8d0edd04c9eed368bd3988444b))
|
||||
|
||||
- **scenario runner:** correct bootstrap issue on IE
|
||||
([ab755a25](https://github.com/angular/angular.js/commit/ab755a25f9ca3f3f000623071d8de3ddc4b1d78e))
|
||||
@@ -139,11 +792,21 @@ _Note: This release also contains all bug fixes available in [1.0.7](#1.0.7)._
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$animator/ngAnimate:**
|
||||
- **$animator/ngAnimate:** due to [11f712bc](https://github.com/angular/angular.js/commit/11f712bc3e310302eb2e8691cf6d110bdcde1810),
|
||||
css transition classes changed from `foo-setup`/`foo-start` to `foo`/`foo-active`
|
||||
|
||||
The CSS transition classes have changed suffixes. To migrate rename
|
||||
|
||||
.foo-setup {...} to .foo {...}
|
||||
.foo-start {...} to .foo-active {...}
|
||||
|
||||
or for type: enter, leave, move, show, hide
|
||||
|
||||
.foo-type-setup {...} to .foo-type {...}
|
||||
.foo-type-start {...} to .foo-type-active {...}
|
||||
|
||||
- **$resource:** due to [53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336),
|
||||
A `/` followed by a `.`, in the last segment of the URL template is now collapsed into a single `.` delimiter.
|
||||
a `/` followed by a `.`, in the last segment of the URL template is now collapsed into a single `.` delimiter.
|
||||
|
||||
For example: `users/.json` will become `users.json`. If your server relied upon this sequence then it will no longer
|
||||
work. In this case you can now escape the `/.` sequence with `/\.`
|
||||
|
||||
+59
-2
@@ -7,6 +7,9 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-ddescribe-iit');
|
||||
grunt.loadNpmTasks('grunt-merge-conflict');
|
||||
grunt.loadNpmTasks('grunt-parallel');
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
@@ -21,6 +24,20 @@ module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
|
||||
parallel: {
|
||||
travis: {
|
||||
options: {
|
||||
stream: true,
|
||||
},
|
||||
tasks: [
|
||||
util.parallelTask('test:modules'),
|
||||
util.parallelTask('test:jquery'),
|
||||
util.parallelTask('test:jqlite'),
|
||||
util.parallelTask('test:e2e')
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
connect: {
|
||||
devserver: {
|
||||
options: {
|
||||
@@ -40,7 +57,29 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}
|
||||
},
|
||||
testserver: {}
|
||||
testserver: {
|
||||
options: {
|
||||
// We use end2end task (which does not start the webserver)
|
||||
// and start the webserver as a separate process (in travis_build.sh)
|
||||
// to avoid https://github.com/joyent/libuv/issues/826
|
||||
port: 8000,
|
||||
hostname: '0.0.0.0',
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
function(req, resp, next) {
|
||||
// cache get requests to speed up tests on travis
|
||||
if (req.method === 'GET') {
|
||||
resp.setHeader('Cache-control', 'public, max-age=3600');
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -137,6 +176,23 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
|
||||
"ddescribe-iit": {
|
||||
files: [
|
||||
'test/**/*.js',
|
||||
'!test/ngScenario/DescribeSpec.js'
|
||||
]
|
||||
},
|
||||
|
||||
"merge-conflict": {
|
||||
files: [
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
'docs/**/*',
|
||||
'css/**/*'
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
copy: {
|
||||
i18n: {
|
||||
files: [
|
||||
@@ -148,7 +204,7 @@ module.exports = function(grunt) {
|
||||
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip'},
|
||||
options: {archive: 'build/' + dist +'.zip', mode: 'zip'},
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
@@ -167,5 +223,6 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
The Closure Compiler project contains externs definitions for AngularJS
|
||||
JavaScript in its `contrib/externs` directory.
|
||||
|
||||
The definitions contain externs for use with the Closure compiler (aka
|
||||
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||
pass allows using type annotations for AngularJS objects. For example,
|
||||
Angular's $scope objects can be annotated as:
|
||||
```js
|
||||
/** @type {angular.Scope} */
|
||||
var scope = $scope;
|
||||
```
|
||||
|
||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||
missing methods or incorrect arguments, and also prevents renaming of property
|
||||
accesses with advanced compilation.
|
||||
|
||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||
be correct. Externs for individual modules should be added in separate files.
|
||||
|
||||
See https://developers.google.com/closure/compiler/
|
||||
@@ -16,6 +16,7 @@ it makes development fun!
|
||||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
|
||||
Vendored
+19
-53
@@ -94,7 +94,6 @@ angularFiles = {
|
||||
'test/matchers.js',
|
||||
'test/ngScenario/*.js',
|
||||
'test/ngScenario/output/*.js',
|
||||
'test/ngScenario/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/auto/*.js',
|
||||
'test/bootstrap/*.js',
|
||||
@@ -109,37 +108,30 @@ angularFiles = {
|
||||
'test/ngMock/*.js'
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'karma': [
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdExclude': [
|
||||
'karmaExclude': [
|
||||
'test/jquery_alias.js',
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdScenario': [
|
||||
'karmaScenario': [
|
||||
'build/angular-scenario.js',
|
||||
'build/jstd-scenario-adapter-config.js',
|
||||
'build/jstd-scenario-adapter.js',
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
"jstdModules": [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
"karmaModules": [
|
||||
'build/angular.js',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'src/ngCookies/cookies.js',
|
||||
@@ -156,39 +148,20 @@ angularFiles = {
|
||||
'test/ngSanitize/filter/*.js'
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'src/ngMock/angular-mocks.js',
|
||||
'perf/data/*.js',
|
||||
'perf/testUtils.js',
|
||||
'perf/*.js'
|
||||
],
|
||||
|
||||
'jstdPerfExclude': [
|
||||
'src/ng/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js'
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'karmaJquery': [
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
|
||||
'@angularTest',
|
||||
'example/personalLog/*.js',
|
||||
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
'jstdJqueryExclude': [
|
||||
'karmaJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
'test/jquery_remove.js'
|
||||
@@ -196,29 +169,22 @@ angularFiles = {
|
||||
};
|
||||
|
||||
if (exports) {
|
||||
exports.files = angularFiles
|
||||
exports.mergeFiles = function mergeFiles() {
|
||||
exports.files = angularFiles;
|
||||
exports.mergeFilesFor = function() {
|
||||
var files = [];
|
||||
|
||||
[].splice.call(arguments, 0).forEach(function(file) {
|
||||
if (file.match(/karma/)) {
|
||||
files.push(file);
|
||||
} else {
|
||||
angularFiles[file].forEach(function(f) {
|
||||
// replace @ref
|
||||
var match = f.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
var deps = angularFiles[match[1]];
|
||||
files = files.concat(deps);
|
||||
} else {
|
||||
if (!/jstd|jasmine/.test(f)) { //TODO(i): remove once we don't have jstd/jasmine in repo
|
||||
files.push(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
|
||||
angularFiles[filegroup].forEach(function(file) {
|
||||
// replace @ref
|
||||
var match = file.match(/^\@(.*)/);
|
||||
if (match) {
|
||||
files = files.concat(angularFiles[match[1]]);
|
||||
} else {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -192,7 +192,7 @@ var getPreviousTag = function() {
|
||||
var generate = function(version, file) {
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
|
||||
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
|
||||
console.log('Parsed', commits.length, 'commits');
|
||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ng\:form {
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="HelloCntl">
|
||||
Your name: <input type="text" ng-model="name" value="World"/>
|
||||
Your name: <input type="text" ng-model="name"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
Hello {{name || "World"}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
This page explains the Angular initialization process and how you can manually initialize Angular
|
||||
if necessary.
|
||||
|
||||
|
||||
# Angular `<script>` Tag
|
||||
## Angular `<script>` Tag
|
||||
|
||||
This example shows the recommended path for integrating Angular with what we call automatic
|
||||
initialization.
|
||||
@@ -50,7 +49,7 @@ initialization.
|
||||
|
||||
|
||||
|
||||
# Automatic Initialization
|
||||
## Automatic Initialization
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event, at which point Angular looks for
|
||||
the {@link api/ng.directive:ngApp `ng-app`} directive which
|
||||
@@ -77,16 +76,14 @@ will:
|
||||
|
||||
|
||||
|
||||
# Manual Initialization
|
||||
## 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.
|
||||
|
||||
|
||||
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
|
||||
api/ng.directive:ngApp ng-app} directive.
|
||||
Here is an example of manually initializing Angular:
|
||||
|
||||
<pre>
|
||||
<!doctype html>
|
||||
@@ -96,17 +93,22 @@ api/ng.directive:ngApp ng-app} directive.
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.bootstrap(document);
|
||||
angular.module('myApp', []);
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
Note that we have provided the name of our application module to be loaded into the injector as the second
|
||||
parameter of the {@link api/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.
|
||||
|
||||
This is the sequence that your code should follow:
|
||||
|
||||
1. After the page and all of the code is loaded, find the root of the HTML template, which is
|
||||
typically the root of the document.
|
||||
1. After the page and all of the code is loaded, find the root element of your AngularJS
|
||||
application, which is typically the root of the document.
|
||||
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
|
||||
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
|
||||
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
|
||||
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
|
||||
extensions {@link api/ng.$compileProvider#directive directives}.
|
||||
|
||||
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
|
||||
For example if something needs to be centered, there is no need to provide instructions to the
|
||||
browser how the window size needs to be divided in half so that center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
|
||||
browser how the window size needs to be divided in half so that the center is found, and that this
|
||||
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.
|
||||
|
||||
But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
|
||||
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
|
||||
|
||||
Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
expect that you will create directives that are specific to your app. These extension become a
|
||||
expect that you will create directives that are specific to your app. These extensions become a
|
||||
Domain Specific Language for building your application.
|
||||
|
||||
All of this compilation takes place in the web browser; no server side or pre-compilation step is
|
||||
@@ -39,17 +39,16 @@ process happens in two phases.
|
||||
scope model are reflected in the view, and any user interactions with the view are reflected
|
||||
in the scope model. This makes the scope model the single source of truth.
|
||||
|
||||
Some directives such {@link api/ng.directive:ngRepeat
|
||||
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
|
||||
improves performance since the cloned template only needs to be compiled once, and then linked
|
||||
once for each clone instance.
|
||||
Some directives such as {@link api/ng.directive:ngRepeat `ng-repeat`} clone DOM elements once
|
||||
for each item in a collection. Having a compile and link phase improves performance since the
|
||||
cloned template only needs to be compiled once, and then linked once for each clone instance.
|
||||
|
||||
|
||||
# Directive
|
||||
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered in
|
||||
the compilation process. The directives can be placed in element names, attributes, class names, as
|
||||
well as comments. Here are some equivalent examples of invoking the {@link
|
||||
A directive is a behavior which should be triggered when specific HTML constructs are encountered
|
||||
during the compilation process. The directives can be placed in element names, attributes, class
|
||||
names, as well as comments. Here are some equivalent examples of invoking the {@link
|
||||
api/ng.directive:ngBind `ng-bind`} directive.
|
||||
|
||||
<pre>
|
||||
|
||||
@@ -45,8 +45,6 @@ This is how we get the ball rolling (refer to the diagram and example below):
|
||||
9. The `{{name}}` {@link api/ng.$interpolate interpolates} the expression to
|
||||
`Hello World!`
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<p ng-init=" name='World' ">Hello {{name}}!</p>
|
||||
@@ -126,8 +124,6 @@ user enters text into the text field.
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<input ng-model="name">
|
||||
@@ -147,8 +143,6 @@ The following example demonstrates how the `name` {@link guide/expression expres
|
||||
into a different value depending on which scope it is evaluated in. The example is followed by
|
||||
a diagram depicting the scope boundaries.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<div class="show-scope">
|
||||
<example>
|
||||
<file name="index.html">
|
||||
@@ -204,8 +198,6 @@ The separation of the controller and the view is important because:
|
||||
controller. This is important for re-skinning, device specific views (i.e. mobile vs desktop),
|
||||
and testability.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MyCtrl">
|
||||
@@ -239,10 +231,6 @@ to inherit from or special accessor methods for accessing or changing the model.
|
||||
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
|
||||
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
|
||||
<a name="view"></a>
|
||||
# View
|
||||
|
||||
@@ -269,9 +257,6 @@ rendering the view compared to most other templating systems.
|
||||
continuously updating view which does not need template model re-merging. Your model becomes
|
||||
the single source-of-truth for your view.
|
||||
|
||||
<div class="clear">
|
||||
</div>
|
||||
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-init="list = ['Chrome', 'Safari', 'Firefox', 'IE'] ">
|
||||
@@ -292,7 +277,7 @@ rendering the view compared to most other templating systems.
|
||||
# Directives
|
||||
|
||||
A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute,
|
||||
element name, or a class name. A directive allows you to extend the HTML vocabulary in a
|
||||
element name, class name or comment. A directive allows you to extend the HTML vocabulary in a
|
||||
declarative fashion. Following is an example which enables data-binding for the `contenteditable`
|
||||
in HTML.
|
||||
|
||||
@@ -369,7 +354,6 @@ api/AUTO.$injector injector} asks the instance factory to create a new instance.
|
||||
A {@link api/angular.Module module} is a way to configure the injector's instance factory, known
|
||||
as a {@link api/AUTO.$provide provider}.
|
||||
|
||||
<div class='clear'></div>
|
||||
<pre>
|
||||
// Create a module
|
||||
var myModule = angular.module('myModule', [])
|
||||
|
||||
@@ -38,9 +38,9 @@ it('should filter results', function() {
|
||||
});
|
||||
</pre>
|
||||
This scenario describes the requirements of a Buzz Client, specifically, that it should be able to
|
||||
filter the stream of the user. It starts by entering a value in the 'user' input field, clicking
|
||||
filter the stream of the user. It starts by entering a value in the input field with ng-model="user", clicking
|
||||
the only button on the page, and then it verifies that there are 10 items listed. It then enters
|
||||
'Bees' in the 'filterText' input field and verifies that the list is reduced to a single item.
|
||||
'Bees' in the input field with ng-model='filterText' and verifies that the list is reduced to a single item.
|
||||
|
||||
The API section below lists the available commands and expectations for the Runner.
|
||||
|
||||
@@ -110,16 +110,16 @@ Scopes the next DSL element selection.
|
||||
Returns the value of the first binding matching the given `name`.
|
||||
|
||||
## input(name).enter(value)
|
||||
Enters the given `value` in the text field with the given `name`.
|
||||
Enters the given `value` in the text field with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).check()
|
||||
Checks/unchecks the checkbox with the given `name`.
|
||||
Checks/unchecks the checkbox with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).select(value)
|
||||
Selects the given `value` in the radio button with the given `name`.
|
||||
Selects the given `value` in the radio button with the corresponding ng-model `name`.
|
||||
|
||||
## input(name).val()
|
||||
Returns the current value of an input field with the given `name`.
|
||||
Returns the current value of an input field with the corresponding ng-model `name`.
|
||||
|
||||
## repeater(selector, label).count()
|
||||
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
|
||||
@@ -134,10 +134,10 @@ Returns an array with the values in the column with the given `binding` in the r
|
||||
the given jQuery `selector`. The `label` is used for test output.
|
||||
|
||||
## select(name).option(value)
|
||||
Picks the option with the given `value` on the select with the given `name`.
|
||||
Picks the option with the given `value` on the select with the given ng-model `name`.
|
||||
|
||||
## select(name).option(value1, value2...)
|
||||
Picks the options with the given `values` on the multi select with the given `name`.
|
||||
## 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()
|
||||
Returns the number of elements that match the given jQuery `selector`. The `label` is used for test
|
||||
@@ -228,7 +228,7 @@ conditional assertions or element selection. Even though you should generally tr
|
||||
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
|
||||
added entries (where an entry is some domain object) using the application's web interface.
|
||||
|
||||
Imagine the application to be structure into two views:
|
||||
Imagine the application to be structured into two views:
|
||||
|
||||
1. *Overview view* which lists all the added entries in a table and
|
||||
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
|
||||
@@ -275,7 +275,7 @@ beforeEach(function () {
|
||||
|
||||
In order to understand what is happening, we should emphasize that ngScenario calls are not
|
||||
immediately executed, but queued (in ngScenario terms, we would be talking about adding
|
||||
future actions). If we had only one entry in our table, than the following future actions
|
||||
future actions). If we had only one entry in our table, then the following future actions
|
||||
would be queued:
|
||||
|
||||
<pre>
|
||||
@@ -301,4 +301,8 @@ element('.btn-danger').click();
|
||||
element('table tbody').query(function (tbody, done) { ... });
|
||||
element('table tbody a');
|
||||
element('.btn-danger').click();
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
# Caveats
|
||||
|
||||
ngScenario does not work with apps that manually bootstrap using angular.bootstrap. You must use the ng-app directive.
|
||||
|
||||
@@ -2,79 +2,117 @@
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Controller Component
|
||||
@description
|
||||
|
||||
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link scope Scope}, excluding the root scope.
|
||||
# Understanding Controllers
|
||||
|
||||
Use controllers to:
|
||||
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
|
||||
{@link scope Angular Scope}.
|
||||
|
||||
- Set up the initial state of a scope object.
|
||||
- Add behavior to the scope object.
|
||||
When a Controller is attached to the DOM via the {@link api/ng.directive:ngController ng-controller}
|
||||
directive, Angular will instantiate a new Controller object, using the specified Controller's
|
||||
**constructor function**. A new **child scope** will be available as an injectable parameter to the
|
||||
Controller's constructor function as `$scope`.
|
||||
|
||||
# Setting up the initial state of a scope object
|
||||
Use Controllers to:
|
||||
|
||||
Typically, when you create an application you need to set up an initial state for an Angular scope.
|
||||
- Set up the initial state of the `$scope` object.
|
||||
- Add behavior to the `$scope` object.
|
||||
|
||||
Angular applies (in the sense of JavaScript's `Function#apply`) the controller constructor function
|
||||
to a new Angular scope object, which sets up an initial scope state. This means that Angular never
|
||||
creates instances of the controller type (by invoking the `new` operator on the controller
|
||||
constructor). Constructors are always applied to an existing scope object.
|
||||
# Setting up the initial state of a `$scope` object
|
||||
|
||||
You set up the initial state of a scope by creating model properties. For example:
|
||||
Typically, when you create an application you need to set up the initial state for the Angular
|
||||
`$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object.
|
||||
The properties contain the **view model** (the model that will be presented by the view). All the
|
||||
`$scope` properties will be available to the template at the point in the DOM where the Controller
|
||||
is registered.
|
||||
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
The following example shows a very simple constructor function for a Controller, `GreetingCtrl`,
|
||||
which attaches a `greeting` property containing the string `'Hola!'` to the `$scope`:
|
||||
|
||||
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
|
||||
<pre>
|
||||
function GreetingCtrl($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}
|
||||
</pre>
|
||||
|
||||
**NOTE**: Many of the examples in the documentation show the creation of functions
|
||||
in the global scope. This is only for demonstration purposes - in a real
|
||||
application you should use the `.controller` method of your Angular module for
|
||||
your application as follows:
|
||||
Once the Controller has been attached to the DOM, the `greeting` property can be data-bound to the
|
||||
template:
|
||||
|
||||
var myApp = angular.module('myApp',[]);
|
||||
<pre>
|
||||
<div ng-controller="GreetingCtrl">
|
||||
{{ greeting }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
**NOTE**: Although Angular allows you to create Controller functions in the global scope, this is
|
||||
not recommended. In a real application you should use the `.controller` method of your
|
||||
{@link module Angular Module} for your application as follows:
|
||||
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
|
||||
$scope.greeting = 'Hola!';
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
We have used an **inline injection annotation** to explicitly specify the dependency
|
||||
of the Controller on the `$scope` service provided by Angular. See the guide on
|
||||
{@link http://docs.angularjs.org/guide/di Dependency Injection} for more information.
|
||||
|
||||
Note also that we use the array notation to explicitly specify the dependency
|
||||
of the controller on the `$scope` service provided by Angular.
|
||||
|
||||
# Adding Behavior to a Scope Object
|
||||
|
||||
Behavior on an Angular scope object is in the form of scope method properties available to the
|
||||
template/view. This behavior interacts with and modifies the application model.
|
||||
In order to react to events or execute computation in the view we must provide behavior to the
|
||||
scope. We add behavior the scope by attaching methods to the `$scope` object. These methods are
|
||||
then available to be called from the template/view.
|
||||
|
||||
The following example uses a Controller to add a method to the scope, which doubles a number:
|
||||
|
||||
<pre>
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
myApp.controller('DoubleCtrl', ['$scope', function($scope) {
|
||||
$scope.double = function(value) { return value * 2; };
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
Once the Controller has been attached to the DOM, the `double` method can be invoked in an Angular
|
||||
expression in the template:
|
||||
|
||||
<pre>
|
||||
<div ng-controller="DoubleCtrl">
|
||||
Two times <input ng-model="num"> equals {{ double(num) }}
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any
|
||||
objects (or primitives) assigned to the scope become model properties. Any functions assigned to
|
||||
objects (or primitives) assigned to the scope become model properties. Any methods assigned to
|
||||
the scope are available in the template/view, and can be invoked via angular expressions
|
||||
and `ng` event handler directives (e.g. {@link api/ng.directive:ngClick ngClick}).
|
||||
|
||||
# Using Controllers Correctly
|
||||
|
||||
In general, a controller shouldn't try to do too much. It should contain only the business logic
|
||||
In general, a Controller shouldn't try to do too much. It should contain only the business logic
|
||||
needed for a single view.
|
||||
|
||||
The most common way to keep controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in controllers via dependency injection.
|
||||
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to
|
||||
controllers into services and then using these services in Controllers via dependency injection.
|
||||
This is discussed in the {@link di Dependency Injection} {@link dev_guide.services
|
||||
Services} sections of this guide.
|
||||
|
||||
Do not use controllers for:
|
||||
Do not use Controllers for:
|
||||
|
||||
- Any kind of DOM manipulation — Controllers should contain only business logic. DOM
|
||||
manipulation—the presentation logic of an application—is well known for being hard to test.
|
||||
Putting any presentation logic into controllers significantly affects testability of the business
|
||||
manipulation (the presentation logic of an application) is well known for being hard to test.
|
||||
Putting any presentation logic into Controllers significantly affects testability of the business
|
||||
logic. Angular offers {@link dev_guide.templates.databinding databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in
|
||||
{@link guide/directive directives}.
|
||||
- Input formatting — Use {@link forms angular form controls} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- To run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
- Sharing stateless or stateful code across Controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
- To instantiate or manage the life-cycle of other components (for example, to create service
|
||||
instances).
|
||||
- Managing the life-cycle of other components (for example, to create service instances).
|
||||
|
||||
|
||||
# Associating Controllers with Angular Scope Objects
|
||||
@@ -83,189 +121,208 @@ You can associate controllers with scope objects implicitly via the {@link api/n
|
||||
directive} or {@link api/ng.$route $route service}.
|
||||
|
||||
|
||||
## Controller Constructor and Methods Example
|
||||
## Simple Spicy Controller Example
|
||||
|
||||
To illustrate how the controller component works in angular, let's create a little app with the
|
||||
To illustrate further how Controller components work in Angular, let's create a little app with the
|
||||
following components:
|
||||
|
||||
- A {@link dev_guide.templates template} with two buttons and a simple message
|
||||
- A model consisting of a string named `spice`
|
||||
- A controller with two functions that set the value of `spice`
|
||||
- A Controller with two functions that set the value of `spice`
|
||||
|
||||
The message in our template contains a binding to the `spice` model, which by default is set to the
|
||||
string "very". Depending on which button is clicked, the `spice` model is set to `chili` or
|
||||
`jalapeño`, and the message is automatically updated by data-binding.
|
||||
|
||||
<doc:example module="spicyApp1">
|
||||
<doc:source>
|
||||
<div ng-app="spicyApp1" ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</div>
|
||||
<script>
|
||||
var myApp = angular.module('spicyApp1', []);
|
||||
|
||||
## A Spicy Controller Example
|
||||
|
||||
<pre>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<button ng-click="chiliSpicy()">Chili</button>
|
||||
<button ng-click="jalapenoSpicy()">Jalapeño</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
}
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</pre>
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
$scope.spicy = 'very';
|
||||
|
||||
$scope.chiliSpicy = function() {
|
||||
$scope.spice = 'chili';
|
||||
};
|
||||
|
||||
$scope.jalapenoSpicy = function() {
|
||||
$scope.spice = 'jalapeño';
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
Things to notice in the example above:
|
||||
|
||||
- The `ngController` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyCtrl` controller.
|
||||
- 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".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method)
|
||||
- Both controller methods are available in the template (for the `body` element and and its
|
||||
children).
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) allowed you to use `this` interchangeably with
|
||||
the $scope method, but this is no longer the case. Inside of methods defined on the scope
|
||||
`this` and $scope are interchangeable (angular sets `this` to $scope), but not otherwise
|
||||
inside your controller constructor.
|
||||
- NB: Previous versions of Angular (pre 1.0 RC) added prototype methods into the scope
|
||||
automatically, but this is no longer the case; all methods need to be added manually to
|
||||
the scope.
|
||||
- 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
|
||||
and its children).
|
||||
|
||||
## Spicy Arguments Example
|
||||
|
||||
Controller methods can also take arguments, as demonstrated in the following variation of the
|
||||
previous example.
|
||||
|
||||
## Controller Method Arguments Example
|
||||
<doc:example module="spicyApp2">
|
||||
<doc:source>
|
||||
<div ng-app="spicyApp2" ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</div>
|
||||
<script>
|
||||
var myApp = angular.module('spicyApp2', []);
|
||||
|
||||
<pre>
|
||||
<body ng-controller="SpicyCtrl">
|
||||
<input ng-model="customSpice" value="wasabi">
|
||||
<button ng-click="spicy('chili')">Chili</button>
|
||||
<button ng-click="spicy(customSpice)">Custom spice</button>
|
||||
<p>The food is {{spice}} spicy!</p>
|
||||
</body>
|
||||
myApp.controller('SpicyCtrl', ['$scope', function($scope){
|
||||
$scope.customSpice = "wasabi";
|
||||
$scope.spice = 'very';
|
||||
|
||||
$scope.spicy = function(spice){
|
||||
$scope.spice = spice;
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
function SpicyCtrl($scope) {
|
||||
$scope.spice = 'very';
|
||||
$scope.spicy = function(spice) {
|
||||
$scope.spice = spice;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice that the `SpicyCtrl` 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
|
||||
Notice that the `SpicyCtrl` 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 `spice` (bound to an
|
||||
input box) in the second button.
|
||||
|
||||
## Scope Inheritance Example
|
||||
|
||||
## Controller Inheritance Example
|
||||
It is common to attach Controllers at different levels of the DOM hierarchy. Since the
|
||||
{@link api/ng.directive:ngController ng-controller} directive creates a new child scope, we get a
|
||||
hierarchy of scopes that inherit from each other. The `$scope` that each Controller receives will
|
||||
have access to properties and methods defined by Controllers higher up the hierarchy.
|
||||
See {@link https://github.com/angular/angular.js/wiki/Understanding-Scopes Understanding Scopes} for
|
||||
more information about scope inheritance.
|
||||
|
||||
Controller inheritance in Angular is based on {@link api/ng.$rootScope.Scope Scope} inheritance. Let's
|
||||
have a look at an example:
|
||||
<doc:example module="scopeInheritance">
|
||||
<doc:source>
|
||||
<div ng-app="scopeInheritance" class="spicy">
|
||||
<div ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
<pre>
|
||||
<body ng-controller="MainCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</body>
|
||||
<div ng-controller="ChildCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
|
||||
function MainCtrl($scope) {
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}
|
||||
<div ng-controller="BabyCtrl">
|
||||
<p>Good {{timeOfDay}}, {{name}}!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
div.spicy div {
|
||||
padding: 10px;
|
||||
border: solid 2px blue;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var myApp = angular.module('scopeInheritance', []);
|
||||
myApp.controller('MainCtrl', ['$scope', function($scope){
|
||||
$scope.timeOfDay = 'morning';
|
||||
$scope.name = 'Nikki';
|
||||
}]);
|
||||
myApp.controller('ChildCtrl', ['$scope', function($scope){
|
||||
$scope.name = 'Mattie';
|
||||
}]);
|
||||
myApp.controller('BabyCtrl', ['$scope', function($scope){
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}]);
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
function ChildCtrl($scope) {
|
||||
$scope.name = 'Mattie';
|
||||
}
|
||||
|
||||
function BabyCtrl($scope) {
|
||||
$scope.timeOfDay = 'evening';
|
||||
$scope.name = 'Gingerbreak Baby';
|
||||
}
|
||||
</pre>
|
||||
|
||||
Notice how we nested three `ngController` directives in our template. This template construct will
|
||||
result in 4 scopes being created for our view:
|
||||
Notice how we nested three `ng-controller` directives in our template. This will result in four
|
||||
scopes being created for our view:
|
||||
|
||||
- The root scope
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` models
|
||||
- The `ChildCtrl` scope, which shadows the `name` model from the previous scope and inherits the
|
||||
`timeOfDay` model
|
||||
- The `BabyCtrl` scope, which shadows both the `timeOfDay` model defined in `MainCtrl` and `name`
|
||||
model defined in the ChildCtrl
|
||||
- The `MainCtrl` scope, which contains `timeOfDay` and `name` properties
|
||||
- The `ChildCtrl` scope, which inherits the `timeOfDay` property but overrides (hides) the `name`
|
||||
property from the previous
|
||||
- The `BabyCtrl` scope, which overrides (hides) both the `timeOfDay` property defined in `MainCtrl`
|
||||
and the `name` property defined in `ChildCtrl`
|
||||
|
||||
Inheritance works between controllers in the same way as it does with models. So in our previous
|
||||
examples, all of the models could be replaced with controller methods that return string values.
|
||||
|
||||
Note: Standard prototypical inheritance between two controllers doesn't work as one might expect,
|
||||
because as we mentioned earlier, controllers are not instantiated directly by Angular, but rather
|
||||
are applied to the scope object.
|
||||
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.
|
||||
|
||||
|
||||
## Testing Controllers
|
||||
|
||||
Although there are many ways to test a controller, one of the best conventions, shown below,
|
||||
involves injecting the `$rootScope` and `$controller`
|
||||
Although there are many ways to test a Controller, one of the best conventions, shown below,
|
||||
involves injecting the {@link api/ng.$rootScope $rootScope} and {@link api/ng.$controller $controller}:
|
||||
|
||||
Controller Function:
|
||||
**Controller Definition:**
|
||||
<pre>
|
||||
function myController($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
var myApp = angular.module('myApp',[]);
|
||||
|
||||
$scope.spice = "habanero";
|
||||
}
|
||||
myApp.controller('MyController', function($scope) {
|
||||
$scope.spices = [{"name":"pasilla", "spiciness":"mild"},
|
||||
{"name":"jalapeno", "spiceiness":"hot hot hot!"},
|
||||
{"name":"habanero", "spiceness":"LAVA HOT!!"}];
|
||||
$scope.spice = "habanero";
|
||||
});
|
||||
</pre>
|
||||
|
||||
Controller Test:
|
||||
**Controller Test:**
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function() {
|
||||
var scope;
|
||||
var $scope;
|
||||
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
scope = $rootScope.$new();
|
||||
var ctrl = $controller(myController, {$scope: scope});
|
||||
$scope = $rootScope.$new();
|
||||
$controller('MyController', {$scope: $scope});
|
||||
}));
|
||||
|
||||
it('should create "spices" model with 3 spices', function() {
|
||||
expect(scope.spices.length).toBe(3);
|
||||
expect($scope.spices.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should set the default value of spice', function() {
|
||||
expect(scope.spice).toBe('habanero');
|
||||
expect($scope.spice).toBe('habanero');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
If you need to test a nested controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM.
|
||||
If you need to test a nested Controller you need to create the same scope hierarchy
|
||||
in your test that exists in the DOM:
|
||||
|
||||
<pre>
|
||||
describe('state', function() {
|
||||
var mainScope, childScope, babyScope;
|
||||
|
||||
beforeEach(module('myApp'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
mainScope = $rootScope.$new();
|
||||
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
|
||||
$controller('MainCtrl', {$scope: mainScope});
|
||||
childScope = mainScope.$new();
|
||||
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
|
||||
babyScope = childCtrl.$new();
|
||||
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
|
||||
$controller('ChildCtrl', {$scope: childScope});
|
||||
babyScope = childScope.$new();
|
||||
$controller('BabyCtrl', {$scope: babyScope});
|
||||
}));
|
||||
|
||||
it('should have over and selected', function() {
|
||||
|
||||
@@ -28,7 +28,7 @@ occurs in controllers:
|
||||
|
||||
* Use an {@link expression angular expression} with an assignment operator in templates:
|
||||
|
||||
<button ng-click="{{foo='ball'}}">Click me</button>
|
||||
<button ng-click="{{foo='bar'}}">Click me</button>
|
||||
|
||||
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
|
||||
only, not recommended for real applications):
|
||||
|
||||
@@ -14,10 +14,10 @@ changes to $location are reflected into the browser address bar.
|
||||
- Exposes the current URL in the browser address bar, so you can
|
||||
- Watch and observe the URL.
|
||||
- Change the URL.
|
||||
- Synchronizes the URL with the browser when the user
|
||||
- Changes the address bar.
|
||||
- Clicks the back or forward button (or clicks a History link).
|
||||
- Clicks on a link.
|
||||
- Maintains synchronization between itself and the browser's URL when the user
|
||||
- Changes the address in the browser's address bar.
|
||||
- Clicks the back or forward button in the browser (or clicks a History link).
|
||||
- Clicks on a link in the page.
|
||||
- Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
|
||||
|
||||
|
||||
@@ -121,6 +121,8 @@ All of the setter methods return the same `$location` object to allow chaining.
|
||||
change multiple segments in one go, chain setters like this:
|
||||
<pre>$location.path('/newValue').search({key: value});</pre>
|
||||
|
||||
## Replace method
|
||||
|
||||
There is a special `replace` method which can be used to tell the $location service that the next
|
||||
time the $location service is synced with the browser, the last history record should be replaced
|
||||
instead of creating a new one. This is useful when you want to implement redirection, which would
|
||||
@@ -212,7 +214,7 @@ In this mode, `$location` uses Hashbang URLs in all browsers.
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(false);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
// open http://host.com/base/index.html#!/a
|
||||
@@ -261,7 +263,7 @@ having to worry about whether the browser displaying your app supports the histo
|
||||
it('should show example', inject(
|
||||
function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix = '!';
|
||||
$locationProvider.hashPrefix('!');
|
||||
},
|
||||
function($location) {
|
||||
// in browser with HTML5 history support:
|
||||
@@ -321,6 +323,14 @@ reload to the original link.
|
||||
Example: `<a href="http://angularjs.org/">link</a>`
|
||||
- Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: `<a href="/not-my-base/link">link</a>`
|
||||
|
||||
When running Angular in the root of a domain, along side perhaps a normal application in the same
|
||||
directory, the "otherwise" route handler will try to handle all the URLs, including ones that map
|
||||
to static files.
|
||||
|
||||
To prevent this, you can set your base href for the app to `<base href=".">` and then prefix links
|
||||
to URLs that should be handled with `.`. Now, links to locations, which are not to be routed by Angular,
|
||||
are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`.
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
@@ -57,6 +57,7 @@ myController.$inject = ['$scope','notify'];
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
@@ -98,6 +99,7 @@ function myController($scope, notify) {
|
||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
@@ -7,9 +7,17 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
||||
|
||||
# CSS classes used by angular
|
||||
|
||||
* `ng-scope`
|
||||
- **Usage:** angular applies this class to any element that where a new {@link api/ng.$rootScope.Scope scope}
|
||||
is defined. (see {@link guide/scope scope} guide for more information about scopes)
|
||||
|
||||
* `ng-binding`
|
||||
- **Usage:** angular applies this class to any element that is attached to a data binding, via `ng-bind` or
|
||||
{{}} curly braces, for example. (see {@link guide/dev_guide.templates.databinding databinding} guide)
|
||||
|
||||
* `ng-invalid`, `ng-valid`
|
||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||
not pass validation. (see {@link api/ng.directive:input input} directive).
|
||||
not pass validation. (see {@link api/ng.directive:input input} directive)
|
||||
|
||||
* `ng-pristine`, `ng-dirty`
|
||||
- **Usage:** angular {@link api/ng.directive:input input} directive applies `ng-pristine` class
|
||||
|
||||
@@ -8,7 +8,7 @@ as the first argument. Any filter arguments are passed in as additional argument
|
||||
function.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case and assigns color.
|
||||
text upper-case.
|
||||
|
||||
<doc:example module="MyReverseModule">
|
||||
<doc:source>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@description
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
comes with almost no-help from the compiler. For this reason we feel very strongly that any code
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
@@ -248,10 +248,11 @@ function PasswordCtrl($scope) {
|
||||
and the test is straight forward
|
||||
|
||||
<pre>
|
||||
var pc = new PasswordCtrl();
|
||||
pc.password('abc');
|
||||
pc.grade();
|
||||
expect(pc.strength).toEqual('weak');
|
||||
var $scope = {};
|
||||
var pc = $controller('PasswordCtrl', { $scope: $scope });
|
||||
$scope.password = 'abc';
|
||||
$scope.grade();
|
||||
expect($scope.strength).toEqual('weak');
|
||||
</pre>
|
||||
|
||||
Notice that the test is not only much shorter but it is easier to follow what is going on. We say
|
||||
@@ -294,14 +295,14 @@ app.directive('aGreatEye', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template: '<h1>lidless, wreathed in flame</h1>'
|
||||
template: '<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>'
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
This directive is used as a tag `<a-great-eye></a-great-eye>`. It replaces the entire tag with the
|
||||
template `<h1>lidless, wreathed in flame</h1>`. Now we are going to write a jasmine unit test to
|
||||
verify this functionality.
|
||||
template `<h1>lidless, wreathed in flame, {{1 + 1}} times</h1>`. Now we are going to write a jasmine unit test to
|
||||
verify this functionality. Note that the expression `{{1 + 1}}` times will also be evaluated in the rendered content.
|
||||
|
||||
<pre>
|
||||
describe('Unit testing great quotes', function() {
|
||||
@@ -322,30 +323,18 @@ describe('Unit testing great quotes', function() {
|
||||
it('Replaces the element with the appropriate content', function() {
|
||||
// Compile a piece of HTML containing the directive
|
||||
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
|
||||
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
|
||||
$rootScope.$digest();
|
||||
// Check that the compiled element contains the templated content
|
||||
expect(element.html()).toContain("lidless, wreathed in flame");
|
||||
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
We inject the $compile service and $rootScope before each jasmine test. The $compile service is used
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame" is present.
|
||||
replaced the content and "lidless, wreathed in flame, 2 times" is present.
|
||||
|
||||
## Mocks
|
||||
oue
|
||||
|
||||
## Global State Isolation
|
||||
oue
|
||||
|
||||
# Preferred way of Testing
|
||||
uo
|
||||
|
||||
## JavaScriptTestDriver
|
||||
ou
|
||||
|
||||
## Jasmine
|
||||
ou
|
||||
|
||||
## Sample project
|
||||
See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
|
||||
|
||||
+20
-16
@@ -24,7 +24,7 @@ There are only three ways an object or a function can get a hold of its dependen
|
||||
|
||||
|
||||
The first two options of creating or looking up dependencies are not optimal because they hard
|
||||
code the dependency. This make it difficult, if not impossible, to modify the dependencies.
|
||||
code the dependency. This makes it difficult, if not impossible, to modify the dependencies.
|
||||
This is especially problematic in tests, where it is often desirable to provide mock dependencies
|
||||
for test isolation.
|
||||
|
||||
@@ -102,20 +102,20 @@ dependency lookup responsibility to the injector by declaring the dependencies a
|
||||
|
||||
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
|
||||
dependencies of `MyController` without the controller ever knowing about the injector. This is
|
||||
the best outcome. The application code simply ask for the dependencies it needs, without having to
|
||||
the best outcome. The application code simply asks for the dependencies it needs, without having to
|
||||
deal with the injector. This setup does not break the Law of Demeter.
|
||||
|
||||
# Dependency Annotation
|
||||
## Dependency Annotation
|
||||
|
||||
How does the injector know what service needs to be injected?
|
||||
How does the injector know what service needs to be injected?
|
||||
|
||||
The application developer needs to provide annotation information that the injector uses in order
|
||||
to resolve the dependencies. Throughout Angular certain API functions are invoked using the
|
||||
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
|
||||
injector, as per the API documentation. The injector needs to know what services to inject into
|
||||
the function. Below are three equivalent ways of annotating your code with service name
|
||||
information. These can be used interchangeably as you see fit and are equivalent.
|
||||
|
||||
# Inferring Dependencies
|
||||
### Inferring Dependencies
|
||||
|
||||
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
are the names of the dependencies.
|
||||
@@ -134,10 +134,10 @@ While straightforward, this method will not work with JavaScript minifiers/obfus
|
||||
rename the method parameter names. This makes this way of annotating only useful for {@link
|
||||
http://www.pretotyping.org/ pretotyping}, and demo applications.
|
||||
|
||||
# `$inject` Annotation
|
||||
### `$inject` Annotation
|
||||
|
||||
To allow the minifers to rename the function parameters and still be able to inject right services
|
||||
the function needs to be annotate with the `$inject` property. The `$inject` property is an array
|
||||
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
|
||||
of service names to inject.
|
||||
|
||||
<pre>
|
||||
@@ -147,13 +147,15 @@ of service names to inject.
|
||||
MyController.$inject = ['$scope', 'greeter'];
|
||||
</pre>
|
||||
|
||||
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
|
||||
Using above code snippet as an example, '$scope' will be injected into 'renamed$scope' and 'greeter' into 'renamedGreeter'.
|
||||
Care must be taken that the `$inject` annotation is kept in sync with the actual arguments in the
|
||||
function declaration.
|
||||
|
||||
This method of annotation is useful for controller declarations since it assigns the annotation
|
||||
information with the function.
|
||||
|
||||
# Inline Annotation
|
||||
### Inline Annotation
|
||||
|
||||
Sometimes using the `$inject` annotation style is not convenient such as when annotating
|
||||
directives.
|
||||
@@ -189,27 +191,29 @@ For this reason the third annotation style is provided as well.
|
||||
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
where injection is supported.
|
||||
|
||||
# Where can I use DI?
|
||||
## Where can I use DI?
|
||||
|
||||
DI is pervasive throughout Angular. It is typically used in controllers and factory methods.
|
||||
|
||||
## DI in controllers
|
||||
### DI in controllers
|
||||
|
||||
Controllers are classes which are responsible for application behavior. The recommended way of
|
||||
declaring controllers is:
|
||||
declaring controllers is using the array notation:
|
||||
|
||||
<pre>
|
||||
var MyController = function($scope, dep1, dep2) {
|
||||
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
|
||||
...
|
||||
$scope.aMethod = function() {
|
||||
...
|
||||
}
|
||||
}
|
||||
MyController.$inject = ['$scope', 'dep1', 'dep2'];
|
||||
...
|
||||
}]);
|
||||
</pre>
|
||||
|
||||
This avoids the creation of global functions for controllers and also protects against minification.
|
||||
|
||||
## Factory methods
|
||||
|
||||
### Factory methods
|
||||
|
||||
Factory methods are responsible for creating most objects in Angular. Examples are directives,
|
||||
services, and filters. The factory methods are registered with the module, and the recommended way
|
||||
|
||||
@@ -27,28 +27,28 @@ attribute only.)
|
||||
<!-- directive: my-dir exp -->
|
||||
</pre>
|
||||
|
||||
Directives can be invoked in many different ways, but are equivalent in the end result as shown in
|
||||
the following example.
|
||||
The following demonstrates the various ways a Directive (ngBind in this case) can be referenced from within a template.
|
||||
|
||||
<doc:example>
|
||||
<doc:source >
|
||||
<script>
|
||||
function Ctrl1($scope) {
|
||||
$scope.name = 'angular';
|
||||
$scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl1">
|
||||
Hello <input ng-model='name'> <hr/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span ng:bind="name"> <span ng:bind="name"></span> <br/>
|
||||
<span ng_bind="name"> <span ng_bind="name"></span> <br/>
|
||||
<span ng-bind="name"> <span ng-bind="name"></span> <br/>
|
||||
<span data-ng-bind="name"> <span data-ng-bind="name"></span> <br/>
|
||||
<span x-ng-bind="name"> <span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show off bindings', function() {
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text()).toBe('angular');
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -244,7 +244,8 @@ Here's an example directive declared with a Directive Definition Object:
|
||||
transclude: false,
|
||||
restrict: 'A',
|
||||
scope: false,
|
||||
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
|
||||
controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
|
||||
function($scope, $element, $attrs, $transclude, otherInjectables) { ... }],
|
||||
compile: function compile(tElement, tAttrs, transclude) {
|
||||
return {
|
||||
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
|
||||
@@ -307,8 +308,9 @@ compiler}. The attributes are:
|
||||
|
||||
* `priority` - When there are multiple directives defined on a single DOM element, sometimes it
|
||||
is necessary to specify the order in which the directives are applied. The `priority` is used
|
||||
to sort the directives before their `compile` functions get called. Higher `priority` goes
|
||||
first. The order of directives within the same priority is undefined.
|
||||
to sort the directives before their `compile` functions get called. Priority is defined as a
|
||||
number. Directives with greater numerical `priority` are compiled first. The order of directives with
|
||||
the same priority is undefined. The default priority is `0`.
|
||||
|
||||
* `terminal` - If set to true then the current `priority` will be the last set of directives
|
||||
which will execute (any directives at the current priority will still execute
|
||||
@@ -496,7 +498,7 @@ The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a
|
||||
link() or compile() functions - is a way of accessing:
|
||||
|
||||
* *normalized attribute names:* Since a directive such as 'ngBind' can be expressed in many ways
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
|
||||
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized access to
|
||||
the attributes.
|
||||
|
||||
* *directive inter-communication:* All directives share the same instance of the attributes
|
||||
@@ -645,10 +647,10 @@ Following is an example of building a reusable widget.
|
||||
'</div>',
|
||||
// The linking function will add behavior to the template
|
||||
link: function(scope, element, attrs) {
|
||||
// Title element
|
||||
// Title element
|
||||
var title = angular.element(element.children()[0]),
|
||||
// Opened / closed state
|
||||
opened = true;
|
||||
// Opened / closed state
|
||||
opened = true;
|
||||
|
||||
// Clicking on title should open/close the zippy
|
||||
title.bind('click', toggle);
|
||||
|
||||
@@ -23,8 +23,8 @@ You can think of Angular expressions as JavaScript expressions with following di
|
||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||
`window`.
|
||||
|
||||
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript,
|
||||
where such evaluations generate `NullPointerExceptions`.
|
||||
* **Forgiving:** expression evaluation is forgiving to `undefined` and `null`, unlike in JavaScript,
|
||||
where trying to evaluate undefined properties can generate `ReferenceError` or `TypeError`.
|
||||
|
||||
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
|
||||
conditionals, loops, or throw.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
@name Forms
|
||||
@description
|
||||
|
||||
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
|
||||
Form is a collection of controls for the purpose of grouping related controls together.
|
||||
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
|
||||
A Form is a collection of controls for the purpose of grouping related controls together.
|
||||
|
||||
Form and controls provide validation services, so that the user can be notified of invalid input.
|
||||
This provides a better user experience, because the user gets instant feedback on how to correct the error.
|
||||
@@ -154,10 +154,10 @@ This allows us to extend the above example with these features:
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
|
||||
@@ -56,22 +56,22 @@ locale-specific file to the end of `angular.js` or `angular.min.js` file.
|
||||
For example on *nix, to create an angular.js file that contains localization rules for german
|
||||
locale, you can do the following:
|
||||
|
||||
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
|
||||
`cat angular.js i18n/angular-locale_de-de.js > angular_de-de.js`
|
||||
|
||||
When the application containing `angular_de-ge.js` script instead of the generic angular.js script
|
||||
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**
|
||||
|
||||
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-ge.html which will look something like this:
|
||||
requires German locale, you would serve index_de-de.html which will look something like this:
|
||||
|
||||
<pre>
|
||||
<html ng-app>
|
||||
<head>
|
||||
….
|
||||
<script src="angular.js"></script>
|
||||
<script src="i18n/angular-locale_de-ge.js"></script>
|
||||
<script src="i18n/angular-locale_de-de.js"></script>
|
||||
….
|
||||
</head>
|
||||
</html>
|
||||
|
||||
@@ -191,6 +191,24 @@ scripts into a VM. There are existing projects which deal with script loading, w
|
||||
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
|
||||
and thus script loaders can take advantage of this property and parallelize the loading process.
|
||||
|
||||
## Creation versus Retrieval
|
||||
|
||||
Beware that using `angular.module('myModule', [])` will create the module `myModule` and overwrite any
|
||||
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
|
||||
|
||||
<pre>
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// add some directives and services
|
||||
myModule.service('myService', ...);
|
||||
myModule.directive('myDirective', ...);
|
||||
|
||||
// overwrites both myService and myDirective by creating a new module
|
||||
var myModule = angular.module('myModule', []);
|
||||
|
||||
// throws an error because myOtherModule has yet to be defined
|
||||
var myModule = angular.module('myOtherModule');
|
||||
</pre>
|
||||
|
||||
# Unit Testing
|
||||
|
||||
|
||||
@@ -8,28 +8,28 @@
|
||||
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
|
||||
language and lets you extend HTML's syntax to express your application's components clearly and
|
||||
succinctly. Out of the box, it eliminates much of the code you currently write through data
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser making it an
|
||||
ideal partner with any server technology.
|
||||
binding and dependency injection. And it all happens in JavaScript within the browser, making it
|
||||
an ideal partner with any server technology.
|
||||
|
||||
Angular is what HTML would have been had it been designed for applications. HTML is a great
|
||||
declarative language for static documents. It does not contain much in the way of creating
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do, so
|
||||
that I trick the browser in to doing what I want.*
|
||||
applications, and as a result building web applications is an exercise in *what do I have to do
|
||||
to trick the browser into doing what I want.*
|
||||
|
||||
The impedance mismatch between dynamic applications and static documents is often solved as:
|
||||
The impedance mismatch between dynamic applications and static documents is often solved with:
|
||||
|
||||
* **library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
* **a library** - a collection of functions which are useful when writing web apps. Your code is
|
||||
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
|
||||
* **frameworks** - a particular implementation of a web application, where your code fills in
|
||||
the details. The framework is in charge and it calls into your code when it needs something
|
||||
app specific. E.g., `knockout`, `sproutcore`, etc.
|
||||
app specific. E.g., `knockout`, `ember`, etc.
|
||||
|
||||
|
||||
Angular takes another approach. It attempts to minimize the impedance mismatch between document
|
||||
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
|
||||
browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
* Data binding as in `{{}}`.
|
||||
* Data binding, as in `{{}}`.
|
||||
* DOM control structures for repeating/hiding DOM fragments.
|
||||
* Support for forms and form validation.
|
||||
* Attaching code-behind to DOM elements.
|
||||
@@ -37,13 +37,13 @@ browser new syntax through a construct we call directives. Examples include:
|
||||
|
||||
|
||||
|
||||
## End-to-end solution
|
||||
## A complete client-side solution
|
||||
|
||||
Angular tries to be an end-to-end solution, when building a web application. This means it is
|
||||
not a single piece in an overall puzzle of building a web application, but an end-to-end solution.
|
||||
This makes Angular opinionated about how a CRUD application should be built. But while it is
|
||||
opinionated, it also tries to make sure that its opinion is just a starting point, which you can
|
||||
easily change. Angular comes with the following out-of-the-box:
|
||||
Angular is not a single piece in the overall puzzle of building the client-side of a web
|
||||
application. It handles all of the DOM and AJAX glue code you once wrote by hand and puts it in a
|
||||
well-defined structure. This makes Angular opinionated about how a CRUD application should be
|
||||
built. But while it is opinionated, it also tries to make sure that its opinion is just a
|
||||
starting point you can easily change. Angular comes with the following out-of-the-box:
|
||||
|
||||
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
|
||||
directives, form validation, routing, deep-linking, reusable components, dependency injection.
|
||||
@@ -56,12 +56,12 @@ easily change. Angular comes with the following out-of-the-box:
|
||||
Angular simplifies application development by presenting a higher level of abstraction to the
|
||||
developer. Like any abstraction, it comes at a cost of flexibility. In other words not every app
|
||||
is a good fit for Angular. Angular was built for the CRUD application in mind. Luckily CRUD
|
||||
applications represent at least 90% of the web applications. But to understand what Angular is
|
||||
applications represent the majority of web applications. But to understand what Angular is
|
||||
good at one also has to understand when an app is not a good fit for Angular.
|
||||
|
||||
Games, and GUI editors are examples of very intensive and tricky DOM manipulation. These kinds of
|
||||
apps are different from CRUD apps, and as a result are not a good fit for Angular. In these cases
|
||||
using something closer to bare metal such as `jQuery` may be a better fit.
|
||||
Games and GUI editors are examples of applications with intensive and tricky DOM manipulation.
|
||||
These kinds of apps are different from CRUD apps, and as a result are probably not a good fit for Angular.
|
||||
In these cases it may be better to use a library with a lower level of abstraction, such as `jQuery`.
|
||||
|
||||
|
||||
# An Introductory Angular Example
|
||||
@@ -134,7 +134,7 @@ These input widgets look normal enough, but consider these points:
|
||||
Model-View-Controller design pattern.
|
||||
* Note that the HTML widget {@link api/ng.directive:input input}
|
||||
has special powers. The input invalidates itself by turning red when you enter invalid data or
|
||||
leave the the input fields blank. These new widget behaviors make it easier to implement field
|
||||
leave the input fields blank. These new widget behaviors make it easier to implement field
|
||||
validation common in CRUD applications.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
@@ -151,7 +151,7 @@ into output that looks like money."
|
||||
|
||||
Notice that we achieved this application behavior not by calling Angular methods, nor by
|
||||
implementing application specific behavior as a framework. We achieved the behavior because the
|
||||
browser behaved more in line with what is needed for a dynamic web application rather then what is
|
||||
browser behaved more in line with what is needed for a dynamic web application rather than what is
|
||||
needed for a static document. Angular has lowered the impedance mismatch to the point where no
|
||||
library/framework calls are needed.
|
||||
|
||||
@@ -175,7 +175,7 @@ expressing business logic.
|
||||
|
||||
|
||||
|
||||
Angular frees you from the following pain:
|
||||
Angular frees you from the following pains:
|
||||
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, making it hard to see the
|
||||
forest for the trees. Removing common boilerplate code such as callbacks is a good thing. It
|
||||
|
||||
@@ -247,31 +247,27 @@ To create and submit a change:
|
||||
[print, sign and one of scan+email, fax or mail the form](http://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
|
||||
|
||||
2. Create a new branch off the master for your changes:
|
||||
2. Create and checkout a new branch off the master branch for your changes:
|
||||
|
||||
git branch my-fix-branch
|
||||
git checkout -b my-fix-branch master
|
||||
|
||||
3. Check out the branch:
|
||||
3. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
git checkout my-fix-branch
|
||||
|
||||
4. Create your patch, make sure to have plenty of tests (that pass).
|
||||
|
||||
5. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
|
||||
4. 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](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#)
|
||||
and our commit message presubmit hook `validate-commit-msg.js`):
|
||||
|
||||
git commit -a
|
||||
|
||||
6. Push your branch to Github:
|
||||
5. Push your branch to Github:
|
||||
|
||||
git push origin my-fix-branch
|
||||
|
||||
7. In Github, send a pull request to `angular:master`.
|
||||
6. In Github, send a pull request to `angular:master`.
|
||||
|
||||
|
||||
8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
|
||||
7. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
1. To delete the branch in Github, run:
|
||||
|
||||
@@ -51,7 +51,7 @@ Yes. See instructions in {@link downloading}.
|
||||
### What browsers does Angular work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
Explorer Compatibility} for more details in supporting legacy IE browsers.
|
||||
|
||||
|
||||
@@ -101,9 +101,9 @@ The MIT License.
|
||||
|
||||
### Can I download and use the Angular logo artwork?
|
||||
|
||||
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
|
||||
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
|
||||
angular.js/images/logo}"
|
||||
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
|
||||
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
|
||||
Creative Commons Attribution-ShareAlike 3.0 Unported License}". If you have some other use in mind, contact us.
|
||||
|
||||
### How can I get some AngularJS schwag?
|
||||
@@ -114,15 +114,10 @@ they'll waive the setup costs, and you can order any quantity you need.
|
||||
|
||||
**Stickers**
|
||||
Contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, {@link http://www.stickergiant.com StickerGiant} will give you a reorder discount.
|
||||
|
||||
**T-shirts**
|
||||
Contact sales at {@link http://www.customink.com www.customink.com} and tell them you want some shirts with design name "angularjs",
|
||||
just like past order #2106371. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, CustomInk won't charge for any set up fees, and they'll give you a reorder discount.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ becoming an Angular expert.
|
||||
##Watch Videos
|
||||
|
||||
If you haven’t had a chance to watch the videos from the homepage, please check out:
|
||||
|
||||
* {@link http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Introduction to AngularJS}
|
||||
* {@link http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Creating Directives}
|
||||
* {@link http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Communicating with Servers}
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
@ngdoc overview
|
||||
@name Tutorial
|
||||
@name Tutorial: Index
|
||||
@description
|
||||
|
||||
<div class="tutorial-page tutorial-page-no-nav">
|
||||
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.
|
||||
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">
|
||||
|
||||
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 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.
|
||||
* See how Angular creates listeners on your data without the need for DOM manipulation.
|
||||
* Learn a better, easier way to test your web apps.
|
||||
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
|
||||
easier.
|
||||
* 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.
|
||||
* See how Angular creates listeners on your data without the need for DOM manipulation.
|
||||
* Learn a better, easier way to test your web apps.
|
||||
* 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!
|
||||
And all of this works in any browser without modification to the browser!
|
||||
|
||||
When you finish the tutorial you will be able to:
|
||||
When you finish the tutorial you will be able to:
|
||||
|
||||
* Create a dynamic application that works in any browser.
|
||||
* 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.
|
||||
* Identify resources for learning more about AngularJS.
|
||||
* Create a dynamic application that works in any browser.
|
||||
* 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.
|
||||
* Identify resources for learning more about AngularJS.
|
||||
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
writing and running unit and end-to-end tests. Experiments at the end of each step provide
|
||||
suggestions for you to learn more about AngularJS and the application you are building.
|
||||
The tutorial guides you through the entire process of building a simple application, including
|
||||
writing and running unit and end-to-end tests. Experiments at the end of each step provide
|
||||
suggestions for you to learn more about AngularJS and the application you are building.
|
||||
|
||||
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
|
||||
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
|
||||
{@link misc/started Getting Started} document.
|
||||
|
||||
|
||||
|
||||
@@ -45,73 +44,72 @@
|
||||
|
||||
|
||||
|
||||
# Working with the code
|
||||
# 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 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.
|
||||
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 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 will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<div class="tabbable" show="true">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<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.8 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><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 git://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></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 <code>scripts/web-server.js</code>, a simple bundled http server.</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.8 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>
|
||||
don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
<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 git://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 angular-phonecat
|
||||
directory.</p></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 <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
</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 git://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 <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</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.8 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 git://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to angular-phonecat.</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
|
||||
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 <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
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!
|
||||
|
||||
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!
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
</div>
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
|
||||
@@ -13,7 +13,7 @@ angular-seed, and run the application in the browser.
|
||||
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
|
||||
<ol>
|
||||
<li><p>In angular-phonecat directory, run this command:</p>
|
||||
<li><p>In <code>angular-phonecat</code> directory, run this command:</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
@@ -25,7 +25,7 @@ angular-seed, and run the application in the browser.
|
||||
<li><b>For node.js users:</b>
|
||||
<ol>
|
||||
<li>In a <i>separate</i> terminal tab or window, run
|
||||
<code>./scripts/web-server.js</code> to start the web server.</li>
|
||||
<code>node ./scripts/web-server.js</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">http://localhost:8000/app/index.html</a></li>
|
||||
</ol>
|
||||
@@ -46,7 +46,7 @@ directory.</li>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
|
||||
<ol>
|
||||
<li><p>Open Git bash and run this command (in angular-phonecat directory):</p>
|
||||
<li><p>Open Git bash and run this command (in <code>angular-phonecat</code> directory):</p>
|
||||
<pre>git checkout -f step-0</pre>
|
||||
<p>This resets your workspace to step 0 of the tutorial app.</p>
|
||||
<p>You must repeat this for every future step in the tutorial and change the number to
|
||||
@@ -153,7 +153,7 @@ for most cases. In advanced cases, such as when using script loaders, you can us
|
||||
There are 3 important things that happen during the app bootstrap:
|
||||
|
||||
1. The {@link api/AUTO.$injector injector} that will be used for dependency injection
|
||||
within this app is created.
|
||||
when this app is created.
|
||||
|
||||
2. The injector will then create the {@link api/ng.$rootScope root scope} that will
|
||||
become the context for the model of our application.
|
||||
|
||||
@@ -134,10 +134,10 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
The test verifies that we have three records in the phones array and the example demonstrates how
|
||||
easy it is to create a unit test for code in Angular. Since testing is such a critical part of
|
||||
software development, we make it easy to create tests in Angular so that developers are encouraged
|
||||
to write them.
|
||||
The test instantiates our PhoneListCtrl and verifies that its phones array property contains three
|
||||
records. This example demonstrates how easy it is to create a unit test for code in Angular. Since
|
||||
testing is such a critical part of software development, we make it easy to create tests in Angular
|
||||
so that developers are encouraged to write them.
|
||||
|
||||
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
|
||||
|
||||
@@ -69,7 +69,7 @@ available as a filter input in the list repeater (`phone in phones | filter:`__`
|
||||
changes to the data model cause the repeater's input to change, the repeater efficiently updates
|
||||
the DOM to reflect the current state of the model.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_03.png">
|
||||
<img class="diagram" src="img/tutorial/tutorial_03.png">
|
||||
|
||||
* Use of the `filter` filter: The {@link api/ng.filter:filter filter} function uses the
|
||||
`query` value to create a new array that contains only those records that match the `query`.
|
||||
@@ -127,6 +127,9 @@ end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests
|
||||
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` script again.
|
||||
|
||||
Note: You must ensure you've installed karma-ng-scenario prior to running the `e2e-test.sh` script.
|
||||
You can do this by issuing `npm install karma-ng-scenario` 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
|
||||
really is that easy to set up any functional, readable, end-to-end test.
|
||||
@@ -138,12 +141,12 @@ really is that easy to set up any functional, readable, end-to-end test.
|
||||
|
||||
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
You might think you could just add the {{query}} to the title tag element as follows:
|
||||
You might think you could just add the `{{query}}` to the title tag element as follows:
|
||||
|
||||
<title>Google Phone Gallery: {{query}}</title>
|
||||
|
||||
However, when you reload the page, you won't see the expected result. This is because the "query"
|
||||
model lives in the scope defined by the body element:
|
||||
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body element:
|
||||
|
||||
<body ng-controller="PhoneListCtrl">
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ We made the following changes to the `index.html` template:
|
||||
* First, we added a `<select>` html element named `orderProp`, so that our users can pick from the
|
||||
two provided sorting options.
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_04.png">
|
||||
<img class="diagram" src="img/tutorial/tutorial_04.png">
|
||||
|
||||
* We then chained the `filter` filter with {@link api/ng.filter:orderBy `orderBy`}
|
||||
filter to further process the input into the repeater. `orderBy` is a filter that takes an input
|
||||
@@ -178,6 +178,8 @@ ordering will default to unordered/natural order.
|
||||
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
|
||||
text.
|
||||
|
||||
* Reverse the sort order by adding a `-` symbol before the sorting value: `<option value="-age">Oldest</option>`
|
||||
|
||||
# Summary
|
||||
|
||||
Now that you have added list sorting and tested the app, go to {@link step_05 step 5} to learn
|
||||
|
||||
@@ -44,7 +44,7 @@ Following is a sample of the file:
|
||||
|
||||
We'll use angular's {@link api/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 api/ng angular services} that handle common operations
|
||||
one of several built-in {@link guide/dev_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
|
||||
@@ -207,8 +207,6 @@ Finally, we verify that the default value of `orderProp` is set correctly:
|
||||
it('should set the default value of orderProp model', function() {
|
||||
expect(scope.orderProp).toBe('age');
|
||||
});
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
@@ -53,7 +53,7 @@ When the application bootstraps, Angular creates an injector that will be used f
|
||||
this app. The injector itself doesn't know anything about what `$http` or `$route` services do, in
|
||||
fact it doesn't even know about the existence of these services unless it is configured with proper
|
||||
module definitions. The sole responsibilities of the injector are to load specified module
|
||||
definition(s), register all service providers defined in these modules and when asked inject
|
||||
definition(s), register all service providers defined in these modules, and when asked, inject
|
||||
a specified function with dependencies (services) that it lazily instantiates via their providers.
|
||||
|
||||
Providers are objects that provide (create) instances of services and expose configuration APIs
|
||||
|
||||
@@ -37,8 +37,7 @@ angular.module('phonecatFilters', []).filter('checkmark', function() {
|
||||
</pre>
|
||||
|
||||
The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
|
||||
return one of two unicode characters we have chosen to represent true or false (`\u2713` and
|
||||
`\u2718`).
|
||||
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.
|
||||
@@ -95,7 +94,6 @@ describe('filter', function() {
|
||||
|
||||
beforeEach(module('phonecatFilters'));
|
||||
|
||||
|
||||
describe('checkmark', function() {
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
@@ -107,8 +105,12 @@ describe('filter', function() {
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that you need to configure our test injector with the `phonecatFilters` module before any of
|
||||
our filter tests execute.
|
||||
We must call `beforeEach(module('phonecatFilters'))` before any of
|
||||
our filter tests execute. This call loads our `phonecatFilters` module into the injector
|
||||
for this test run.
|
||||
|
||||
Note that we call the helper function, `inject(function(checkmarkFilter) { ... })`, to get
|
||||
access to the filter that we want to test. See {@link api/angular.mock.inject angular.mock.inject()}.
|
||||
|
||||
You should now see the following output in the Karma tab:
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ __`test/e2e/scenarios.js`:__
|
||||
|
||||
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 {@link
|
||||
http://angular.github.com/angular-phonecat/step-8/test/e2e/runner.html
|
||||
http://angular.github.com/angular-phonecat/step-10/test/e2e/runner.html
|
||||
Angular's server}.
|
||||
|
||||
# Experiments
|
||||
|
||||
@@ -135,7 +135,7 @@ The {@link api/ngResource.$resource $resource} service augments the response obj
|
||||
with methods for updating and deleting the resource. If we were to use the standard `toEqual`
|
||||
matcher, our tests would fail because the test values would not match the responses exactly. To
|
||||
solve the problem, we use a newly-defined `toEqualData` {@link
|
||||
http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Matchers.html Jasmine matcher}. When the
|
||||
https://github.com/pivotal/jasmine/wiki/Matchers Jasmine matcher}. When the
|
||||
`toEqualData` matcher compares two objects, it takes only object properties into account and
|
||||
ignores methods.
|
||||
|
||||
|
||||
@@ -2,22 +2,20 @@
|
||||
@name Tutorial: The End
|
||||
@description
|
||||
|
||||
<div class="tutorial-page tutorial-page-no-nav">
|
||||
Our application is now complete. Feel free to experiment with the code further, and jump back to
|
||||
previous steps using the `git checkout` command.
|
||||
Our application is now complete. Feel free to experiment with the code further, and jump back to
|
||||
previous steps using the `git checkout` command.
|
||||
|
||||
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
||||
{@link guide/ Developer Guide}.
|
||||
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
||||
{@link guide/ Developer Guide}.
|
||||
|
||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
||||
|
||||
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
||||
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
||||
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
||||
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
||||
|
||||
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
|
||||
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
|
||||
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
|
||||
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
|
||||
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
|
||||
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
|
||||
|
||||
If you have questions or feedback or just want to say "hi", please post a message at {@link
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
</div>
|
||||
If you have questions or feedback or just want to say "hi", please post a message at {@link
|
||||
https://groups.google.com/forum/#!forum/angular}.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<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"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path fill="#FFFFFF" d="M179.011,125.328V54.527h9.158l43.322,57.035V54.527h8.666v70.801h-9.158l-43.326-57.536v57.536H179.011z
|
||||
M179.011,125.328"/>
|
||||
<path fill="#FFFFFF" d="M310.46,122.554c-5.708,2.182-11.864,3.269-18.467,3.269c-25.644,0-38.469-12.294-38.469-36.887
|
||||
c0-23.27,12.378-34.908,37.134-34.908c7.096,0,13.7,0.994,19.802,2.976v7.921c-6.103-2.311-12.378-3.468-18.813-3.468
|
||||
c-19.306,0-28.96,9.162-28.96,27.479c0,19.639,9.504,29.463,28.517,29.463c3.034,0,6.404-0.396,10.103-1.193V93.145h9.154V122.554z
|
||||
M310.46,122.554"/>
|
||||
<path fill="#FFFFFF" d="M325.067,97.996V54.523h9.154v43.473c0,13.598,6.768,20.4,20.303,20.4c13.531,0,20.301-6.803,20.301-20.4
|
||||
V54.523h9.158v43.473c0,18.556-9.82,27.825-29.459,27.825C334.886,125.821,325.067,116.552,325.067,97.996L325.067,97.996z
|
||||
M325.067,97.996"/>
|
||||
<path fill="#FFFFFF" d="M409.48,54.523v63.376h37.037v7.425h-46.191V54.523H409.48z M409.48,54.523"/>
|
||||
<path fill="#FFFFFF" d="M459.736,125.327h-9.504l35.201-80.146l35.199,80.146h-10.15l-9.158-22.282h-23.418l2.527-7.424h17.82
|
||||
l-13.217-32.088L459.736,125.327z M459.736,125.327"/>
|
||||
<path fill="#FFFFFF" d="M530.289,125.328V54.527h30.203c13.469,0,20.197,5.659,20.197,16.982c0,9.207-6.578,16.028-19.75,20.445
|
||||
l24.309,33.374h-12.086l-22.521-31.835v-5.992c13.531-2.151,20.301-7.344,20.301-15.598c0-6.533-3.766-9.801-11.293-9.801h-20.201
|
||||
v63.226H530.289z M530.289,125.328"/>
|
||||
<path fill="#B52E31" d="M619.561,54.523v50.405c0,13.603-8.006,20.396-24.016,20.396V117.9c9.902,0,14.857-4.329,14.857-12.973
|
||||
V54.523H619.561z M619.561,54.523"/>
|
||||
<path fill="#B52E31" d="M635.896,122.849v-8.418c7.428,2.639,15.447,3.965,24.064,3.965c12.178,0,18.271-4.457,18.271-13.372
|
||||
c0-7.584-4.492-11.385-13.469-11.385h-9.113c-14.818,0-22.234-6.435-22.234-19.31c0-13.531,9.492-20.303,28.479-20.303
|
||||
c8.25,0,15.922,0.998,23.021,2.976v8.418c-7.1-2.644-14.771-3.965-23.021-3.965c-12.875,0-19.311,4.293-19.311,12.875
|
||||
c0,7.588,4.352,11.385,13.066,11.385h9.113c15.08,0,22.627,6.439,22.627,19.31c0,13.864-9.141,20.796-27.43,20.796
|
||||
C651.344,125.819,643.324,124.826,635.896,122.849L635.896,122.849z M635.896,122.849"/>
|
||||
<path fill="#B2B2B2" d="M82.688,0L0,29.1l13.066,108.335l69.71,38.314l70.069-38.834l13.062-108.331L82.688,0z M82.688,0"/>
|
||||
<path fill="#B52E31" d="M157.66,34.846L82.496,9.214v157.381l62.991-34.861L157.66,34.846z M157.66,34.846"/>
|
||||
<path fill="#E23237" d="M9.279,35.308l11.196,96.889l62.019,34.398V9.211L9.279,35.308z M9.279,35.308"/>
|
||||
<path fill="#F2F2F2" d="M99.918,87.493L82.632,51.396L67.415,87.493H99.918z M106.508,102.672h-45.82l-10.251,25.64l-19.067,0.352
|
||||
L82.496,14.929l52.908,113.734h-17.673L106.508,102.672z M106.508,102.672"/>
|
||||
<path fill="#B2B2B2" d="M82.496,14.929l0.136,36.467l17.268,36.125H82.534l-0.039,15.127l24.012,0.023l11.223,25.996l18.245,0.339
|
||||
L82.496,14.929z M82.496,14.929"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
+42
-28
@@ -59,10 +59,10 @@ describe('ngdoc', function() {
|
||||
'@param {function(number, string=)} d fn with optional arguments');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'},
|
||||
{name:'d', description:'<p>fn with optional arguments</p>',
|
||||
{name:'a', description:'<div class="a-page"><p>short</p></div>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<div class="a-page"><p>med</p></div>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<div class="a-page"><p>long\nline</p></div>', type:'Class', optional:true, 'default':'2'},
|
||||
{name:'d', description:'<div class="a-page"><p>fn with optional arguments</p></div>',
|
||||
type: 'function(number, string=)', optional: false, 'default':undefined}
|
||||
]);
|
||||
});
|
||||
@@ -72,7 +72,7 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
type: 'Type',
|
||||
description: '<p>text <em>bold</em>.</p>'
|
||||
description: '<div class="a-page"><p>text <em>bold</em>.</p></div>'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -138,16 +138,30 @@ describe('ngdoc', function() {
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', function() {
|
||||
expect(new Doc().markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x\n' +
|
||||
'<div class="docs-page"><p>bah x\n' +
|
||||
'<pre class="prettyprint linenums">\n' +
|
||||
'<b>angular</b>.k\n' +
|
||||
'</pre>\n' +
|
||||
' asdf x</p>');
|
||||
' asdf x</p></div>');
|
||||
});
|
||||
|
||||
it('should wrap everything inside a container tag', function() {
|
||||
var doc = new Doc('@name superman').parse();
|
||||
var content = doc.markdown('hello');
|
||||
|
||||
expect(content).toMatch('<div class="superman-page"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
it('should use the content before a colon as the name prefix for the className of the tag container', function() {
|
||||
var doc = new Doc('@name super: man').parse();
|
||||
var content = doc.markdown('hello');
|
||||
|
||||
expect(content).toMatch('<div class="super-page super-man-page"><p>hello</p></div>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(new Doc().markdown('<pre>x</pre>\n# One\n<pre>b</pre>')).
|
||||
toMatch('</pre>\n\n<h1>One</h1>\n\n<pre');
|
||||
toMatch('</pre>\n\n<h1 id="one">One</h1>\n\n<pre');
|
||||
});
|
||||
|
||||
it('should ignore nested doc widgets', function() {
|
||||
@@ -157,11 +171,11 @@ describe('ngdoc', function() {
|
||||
'\ngit bla bla\n</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>')).toEqual(
|
||||
|
||||
'<p>before<div class="tabbable">\n' +
|
||||
'<div class="docs-page"><p>before<div class="tabbable">\n' +
|
||||
'<div class="tab-pane well" id="git-mac" ng:model="Git on Mac/Linux">\n' +
|
||||
'git bla bla\n' +
|
||||
'</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions></p>');
|
||||
'</doc:tutorial-instructions></p></div>');
|
||||
});
|
||||
|
||||
it('should unindent text before processing based on the second line', function() {
|
||||
@@ -263,7 +277,7 @@ describe('ngdoc', function() {
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : '<p>Number \nto format.</p>' }]);
|
||||
description : '<div class="a-page"><p>Number \nto format.</p></div>' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function() {
|
||||
@@ -274,7 +288,7 @@ describe('ngdoc', function() {
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : '<p>desc</p>' }]);
|
||||
description : '<div class="a-page"><p>desc</p></div>' }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -284,8 +298,8 @@ describe('ngdoc', function() {
|
||||
doc.ngdoc = 'service';
|
||||
doc.parse();
|
||||
expect(doc.requires).toEqual([
|
||||
{name:'$service', text:'<p>for \n<code>A</code></p>'},
|
||||
{name:'$another', text:'<p>for <code>B</code></p>'}]);
|
||||
{name:'$service', text:'<div class="a-page"><p>for \n<code>A</code></p></div>'},
|
||||
{name:'$another', text:'<div class="a-page"><p>for <code>B</code></p></div>'}]);
|
||||
expect(doc.html()).toContain('<a href="api/ng.$service">$service</a>');
|
||||
expect(doc.html()).toContain('<a href="api/ng.$another">$another</a>');
|
||||
expect(doc.html()).toContain('<p>for \n<code>A</code></p>');
|
||||
@@ -337,7 +351,7 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc("@name a\n@property {string} name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
|
||||
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
@@ -345,7 +359,7 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('bool');
|
||||
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
|
||||
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -368,26 +382,26 @@ describe('ngdoc', function() {
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<div class="a-page"><p>descrip tion</p></div>'});
|
||||
});
|
||||
|
||||
it('should parse @returns with complex type and description', function() {
|
||||
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
|
||||
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<div class="a-page"><p>description</p></div>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip <em>tion</em></p>'});
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<div class="a-page"><p>descrip <em>tion</em></p></div>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
var doc = new Doc("@name a\n@returns {string} description\n new line\n another line");
|
||||
doc.parse();
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: '<p>description\nnew line\nanother line</p>'});
|
||||
toEqual({type: 'string', description: '<div class="a-page"><p>description\nnew line\nanother line</p></div>'});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -396,18 +410,18 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc("@name a\n@description <pre><b>abc</b></pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<pre class="prettyprint linenums"><b>abc</b></pre>');
|
||||
toBe('<div class="a-page"><pre class="prettyprint linenums"><b>abc</b></pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@name a\n@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo \n' +
|
||||
toBe('<div class="a-page"><p>foo \n' +
|
||||
'<pre class="prettyprint linenums">abc</pre>\n\n' +
|
||||
'<h1>bah</h1>\n\n' +
|
||||
'<h1 id="bah">bah</h1>\n\n' +
|
||||
'<p>foo \n' +
|
||||
'<pre class="prettyprint linenums">cba</pre>');
|
||||
'<pre class="prettyprint linenums">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
@@ -450,7 +464,7 @@ describe('ngdoc', function() {
|
||||
it('should not remove {{}}', function() {
|
||||
var doc = new Doc('@name a\n@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('<p>text {{ abc }}</p>');
|
||||
expect(doc.example).toEqual('<div class="a-page"><p>text {{ abc }}</p></div>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -469,10 +483,10 @@ describe('ngdoc', function() {
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div>' +
|
||||
'<p>I am self.</p>' +
|
||||
'<div class="a-page"><p>I am self.</p></div>' +
|
||||
'</div>\n');
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div><p>I am self.</p></div>');
|
||||
'<div><div class="a-page"><p>I am self.</p></div></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -483,7 +497,7 @@ describe('ngdoc', function() {
|
||||
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('text');
|
||||
expect(doc.html()).toContain('<h2>heading</h2>');
|
||||
expect(doc.html()).toContain('<h1 id="heading">heading</h2>');
|
||||
expect(doc.html()).not.toContain('Description');
|
||||
});
|
||||
});
|
||||
|
||||
+20
-1
@@ -173,8 +173,27 @@ Doc.prototype = {
|
||||
'</a>';
|
||||
});
|
||||
});
|
||||
|
||||
text = parts.join('');
|
||||
text = new Showdown.converter({ extensions : ['table'] }).makeHtml(text);
|
||||
|
||||
function prepareClassName(text) {
|
||||
return text.toLowerCase().replace(/[_\W]+/g, '-');
|
||||
};
|
||||
|
||||
var pageClassName, suffix = '-page';
|
||||
if(this.name) {
|
||||
var split = this.name.match(/^\s*(.+?)\s*:\s*(.+)/);
|
||||
if(split && split.length > 1) {
|
||||
var before = prepareClassName(split[1]);
|
||||
var after = prepareClassName(split[2]);
|
||||
pageClassName = before + suffix + ' ' + before + '-' + after + suffix;
|
||||
}
|
||||
}
|
||||
pageClassName = pageClassName || prepareClassName(this.name || 'docs') + suffix;
|
||||
|
||||
text = '<div class="' + pageClassName + '">' +
|
||||
(new Showdown.converter({ extensions : ['table'] }).makeHtml(text)) +
|
||||
'</div>';
|
||||
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
|
||||
return placeholderMap[id];
|
||||
});
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
img.AngularJS-small {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
/* Logo */
|
||||
|
||||
.header .brand {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.header .brand img {
|
||||
height: 25px;
|
||||
width: 92px;
|
||||
}
|
||||
|
||||
/* end: Logo */
|
||||
|
||||
|
||||
.clear-navbar {
|
||||
margin-top: 60px;
|
||||
@@ -88,6 +97,11 @@ img.AngularJS-small {
|
||||
|
||||
.improve-docs {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
z-index:100;
|
||||
}
|
||||
|
||||
.hint {
|
||||
@@ -102,6 +116,7 @@ img.AngularJS-small {
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content h2,
|
||||
@@ -189,13 +204,16 @@ ul.events > li > h3 {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tutorial-index-page,
|
||||
.tutorial-the-end-page {
|
||||
padding-top:50px;
|
||||
}
|
||||
|
||||
.tutorial-page {
|
||||
position:relative;
|
||||
}
|
||||
.tutorial-page-no-nav {
|
||||
padding-top:50px;
|
||||
}
|
||||
.tutorial-page-no-nav .improve-docs {
|
||||
|
||||
.tutorial-page .improve-docs {
|
||||
position:absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
|
||||
@@ -114,8 +114,8 @@
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="http://angularjs.org" style="padding-top: 6px; padding-bottom: 0px;">
|
||||
<img class="AngularJS-small" src="http://angularjs.org/img/AngularJS-small.png">
|
||||
<a class="brand" href="http://angularjs.org">
|
||||
<img class="logo" src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -130,6 +130,7 @@
|
||||
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
|
||||
<li><a href="tutorial">Tutorial</a></li>
|
||||
<li><a href="http://builtwith.angularjs.org/">Case Studies</a></li>
|
||||
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
|
||||
<li><a href="misc/faq">FAQ</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -139,9 +140,9 @@
|
||||
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="http://docs.angularjs.org/tutorial">Tutorial</a></li>
|
||||
<li><a href="http://docs.angularjs.org/guide/">Developer Guide</a></li>
|
||||
<li><a href="http://docs.angularjs.org/api/">API Reference</a></li>
|
||||
<li><a href="./tutorial/">Tutorial</a></li>
|
||||
<li><a href="./guide/">Developer Guide</a></li>
|
||||
<li><a href="./api/">API Reference</a></li>
|
||||
<li><a href="http://docs.angularjs.org/misc/contribute">Contribute</a></li>
|
||||
<li><a href="http://code.angularjs.org/">Download</a></li>
|
||||
</ul>
|
||||
@@ -203,7 +204,7 @@
|
||||
<div class="dropdown search"
|
||||
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
|
||||
<input type="text" ng-model="search" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
|
||||
tabindex="1" accesskey="s" class="input-medium search-query" />
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
|
||||
|
||||
@@ -18,8 +18,8 @@ docsApp.directive.focused = function($timeout) {
|
||||
scope.$eval(attrs.focused + '=false');
|
||||
});
|
||||
});
|
||||
scope.$eval(attrs.focused + '=true')
|
||||
}
|
||||
scope.$eval(attrs.focused + '=true');
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ docsApp.directive.sourceEdit = function(getEmbeddedTemplate) {
|
||||
openPlunkr(sources);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function read(text) {
|
||||
var files = [];
|
||||
@@ -127,7 +127,7 @@ docsApp.directive.docTutorialReset = function() {
|
||||
'</div>\n');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.serviceFactory.angularUrls = function($document) {
|
||||
@@ -141,7 +141,7 @@ docsApp.serviceFactory.angularUrls = function($document) {
|
||||
});
|
||||
|
||||
return urls;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
docsApp.serviceFactory.formPostData = function($document) {
|
||||
@@ -174,7 +174,7 @@ docsApp.serviceFactory.openPlunkr = function(templateMerge, formPostData, angula
|
||||
var scriptDeps = '';
|
||||
angular.forEach(content.deps, function(file) {
|
||||
if (file.name !== 'angular.js') {
|
||||
scriptDeps += ' <script src="' + file.name + '"></script>\n'
|
||||
scriptDeps += ' <script src="' + file.name + '"></script>\n';
|
||||
}
|
||||
});
|
||||
indexProp = {
|
||||
@@ -310,7 +310,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
last: this.$last,
|
||||
active: page1 && this.currentPage == page1 || page2 && this.currentPage == page2
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$scope.submitForm = function() {
|
||||
$scope.bestMatch && $location.path($scope.bestMatch.page.url);
|
||||
@@ -509,7 +509,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
},
|
||||
types: [],
|
||||
filters: []
|
||||
}
|
||||
};
|
||||
modules.push(module);
|
||||
}
|
||||
return module;
|
||||
@@ -560,7 +560,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
angular.module('docsApp', ['ngResource', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify']).
|
||||
|
||||
@@ -257,5 +257,10 @@ describe("serializeContent", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
|
||||
expect((/[^\u0001-\u007f]/).test(serializedContent)).toBe(false);
|
||||
});
|
||||
it("should not transform arrays into objects", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo().fr_CA);
|
||||
var deserializedLocale = eval("(" + serializedContent + ")");
|
||||
expect(deserializedLocale.DATETIME_FORMATS.MONTH.length).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ function canonicalizeForJsonStringify(unused_key, object) {
|
||||
// 2. https://code.google.com/p/v8/issues/detail?id=164
|
||||
// ECMA-262 does not specify enumeration order. The de facto standard
|
||||
// is to match insertion order, which V8 also does ...
|
||||
if (typeof object != "object") {
|
||||
if (typeof object != "object" || Object.prototype.toString.apply(object) === '[object Array]') {
|
||||
return object;
|
||||
}
|
||||
var result = {};
|
||||
|
||||
@@ -7,8 +7,8 @@ cd $BASE_DIR
|
||||
|
||||
set -x # Trace commands as they're executed.
|
||||
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
curl http://google.github.io/closure-library/source/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://google.github.io/closure-library/source/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://google.github.io/closure-library/source/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://google.github.io/closure-library/source/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://google.github.io/closure-library/source/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
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"
|
||||
fi
|
||||
|
||||
if [[ -z "$BROWSERS_E2E" ]]
|
||||
then
|
||||
BROWSERS_E2E="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
|
||||
fi
|
||||
|
||||
|
||||
# CLEAN #
|
||||
rm -f angular.min.js.gzip.size
|
||||
rm -f angular.js.size
|
||||
|
||||
|
||||
# BUILD #
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
|
||||
# UNIT TESTS #
|
||||
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
|
||||
# CHECK SIZE #
|
||||
gzip -c < build/angular.min.js > build/angular.min.js.gzip
|
||||
echo "YVALUE=`ls -l build/angular.min.js | cut -d" " -f 8`" > angular.min.js.size
|
||||
echo "YVALUE=`ls -l build/angular.min.js.gzip | cut -d" " -f 8`" > angular.min.js.gzip.size
|
||||
+21
-16
@@ -1,20 +1,25 @@
|
||||
var angularFiles = require(__dirname + '/angularFiles.js');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
files = [ANGULAR_SCENARIO, ANGULAR_SCENARIO_ADAPTER, 'build/docs/docs-scenario.js'];
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
autoWatch = false;
|
||||
singleRun = true;
|
||||
logLevel = LOG_INFO;
|
||||
logColors = true;
|
||||
browsers = ['Chrome'];
|
||||
config.set({
|
||||
frameworks: ['ng-scenario'],
|
||||
files: [
|
||||
'build/docs/docs-scenario.js'
|
||||
],
|
||||
|
||||
proxies = {
|
||||
// angular.js, angular-resource.js, etc
|
||||
'/angular': 'http://localhost:8000/build/angular',
|
||||
'/': 'http://localhost:8000/build/docs/'
|
||||
};
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'test_out/e2e.xml',
|
||||
suite: 'E2E'
|
||||
proxies: {
|
||||
// angular.js, angular-resource.js, etc
|
||||
'/angular': 'http://localhost:8000/build/angular',
|
||||
'/': 'http://localhost:8000/build/docs/'
|
||||
},
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/e2e.xml',
|
||||
suite: 'E2E'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: e2e';
|
||||
};
|
||||
|
||||
+14
-10
@@ -1,14 +1,18 @@
|
||||
var angularFiles = require(__dirname + '/angularFiles.js');
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
files = angularFiles.mergeFiles(JASMINE, JASMINE_ADAPTER, 'jstd');
|
||||
exclude = ['**/*jasmine*/**', '**/*jstd*/**'].concat(angularFiles.files.jstdExclude);
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
autoWatch = true;
|
||||
logLevel = LOG_INFO;
|
||||
logColors = true;
|
||||
browsers = ['Chrome'];
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karma'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaExclude'),
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'test_out/jqlite.xml',
|
||||
suite: 'jqLite'
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jqlite.xml',
|
||||
suite: 'jqLite'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: jqLite';
|
||||
};
|
||||
|
||||
+14
-10
@@ -1,14 +1,18 @@
|
||||
var angularFiles = require(__dirname + '/angularFiles.js');
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
files = angularFiles.mergeFiles(JASMINE, JASMINE_ADAPTER, 'jstdJquery');
|
||||
exclude = ['**/*jasmine*/**', '**/*jstd*/**'].concat(angularFiles.files.jstdJqueryExclude);
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
autoWatch = true;
|
||||
logLevel = LOG_INFO;
|
||||
logColors = true;
|
||||
browsers = ['Chrome'];
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaJquery'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: jQuery';
|
||||
};
|
||||
|
||||
+13
-10
@@ -1,14 +1,17 @@
|
||||
var angularFiles = require(__dirname + '/angularFiles.js');
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
files = angularFiles.mergeFiles(JASMINE, JASMINE_ADAPTER, 'jstdModules', 'angularSrcModules');
|
||||
exclude = ['**/*jasmine*/**', '**/*jstd*/**'];
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config);
|
||||
|
||||
autoWatch = true;
|
||||
logLevel = LOG_INFO;
|
||||
logColors = true;
|
||||
browsers = ['Chrome'];
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaModules', 'angularSrcModules'),
|
||||
|
||||
junitReporter = {
|
||||
outputFile: 'test_out/modules.xml',
|
||||
suite: 'modules'
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/modules.xml',
|
||||
suite: 'modules'
|
||||
}
|
||||
});
|
||||
|
||||
config.sauceLabs.testName = 'AngularJS: modules';
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
frameworks: ['jasmine'],
|
||||
autoWatch: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
logColors: true,
|
||||
browsers: ['Chrome'],
|
||||
runnerPort: 0,
|
||||
|
||||
// config for Travis CI
|
||||
sauceLabs: {
|
||||
testName: 'AngularJS',
|
||||
startConnect: false,
|
||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
||||
},
|
||||
|
||||
customLaunchers: {
|
||||
'SL_Chrome': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome'
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+37
-6
@@ -2,22 +2,32 @@ var fs = require('fs');
|
||||
var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var version;
|
||||
|
||||
module.exports = {
|
||||
|
||||
init: function() {
|
||||
shell.exec('npm install');
|
||||
if (!process.env.TRAVIS) {
|
||||
shell.exec('npm install');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
var match = package.version.match(/^([^\-]*)(-snapshot)?$/);
|
||||
// TODO(brian): change `(-|rc)` to `-` in the regex below after bower
|
||||
// fixes this issue: https://github.com/bower/bower/issues/782
|
||||
var match = package.version.match(/^([^\-]*)(?:(-|rc)(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
var version = {
|
||||
full: (match[1] + (match[2] ? '-' + hash : '')),
|
||||
var fullVersion = (match[1] + (match[2] ? '-' + hash : ''));
|
||||
var numVersion = semver[0] + '.' + semver[1] + '.' + semver[2];
|
||||
version = {
|
||||
number: numVersion,
|
||||
full: fullVersion,
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2],
|
||||
@@ -33,11 +43,13 @@ module.exports = {
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
var noColor = grunt.option('no-colors');
|
||||
var port = grunt.option('port');
|
||||
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
|
||||
singleRun ? '--single-run=true' : '',
|
||||
reporters ? '--reporters=' + reporters : '',
|
||||
browsers ? '--browsers=' + browsers : '',
|
||||
noColor ? '--no-colors' : ''
|
||||
noColor ? '--no-colors' : '',
|
||||
port ? '--port=' + port : ''
|
||||
]);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
@@ -170,5 +182,24 @@ module.exports = {
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
parallelTask: function(name) {
|
||||
var args = [name, '--port=' + this.lastParallelTaskPort];
|
||||
|
||||
if (grunt.option('browsers')) {
|
||||
args.push('--browsers=' + grunt.option('browsers'));
|
||||
}
|
||||
|
||||
if (grunt.option('reporters')) {
|
||||
args.push('--reporters=' + grunt.option('reporters'));
|
||||
}
|
||||
|
||||
this.lastParallelTaskPort++;
|
||||
|
||||
|
||||
return {grunt: true, args: args};
|
||||
},
|
||||
|
||||
lastParallelTaskPort: 9876
|
||||
};
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
# Wait for Connect to be ready before exiting
|
||||
while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do
|
||||
sleep .5
|
||||
done
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Setup and start Sauce Connect for your TravisCI build
|
||||
# This script requires your .travis.yml to include the following two private env variables:
|
||||
# SAUCE_USERNAME
|
||||
# SAUCE_ACCESS_KEY
|
||||
# Follow the steps at https://saucelabs.com/opensource/travis to set that up.
|
||||
#
|
||||
# Curl and run this script as part of your .travis.yml before_script section:
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="Sauce_Connect.zip"
|
||||
CONNECT_LOG="$CONNECT_DIR/log"
|
||||
|
||||
# Get Connect and start it
|
||||
mkdir -p $CONNECT_DIR
|
||||
cd $CONNECT_DIR
|
||||
curl $CONNECT_URL > $CONNECT_DOWNLOAD 2> /dev/null
|
||||
unzip $CONNECT_DOWNLOAD
|
||||
rm $CONNECT_DOWNLOAD
|
||||
|
||||
|
||||
|
||||
ARGS=""
|
||||
|
||||
# Set tunnel-id only on Travis, to make local testing easier.
|
||||
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
||||
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
||||
fi
|
||||
if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then
|
||||
ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE"
|
||||
fi
|
||||
|
||||
echo "Starting Sauce Connect in the background"
|
||||
echo "Logging into $CONNECT_LOG"
|
||||
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY > $CONNECT_LOG &
|
||||
+24
-10
@@ -1,20 +1,34 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"version": "1.0.7",
|
||||
"cdnVersion": "1.0.6",
|
||||
"codename": "monochromatic-rainbow",
|
||||
"name": "angularjs",
|
||||
"version": "1.0.9-snapshot",
|
||||
"cdnVersion": "1.0.7",
|
||||
"codename": "marc-todo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.0",
|
||||
"grunt-contrib-clean": "0.4.0",
|
||||
"grunt-contrib-compress": "0.4.1",
|
||||
"grunt-contrib-connect": "0.1.2",
|
||||
"grunt-contrib-copy": "0.4.1",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.3.0",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-parallel": "git://github.com/vojtajina/grunt-parallel.git#streaming-per-task",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"jasmine-node": "1.2.3",
|
||||
"q": "~0.9.2",
|
||||
"q-fs": "0.1.36",
|
||||
"qq": "0.3.5",
|
||||
"shelljs": "0.1.2",
|
||||
"karma": "0.8.4",
|
||||
"karma": "~0.10",
|
||||
"karma-jasmine": "~0.1",
|
||||
"karma-chrome-launcher": "~0.1",
|
||||
"karma-firefox-launcher": "~0.1",
|
||||
"karma-ng-scenario": "~0.1",
|
||||
"karma-junit-reporter": "~0.1",
|
||||
"karma-sauce-launcher": "~0.1",
|
||||
"karma-script-launcher": "~0.1",
|
||||
"yaml-js": "0.0.5",
|
||||
"showdown": "0.3.1"
|
||||
},
|
||||
|
||||
+150
-23
@@ -276,7 +276,7 @@ noop.$inject = [];
|
||||
*
|
||||
<pre>
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || identity)(value);
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
</pre>
|
||||
*/
|
||||
@@ -403,6 +403,18 @@ function isArray(value) {
|
||||
function isFunction(value){return typeof value == 'function';}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a value is a regular expression object.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `RegExp`.
|
||||
*/
|
||||
function isRegExp(value) {
|
||||
return toString.apply(value) == '[object RegExp]';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if `obj` is a window object.
|
||||
*
|
||||
@@ -430,9 +442,20 @@ function isBoolean(value) {
|
||||
}
|
||||
|
||||
|
||||
function trim(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
}
|
||||
var trim = (function() {
|
||||
// native trim is way faster: http://jsperf.com/angular-trim-test
|
||||
// but IE doesn't have it... :-(
|
||||
// TODO: we should move this into IE/ES5 polyfill
|
||||
if (!String.prototype.trim) {
|
||||
return function(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
};
|
||||
}
|
||||
return function(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -555,7 +578,8 @@ function isLeafNode (node) {
|
||||
* * If no destination is supplied, a copy of the object or array is created.
|
||||
* * If a destination is provided, all of its elements (for array) or properties (for objects)
|
||||
* are deleted and then all elements/properties from the source are copied to it.
|
||||
* * If `source` is not an object or array, `source` is returned.
|
||||
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
|
||||
* * If `source` is identical to 'destination' an exception will be thrown.
|
||||
*
|
||||
* Note: this function is used to augment the Object type in Angular expressions. See
|
||||
* {@link ng.$filter} for more information about Angular arrays.
|
||||
@@ -565,6 +589,42 @@ function isLeafNode (node) {
|
||||
* @param {(Object|Array)=} destination Destination into which the source is copied. If
|
||||
* provided, must be of the same type as `source`.
|
||||
* @returns {*} The copy or updated `destination`, if `destination` was specified.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng-controller="Controller">
|
||||
<form novalidate class="simple-form">
|
||||
Name: <input type="text" ng-model="user.name" /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
<pre>form = {{user | json}}</pre>
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
// Example with 1 argument
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
// Example with 2 arguments
|
||||
angular.copy($scope.master, $scope.user);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
function copy(source, destination){
|
||||
if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope");
|
||||
@@ -575,6 +635,8 @@ function copy(source, destination){
|
||||
destination = copy(source, []);
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source);
|
||||
} else if (isObject(source)) {
|
||||
destination = copy(source, {});
|
||||
}
|
||||
@@ -622,7 +684,7 @@ function shallowCopy(src, dst) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Determines if two objects or two values are equivalent. Supports value types, arrays and
|
||||
* Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and
|
||||
* objects.
|
||||
*
|
||||
* Two objects or values are considered equivalent if at least one of the following is true:
|
||||
@@ -630,6 +692,9 @@ function shallowCopy(src, dst) {
|
||||
* * Both objects or values pass `===` comparison.
|
||||
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
|
||||
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
|
||||
* * Both values represent the same regular expression (In JavasScript,
|
||||
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
|
||||
* representation matches).
|
||||
*
|
||||
* During a property comparision, properties of `function` type and properties with names
|
||||
* that begin with `$` are ignored.
|
||||
@@ -648,6 +713,7 @@ function equals(o1, o2) {
|
||||
if (t1 == t2) {
|
||||
if (t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for(key=0; key<length; key++) {
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
@@ -656,8 +722,10 @@ function equals(o1, o2) {
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
return isDate(o2) && o1.getTime() == o2.getTime();
|
||||
} else if (isRegExp(o1) && isRegExp(o2)) {
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
|
||||
keySet = {};
|
||||
for(key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
@@ -695,7 +763,8 @@ function sliceArgs(args, startIndex) {
|
||||
* @description
|
||||
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
|
||||
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
|
||||
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
|
||||
* known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as distinguished
|
||||
* from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
|
||||
*
|
||||
* @param {Object} self Context which `fn` should be evaluated in.
|
||||
* @param {function()} fn Function to be bound.
|
||||
@@ -726,7 +795,7 @@ function bind(self, fn) {
|
||||
function toJsonReplacer(key, value) {
|
||||
var val = value;
|
||||
|
||||
if (/^\$+/.test(key)) {
|
||||
if (typeof key === 'string' && key.charAt(0) === '$') {
|
||||
val = undefined;
|
||||
} else if (isWindow(value)) {
|
||||
val = '$WINDOW';
|
||||
@@ -746,13 +815,15 @@ function toJsonReplacer(key, value) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes input into a JSON-formatted string.
|
||||
* Serializes input into a JSON-formatted string. Properties with leading $ characters will be
|
||||
* stripped since angular uses this notation internally.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
* @returns {string|undefined} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
if (typeof obj === 'undefined') return undefined;
|
||||
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
@@ -812,6 +883,23 @@ function startingTag(element) {
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Tries to decode the URI component without throwing an exception.
|
||||
*
|
||||
* @private
|
||||
* @param str value potential URI component to check.
|
||||
* @returns {boolean} True if `value` can be decoded
|
||||
* with the decodeURIComponent function.
|
||||
*/
|
||||
function tryDecodeURIComponent(value) {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch(e) {
|
||||
// Ignore any invalid uri component
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses an escaped url query string into key-value pairs.
|
||||
* @returns Object.<(string|boolean)>
|
||||
@@ -819,10 +907,12 @@ function startingTag(element) {
|
||||
function parseKeyValue(/**string*/keyValue) {
|
||||
var obj = {}, key_value, key;
|
||||
forEach((keyValue || "").split('&'), function(keyValue){
|
||||
if (keyValue) {
|
||||
if ( keyValue ) {
|
||||
key_value = keyValue.split('=');
|
||||
key = decodeURIComponent(key_value[0]);
|
||||
obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
|
||||
key = tryDecodeURIComponent(key_value[0]);
|
||||
if ( isDefined(key) ) {
|
||||
obj[key] = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
@@ -888,11 +978,15 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
* @description
|
||||
*
|
||||
* Use this directive to auto-bootstrap an application. Only
|
||||
* one directive can be used per HTML document. The directive
|
||||
* one ngApp directive can be used per HTML document. The directive
|
||||
* designates the root of the application and is typically placed
|
||||
* at the root of the page.
|
||||
*
|
||||
* In the example below if the `ngApp` directive would not be placed
|
||||
* The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an
|
||||
* HTML document you must manually bootstrap them using {@link angular.bootstrap}.
|
||||
* Applications cannot be nested.
|
||||
*
|
||||
* In the example below if the `ngApp` directive were not placed
|
||||
* on the `html` element then the document would not be compiled
|
||||
* and the `{{ 1+2 }}` would not be resolved to `3`.
|
||||
*
|
||||
@@ -957,12 +1051,17 @@ function angularInit(element, bootstrap) {
|
||||
*
|
||||
* See: {@link guide/bootstrap Bootstrap}
|
||||
*
|
||||
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
|
||||
* They must use {@link api/ng.directive:ngApp ngApp}.
|
||||
*
|
||||
* @param {Element} element DOM element which is the root of angular application.
|
||||
* @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
|
||||
* @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. See: {@link angular.module modules}
|
||||
* @returns {AUTO.$injector} Returns the newly created injector for this app.
|
||||
*/
|
||||
function bootstrap(element, modules) {
|
||||
var resumeBootstrapInternal = function() {
|
||||
var doBootstrap = function() {
|
||||
element = jqLite(element);
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
@@ -984,7 +1083,7 @@ function bootstrap(element, modules) {
|
||||
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
||||
|
||||
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
||||
return resumeBootstrapInternal();
|
||||
return doBootstrap();
|
||||
}
|
||||
|
||||
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
|
||||
@@ -992,7 +1091,7 @@ function bootstrap(element, modules) {
|
||||
forEach(extraModules, function(module) {
|
||||
modules.push(module);
|
||||
});
|
||||
resumeBootstrapInternal();
|
||||
doBootstrap();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1016,9 +1115,10 @@ function bindJQuery() {
|
||||
injector: JQLitePrototype.injector,
|
||||
inheritedData: JQLitePrototype.inheritedData
|
||||
});
|
||||
JQLitePatchJQueryRemove('remove', true);
|
||||
JQLitePatchJQueryRemove('empty');
|
||||
JQLitePatchJQueryRemove('html');
|
||||
// Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
|
||||
JQLitePatchJQueryRemove('remove', true, true, false);
|
||||
JQLitePatchJQueryRemove('empty', false, false, false);
|
||||
JQLitePatchJQueryRemove('html', false, false, true);
|
||||
} else {
|
||||
jqLite = JQLite;
|
||||
}
|
||||
@@ -1044,3 +1144,30 @@ function assertArgFn(arg, name, acceptArrayAnnotation) {
|
||||
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value accessible from the object by path. Any undefined traversals are ignored
|
||||
* @param {Object} obj starting object
|
||||
* @param {string} path path to traverse
|
||||
* @param {boolean=true} bindFnToScope
|
||||
* @returns value as accessible by path
|
||||
*/
|
||||
//TODO(misko): this function needs to be removed
|
||||
function getter(obj, path, bindFnToScope) {
|
||||
if (!path) return obj;
|
||||
var keys = path.split('.');
|
||||
var key;
|
||||
var lastInstance = obj;
|
||||
var len = keys.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
key = keys[i];
|
||||
if (obj) {
|
||||
obj = (lastInstance = obj)[key];
|
||||
}
|
||||
}
|
||||
if (!bindFnToScope && isFunction(obj)) {
|
||||
return bind(lastInstance, obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ function publishExternalAPI(angular){
|
||||
ngPluralize: ngPluralizeDirective,
|
||||
ngRepeat: ngRepeatDirective,
|
||||
ngShow: ngShowDirective,
|
||||
ngSubmit: ngSubmitDirective,
|
||||
ngStyle: ngStyleDirective,
|
||||
ngSwitch: ngSwitchDirective,
|
||||
ngSwitchWhen: ngSwitchWhenDirective,
|
||||
|
||||
+202
-49
@@ -253,46 +253,37 @@ function annotate(fn) {
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
||||
* The providers share the same name as the instance they create with `Provider` suffixed to them.
|
||||
* The {@link AUTO.$provide $provide} service has a number of methods for registering components with
|
||||
* the {@link AUTO.$injector $injector}. Many of these functions are also exposed on {@link angular.Module}.
|
||||
*
|
||||
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
||||
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
||||
* An Angular **service** is a singleton object created by a **service factory**. These **service
|
||||
* factories** are functions which, in turn, are created by a **service provider**.
|
||||
* The **service providers** are constructor functions. When instantiated they must contain a property
|
||||
* called `$get`, which holds the **service factory** function.
|
||||
*
|
||||
* When you request a service, the {@link AUTO.$injector $injector} is responsible for finding the
|
||||
* correct **service provider**, instantiating it and then calling its `$get` **service factory**
|
||||
* function to get the instance of the **service**.
|
||||
*
|
||||
* Often services have no configuration options and there is no need to add methods to the service
|
||||
* provider. The provider will be no more than a constructor function with a `$get` property. For
|
||||
* these cases the {@link AUTO.$provide $provide} service has additional helper methods to register
|
||||
* services without specifying a provider.
|
||||
*
|
||||
* <pre>
|
||||
* function GreetProvider() {
|
||||
* var salutation = 'Hello';
|
||||
* * {@link AUTO.$provide#provider provider(provider)} - registers a **service provider** with the
|
||||
* {@link AUTO.$injector $injector}
|
||||
* * {@link AUTO.$provide#constant constant(obj)} - registers a value/object that can be accessed by
|
||||
* providers and services.
|
||||
* * {@link AUTO.$provide#value value(obj)} - registers a value/object that can only be accessed by
|
||||
* services, not providers.
|
||||
* * {@link AUTO.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, that
|
||||
* will be wrapped in a **service provider** object, whose `$get` property will contain the given
|
||||
* factory function.
|
||||
* * {@link AUTO.$provide#service service(class)} - registers a **constructor function**, `class` that
|
||||
* will be wrapped in a **service provider** object, whose `$get` property will instantiate a new
|
||||
* object using the given constructor function.
|
||||
*
|
||||
* this.salutation = function(text) {
|
||||
* salutation = text;
|
||||
* };
|
||||
*
|
||||
* this.$get = function() {
|
||||
* return function (name) {
|
||||
* return salutation + ' ' + name + '!';
|
||||
* };
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* describe('Greeter', function(){
|
||||
*
|
||||
* beforeEach(module(function($provide) {
|
||||
* $provide.provider('greet', GreetProvider);
|
||||
* }));
|
||||
*
|
||||
* it('should greet', inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Hello angular!');
|
||||
* }));
|
||||
*
|
||||
* it('should allow configuration of salutation', function() {
|
||||
* module(function(greetProvider) {
|
||||
* greetProvider.salutation('Ahoj');
|
||||
* });
|
||||
* inject(function(greet) {
|
||||
* expect(greet('angular')).toEqual('Ahoj angular!');
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
* See the individual methods for more information and examples.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -301,7 +292,18 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* Register a provider for a service. The providers can be retrieved and can have additional configuration methods.
|
||||
* Register a **provider function** with the {@link AUTO.$injector $injector}. Provider functions are
|
||||
* constructor functions, whose instances are responsible for "providing" a factory for a service.
|
||||
*
|
||||
* Service provider names start with the name of the service they provide followed by `Provider`.
|
||||
* For example, the {@link ng.$log $log} service has a provider called {@link ng.$logProvider $logProvider}.
|
||||
*
|
||||
* Service provider objects can have additional methods which allow configuration of the provider and
|
||||
* its service. Importantly, you can configure what kind of service is created by the `$get` method,
|
||||
* or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a method
|
||||
* {@link ng.$logProvider#debugEnabled debugEnabled}
|
||||
* which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
|
||||
* console or not.
|
||||
*
|
||||
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key.
|
||||
* @param {(Object|function())} provider If the provider is:
|
||||
@@ -312,6 +314,70 @@ function annotate(fn) {
|
||||
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
||||
*
|
||||
* @returns {Object} registered provider instance
|
||||
|
||||
* @example
|
||||
*
|
||||
* The following example shows how to create a simple event tracking service and register it using
|
||||
* {@link AUTO.$provide#provider $provide.provider()}.
|
||||
*
|
||||
* <pre>
|
||||
* // Define the eventTracker provider
|
||||
* function EventTrackerProvider() {
|
||||
* var trackingUrl = '/track';
|
||||
*
|
||||
* // A provider method for configuring where the tracked events should been saved
|
||||
* this.setTrackingUrl = function(url) {
|
||||
* trackingUrl = url;
|
||||
* };
|
||||
*
|
||||
* // The service factory function
|
||||
* this.$get = ['$http', function($http) {
|
||||
* var trackedEvents = {};
|
||||
* return {
|
||||
* // Call this to track an event
|
||||
* event: function(event) {
|
||||
* var count = trackedEvents[event] || 0;
|
||||
* count += 1;
|
||||
* trackedEvents[event] = count;
|
||||
* return count;
|
||||
* },
|
||||
* // Call this to save the tracked events to the trackingUrl
|
||||
* save: function() {
|
||||
* $http.post(trackingUrl, trackedEvents);
|
||||
* }
|
||||
* };
|
||||
* }];
|
||||
* }
|
||||
*
|
||||
* describe('eventTracker', function() {
|
||||
* var postSpy;
|
||||
*
|
||||
* beforeEach(module(function($provide) {
|
||||
* // Register the eventTracker provider
|
||||
* $provide.provider('eventTracker', EventTrackerProvider);
|
||||
* }));
|
||||
*
|
||||
* beforeEach(module(function(eventTrackerProvider) {
|
||||
* // Configure eventTracker provider
|
||||
* eventTrackerProvider.setTrackingUrl('/custom-track');
|
||||
* }));
|
||||
*
|
||||
* it('tracks events', inject(function(eventTracker) {
|
||||
* expect(eventTracker.event('login')).toEqual(1);
|
||||
* expect(eventTracker.event('login')).toEqual(2);
|
||||
* }));
|
||||
*
|
||||
* it('saves to the tracking url', inject(function(eventTracker, $http) {
|
||||
* postSpy = spyOn($http, 'post');
|
||||
* eventTracker.event('login');
|
||||
* eventTracker.save();
|
||||
* expect(postSpy).toHaveBeenCalled();
|
||||
* expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
|
||||
* expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
|
||||
* expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
|
||||
* }));
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -320,12 +386,32 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* A short hand for configuring services if only `$get` method is required.
|
||||
* Register a **service factory**, which will be called to return the service instance.
|
||||
* This is short for registering a service where its provider consists of only a `$get` property,
|
||||
* which is the given service factory function.
|
||||
* You should use {@link AUTO.$provide#factory $provide.factor(getFn)} if you do not need to configure
|
||||
* your service in a provider.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for
|
||||
* `$provide.provider(name, {$get: $getFn})`.
|
||||
* @returns {Object} registered provider instance
|
||||
*
|
||||
* @example
|
||||
* Here is an example of registering a service
|
||||
* <pre>
|
||||
* $provide.factory('ping', ['$http', function($http) {
|
||||
* return function ping() {
|
||||
* return $http.send('/ping');
|
||||
* };
|
||||
* }]);
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping();
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -335,11 +421,34 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* A short hand for registering service of given class.
|
||||
* Register a **service constructor**, which will be invoked with `new` to create the service instance.
|
||||
* This is short for registering a service where its provider's `$get` property is the service
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
*
|
||||
* You should use {@link AUTO.$provide#service $provide.service(class)} if you define your service
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
* @returns {Object} registered provider instance
|
||||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using {@link AUTO.$provide#service $provide.service(class)}
|
||||
* that is defined as a CoffeeScript class.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http)->
|
||||
* send: ()=>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping)->
|
||||
* ping.send()
|
||||
* ]
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -349,11 +458,29 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* A short hand for configuring services if the `$get` method is a constant.
|
||||
* Register a **value service** with the {@link AUTO.$injector $injector}, such as a string, a number,
|
||||
* an array, an object or a function. This is short for registering a service where its provider's
|
||||
* `$get` property is a factory function that takes no arguments and returns the **value service**.
|
||||
*
|
||||
* Value services are similar to constant services, except that they cannot be injected into a module
|
||||
* configuration function (see {@link angular.Module#config}) but they can be overridden by an Angular
|
||||
* {@link AUTO.$provide#decorator decorator}.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {*} value The value.
|
||||
* @returns {Object} registered provider instance
|
||||
*
|
||||
* @example
|
||||
* Here are some examples of creating value services.
|
||||
* <pre>
|
||||
* $provide.constant('ADMIN_USER', 'admin');
|
||||
*
|
||||
* $provide.constant('RoleLookup', { admin: 0, writer: 1, reader: 2 });
|
||||
*
|
||||
* $provide.constant('halfOf', function(value) {
|
||||
* return value / 2;
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -363,13 +490,26 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* A constant value, but unlike {@link AUTO.$provide#value value} it can be injected
|
||||
* into configuration function (other modules) and it is not interceptable by
|
||||
* {@link AUTO.$provide#decorator decorator}.
|
||||
* Register a **constant service**, such as a string, a number, an array, an object or a function, with
|
||||
* the {@link AUTO.$injector $injector}. Unlike {@link AUTO.$provide#value value} it can be injected
|
||||
* into a module configuration function (see {@link angular.Module#config}) and it cannot be
|
||||
* overridden by an Angular {@link AUTO.$provide#decorator decorator}.
|
||||
*
|
||||
* @param {string} name The name of the constant.
|
||||
* @param {*} value The constant value.
|
||||
* @returns {Object} registered instance
|
||||
*
|
||||
* @example
|
||||
* Here a some examples of creating constants:
|
||||
* <pre>
|
||||
* $provide.constant('SHARD_HEIGHT', 306);
|
||||
*
|
||||
* $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
|
||||
*
|
||||
* $provide.constant('double', function(value) {
|
||||
* return value * 2;
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -379,17 +519,29 @@ function annotate(fn) {
|
||||
* @methodOf AUTO.$provide
|
||||
* @description
|
||||
*
|
||||
* Decoration of service, allows the decorator to intercept the service instance creation. The
|
||||
* returned instance may be the original instance, or a new instance which delegates to the
|
||||
* original instance.
|
||||
* Register a **service decorator** with the {@link AUTO.$injector $injector}. A service decorator
|
||||
* intercepts the creation of a service, allowing it to override or modify the behaviour of the
|
||||
* service. The object returned by the decorator may be the original service, or a new service object
|
||||
* which replaces or wraps and delegates to the original service.
|
||||
*
|
||||
* @param {string} name The name of the service to decorate.
|
||||
* @param {function()} decorator This function will be invoked when the service needs to be
|
||||
* instantiated. The function is called using the {@link AUTO.$injector#invoke
|
||||
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
|
||||
* instantiated and should return the decorated service instance. The function is called using
|
||||
* the {@link AUTO.$injector#invoke injector.invoke} method and is therefore fully injectable.
|
||||
* Local injection arguments:
|
||||
*
|
||||
* * `$delegate` - The original service instance, which can be monkey patched, configured,
|
||||
* decorated or delegated to.
|
||||
*
|
||||
* @example
|
||||
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
|
||||
* calls to {@link ng.$log#error $log.warn()}.
|
||||
* <pre>
|
||||
* $provider.decorator('$log', ['$delegate', function($delegate) {
|
||||
* $delegate.warn = $delegate.error;
|
||||
* return $delegate;
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -602,3 +754,4 @@ function createInjector(modulesToLoad) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+13
-3
@@ -183,7 +183,9 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
var modules = [];
|
||||
var modules = [],
|
||||
embedRootScope,
|
||||
deregisterEmbedRootScope;
|
||||
|
||||
modules.push(['$provide', function($provide) {
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
@@ -209,10 +211,12 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
}
|
||||
}, $delegate);
|
||||
}]);
|
||||
$provide.decorator('$rootScope', ['$delegate', function(embedRootScope) {
|
||||
docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
embedRootScope.$digest();
|
||||
});
|
||||
|
||||
return embedRootScope;
|
||||
}]);
|
||||
}]);
|
||||
@@ -223,6 +227,12 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('$destroy', function() {
|
||||
deregisterEmbedRootScope();
|
||||
embedRootScope.$destroy();
|
||||
});
|
||||
|
||||
angular.bootstrap(element, modules);
|
||||
}
|
||||
};
|
||||
|
||||
+40
-28
@@ -26,7 +26,8 @@
|
||||
* Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
|
||||
* raw DOM references.
|
||||
*
|
||||
* ## Angular's jQuery lite provides the following methods:
|
||||
* ## Angular's jqLite
|
||||
* Angular's lite version of jQuery provides only the following jQuery methods:
|
||||
*
|
||||
* - [addClass()](http://api.jquery.com/addClass/)
|
||||
* - [after()](http://api.jquery.com/after/)
|
||||
@@ -59,8 +60,14 @@
|
||||
* - [val()](http://api.jquery.com/val/)
|
||||
* - [wrap()](http://api.jquery.com/wrap/)
|
||||
*
|
||||
* ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
|
||||
* ## jQuery/jqLite Extras
|
||||
* Angular also provides the following additional methods and events to both jQuery and jqLite:
|
||||
*
|
||||
* ### Events
|
||||
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
|
||||
* on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
|
||||
* element before it is removed.
|
||||
* ### Methods
|
||||
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
|
||||
* retrieves controller associated with the `ngController` directive. If `name` is provided as
|
||||
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
|
||||
@@ -107,37 +114,38 @@ function camelCase(name) {
|
||||
/////////////////////////////////////////////
|
||||
// jQuery mutation patch
|
||||
//
|
||||
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
|
||||
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
|
||||
// $destroy event on all DOM nodes being removed.
|
||||
//
|
||||
/////////////////////////////////////////////
|
||||
|
||||
function JQLitePatchJQueryRemove(name, dispatchThis) {
|
||||
function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
|
||||
var originalJqFn = jQuery.fn[name];
|
||||
originalJqFn = originalJqFn.$original || originalJqFn;
|
||||
removePatch.$original = originalJqFn;
|
||||
jQuery.fn[name] = removePatch;
|
||||
|
||||
function removePatch() {
|
||||
var list = [this],
|
||||
function removePatch(param) {
|
||||
var list = filterElems && param ? [this.filter(param)] : [this],
|
||||
fireEvent = dispatchThis,
|
||||
set, setIndex, setLength,
|
||||
element, childIndex, childLength, children,
|
||||
fns, events;
|
||||
element, childIndex, childLength, children;
|
||||
|
||||
while(list.length) {
|
||||
set = list.shift();
|
||||
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
|
||||
element = jqLite(set[setIndex]);
|
||||
if (fireEvent) {
|
||||
element.triggerHandler('$destroy');
|
||||
} else {
|
||||
fireEvent = !fireEvent;
|
||||
}
|
||||
for(childIndex = 0, childLength = (children = element.children()).length;
|
||||
childIndex < childLength;
|
||||
childIndex++) {
|
||||
list.push(jQuery(children[childIndex]));
|
||||
if (!getterIfNoArguments || param != null) {
|
||||
while(list.length) {
|
||||
set = list.shift();
|
||||
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
|
||||
element = jqLite(set[setIndex]);
|
||||
if (fireEvent) {
|
||||
element.triggerHandler('$destroy');
|
||||
} else {
|
||||
fireEvent = !fireEvent;
|
||||
}
|
||||
for(childIndex = 0, childLength = (children = element.children()).length;
|
||||
childIndex < childLength;
|
||||
childIndex++) {
|
||||
list.push(jQuery(children[childIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +205,7 @@ function JQLiteUnbind(element, type, fn) {
|
||||
removeEventListenerFn(element, type, events[type]);
|
||||
delete events[type];
|
||||
} else {
|
||||
arrayRemove(events[type], fn);
|
||||
arrayRemove(events[type] || [], fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,6 +479,15 @@ forEach({
|
||||
|
||||
val: function(element, value) {
|
||||
if (isUndefined(value)) {
|
||||
if (nodeName_(element) === 'SELECT' && element.multiple) {
|
||||
var result = [];
|
||||
forEach(element.options, function (option) {
|
||||
if (option.selected) {
|
||||
result.push(option.value || option.text);
|
||||
}
|
||||
});
|
||||
return result.length === 0 ? null : result;
|
||||
}
|
||||
return element.value;
|
||||
}
|
||||
element.value = value;
|
||||
@@ -688,12 +705,7 @@ forEach({
|
||||
if (element.nodeType === 1) {
|
||||
var index = element.firstChild;
|
||||
forEach(new JQLite(node), function(child){
|
||||
if (index) {
|
||||
element.insertBefore(child, index);
|
||||
} else {
|
||||
element.appendChild(child);
|
||||
index = child;
|
||||
}
|
||||
element.insertBefore(child, index);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
+6
-4
@@ -30,8 +30,8 @@ function setupModuleLoader(window) {
|
||||
*
|
||||
* # Module
|
||||
*
|
||||
* A module is a collocation of services, directives, filters, and configuration information. Module
|
||||
* is used to configure the {@link AUTO.$injector $injector}.
|
||||
* A module is a collection of services, directives, filters, and configuration information.
|
||||
* `angular.module` is used to configure the {@link AUTO.$injector $injector}.
|
||||
*
|
||||
* <pre>
|
||||
* // Create a new module
|
||||
@@ -178,7 +178,8 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc method
|
||||
* @name angular.Module#controller
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Controller name.
|
||||
* @param {string|Object} name Controller name, or an object map of controllers where the
|
||||
* keys are the names and the values are the constructors.
|
||||
* @param {Function} constructor Controller constructor function.
|
||||
* @description
|
||||
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
|
||||
@@ -189,7 +190,8 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc method
|
||||
* @name angular.Module#directive
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name directive name
|
||||
* @param {string|Object} name Directive name, or an object map of directives where the
|
||||
* keys are the names and the values are the factories.
|
||||
* @param {Function} directiveFactory Factory function for creating new instance of
|
||||
* directives.
|
||||
* @description
|
||||
|
||||
+29
-1
@@ -10,8 +10,36 @@
|
||||
* according to rules specified in
|
||||
* {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}.
|
||||
*
|
||||
* It also watches the `$location.hash()` and scroll whenever it changes to match any anchor.
|
||||
* It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
|
||||
* This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCtrl">
|
||||
<a ng-click="gotoBottom()">Go to bottom</a>
|
||||
<a id="bottom"></a> You're at the bottom!
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
function ScrollCtrl($scope, $location, $anchorScroll) {
|
||||
$scope.gotoBottom = function (){
|
||||
// set the location.hash to the id of
|
||||
// the element you wish to scroll to.
|
||||
$location.hash('bottom');
|
||||
|
||||
// call $anchorScroll()
|
||||
$anchorScroll();
|
||||
}
|
||||
}
|
||||
</file>
|
||||
<file name="style.css">
|
||||
#bottom {
|
||||
display: block;
|
||||
margin-top: 2000px;
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $AnchorScrollProvider() {
|
||||
|
||||
|
||||
+18
-6
@@ -124,7 +124,8 @@ function Browser(window, document, $log, $sniffer) {
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
var lastBrowserUrl = location.href,
|
||||
baseElement = document.find('base');
|
||||
baseElement = document.find('base'),
|
||||
replacedUrl = null;
|
||||
|
||||
/**
|
||||
* @name ng.$browser#url
|
||||
@@ -159,14 +160,21 @@ function Browser(window, document, $log, $sniffer) {
|
||||
baseElement.attr('href', baseElement.attr('href'));
|
||||
}
|
||||
} else {
|
||||
if (replace) location.replace(url);
|
||||
else location.href = url;
|
||||
if (replace) {
|
||||
location.replace(url);
|
||||
replacedUrl = url;
|
||||
} else {
|
||||
location.href = url;
|
||||
replacedUrl = null;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
// getter
|
||||
} else {
|
||||
// the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
|
||||
return location.href.replace(/%27/g,"'");
|
||||
// - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
|
||||
// location.href synchronously
|
||||
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
|
||||
return replacedUrl || location.href.replace(/%27/g,"'");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -230,10 +238,14 @@ function Browser(window, document, $log, $sniffer) {
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @name ng.$browser#baseHref
|
||||
* @methodOf ng.$browser
|
||||
*
|
||||
* @description
|
||||
* Returns current <base href>
|
||||
* (always relative - without domain)
|
||||
*
|
||||
* @returns {string=}
|
||||
* @returns {string=} current <base href>
|
||||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
|
||||
+73
-3
@@ -3,7 +3,20 @@
|
||||
* @name ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Factory that constructs cache objects.
|
||||
* Factory that constructs cache objects and gives access to them.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* var cache = $cacheFactory('cacheId');
|
||||
* expect($cacheFactory.get('cacheId')).toBe(cache);
|
||||
* expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
|
||||
*
|
||||
* cache.put("key", "value");
|
||||
* cache.put("another key", "another value");
|
||||
*
|
||||
* expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param {string} cacheId Name or id of the newly created cache.
|
||||
@@ -135,6 +148,16 @@ function $CacheFactoryProvider() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ng.$cacheFactory#info
|
||||
* @methodOf ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Get information about all the of the caches that have been created
|
||||
*
|
||||
* @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
|
||||
*/
|
||||
cacheFactory.info = function() {
|
||||
var info = {};
|
||||
forEach(caches, function(cache, cacheId) {
|
||||
@@ -144,6 +167,17 @@ function $CacheFactoryProvider() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ng.$cacheFactory#get
|
||||
* @methodOf ng.$cacheFactory
|
||||
*
|
||||
* @description
|
||||
* Get access to a cache object by the `cacheId` used when it was created.
|
||||
*
|
||||
* @param {string} cacheId Name or id of a cache to access.
|
||||
* @returns {object} Cache object identified by the cacheId or undefined if no such cache.
|
||||
*/
|
||||
cacheFactory.get = function(cacheId) {
|
||||
return caches[cacheId];
|
||||
};
|
||||
@@ -158,8 +192,44 @@ function $CacheFactoryProvider() {
|
||||
* @name ng.$templateCache
|
||||
*
|
||||
* @description
|
||||
* Cache used for storing html templates.
|
||||
*
|
||||
* The first time a template is used, it is loaded in the template cache for quick retrieval. You can
|
||||
* load templates directly into the cache in a `script` tag, or by consuming the `$templateCache`
|
||||
* service directly.
|
||||
*
|
||||
* Adding via the `script` tag:
|
||||
* <pre>
|
||||
* <html ng-app>
|
||||
* <head>
|
||||
* <script type="text/ng-template" id="templateId.html">
|
||||
* This is the content of the template
|
||||
* </script>
|
||||
* </head>
|
||||
* ...
|
||||
* </html>
|
||||
* </pre>
|
||||
*
|
||||
* **Note:** the `script` tag containing the template does not need to be included in the `head` of the document, but
|
||||
* it must be below the `ng-app` definition.
|
||||
*
|
||||
* Adding via the $templateCache service:
|
||||
*
|
||||
* <pre>
|
||||
* var myApp = angular.module('myApp', []);
|
||||
* myApp.run(function($templateCache) {
|
||||
* $templateCache.put('templateId.html', 'This is the content of the template');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* To retrieve the template later, simply use it in your HTML:
|
||||
* <pre>
|
||||
* <div ng-include=" 'templateId.html' "></div>
|
||||
* </pre>
|
||||
*
|
||||
* or get it via Javascript:
|
||||
* <pre>
|
||||
* $templateCache.get('templateId.html')
|
||||
* </pre>
|
||||
*
|
||||
* See {@link ng.$cacheFactory $cacheFactory}.
|
||||
*
|
||||
*/
|
||||
|
||||
+19
-9
@@ -166,12 +166,13 @@ function $CompileProvider($provide) {
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Register a new directives with the compiler.
|
||||
* Register a new directive with the compiler.
|
||||
*
|
||||
* @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as
|
||||
* <code>ng-bind</code>).
|
||||
* @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
|
||||
* info.
|
||||
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
|
||||
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
|
||||
* names and the values are the factories.
|
||||
* @param {function|Array} directiveFactory An injectable directive factory function. See
|
||||
* {@link guide/directive} for more info.
|
||||
* @returns {ng.$compileProvider} Self for chaining.
|
||||
*/
|
||||
this.directive = function registerDirective(name, directiveFactory) {
|
||||
@@ -293,7 +294,7 @@ function $CompileProvider($provide) {
|
||||
|
||||
// href property always returns normalized absolute url, so we can match against that
|
||||
normalizedVal = urlSanitizationNode.href;
|
||||
if (!normalizedVal.match(urlSanitizationWhitelist)) {
|
||||
if (normalizedVal !== '' && !normalizedVal.match(urlSanitizationWhitelist)) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
@@ -319,12 +320,21 @@ function $CompileProvider($provide) {
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$compile.directive.Attributes#$observe
|
||||
* @methodOf ng.$compile.directive.Attributes
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Observe an interpolated attribute.
|
||||
* The observer will never be called, if given attribute is not interpolated.
|
||||
* The interpolated value of the attribute is passed to the observer function.
|
||||
*
|
||||
* @param {string} key Normalized key. (ie ngAttribute) .
|
||||
* @param {function(*)} fn Function that will be called whenever the attribute value changes.
|
||||
* @returns {function(*)} the `fn` Function passed in.
|
||||
* @param {function(interpolatedValue)} fn Function that will be called whenever
|
||||
the interpolated value of the attribute changes.
|
||||
* See the {@link guide/directive#Attributes Directives} guide for more info.
|
||||
* @returns {function()} the `fn` parameter.
|
||||
*/
|
||||
$observe: function(key, fn) {
|
||||
var attrs = this,
|
||||
@@ -517,7 +527,7 @@ function $CompileProvider($provide) {
|
||||
for (var attr, name, nName, value, nAttrs = node.attributes,
|
||||
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
|
||||
attr = nAttrs[j];
|
||||
if (attr.specified) {
|
||||
if (!msie || msie >= 8 || attr.specified) {
|
||||
name = attr.name;
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
|
||||
@@ -18,7 +18,8 @@ function $ControllerProvider() {
|
||||
* @ngdoc function
|
||||
* @name ng.$controllerProvider#register
|
||||
* @methodOf ng.$controllerProvider
|
||||
* @param {string} name Controller name
|
||||
* @param {string|Object} name Controller name, or an object map of controllers where the keys are
|
||||
* the names and the values are the constructors.
|
||||
* @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
|
||||
* annotations in the array notation).
|
||||
*/
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
* @restrict E
|
||||
*
|
||||
* @description
|
||||
* Modifies the default behavior of html A tag, so that the default action is prevented when href
|
||||
* attribute is empty.
|
||||
* Modifies the default behavior of the html A tag so that the default action is prevented when
|
||||
* the href attribute is empty.
|
||||
*
|
||||
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
|
||||
* This change permits the easy creation of action links with the `ngClick` directive
|
||||
* without changing the location or causing page reloads, e.g.:
|
||||
* `<a href="" ng-click="model.$save()">Save</a>`
|
||||
*/
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like {{hash}} in an href attribute makes
|
||||
* the page open to a wrong URL, if the user clicks that link before
|
||||
* angular has a chance to replace the {{hash}} with actual URL, the
|
||||
* link will be broken and will most likely return a 404 error.
|
||||
* Using Angular markup like `{{hash}}` in an href attribute will
|
||||
* make the link go to the wrong URL if the user clicks it before
|
||||
* Angular has a chance to replace the `{{hash}}` markup with its
|
||||
* value. Until Angular replaces the markup the link will be broken
|
||||
* and will most likely return a 404 error.
|
||||
*
|
||||
* The `ngHref` directive solves this problem.
|
||||
*
|
||||
* The buggy way to write it:
|
||||
* The wrong way to write it:
|
||||
* <pre>
|
||||
* <a href="http://www.gravatar.com/avatar/{{hash}}"/>
|
||||
* </pre>
|
||||
@@ -26,7 +28,8 @@
|
||||
* @param {template} ngHref any string which can contain `{{}}` markup.
|
||||
*
|
||||
* @example
|
||||
* This example uses `link` variable inside `href` attribute:
|
||||
* This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
|
||||
* in links and their different behaviors:
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<input ng-model="value" /><br />
|
||||
@@ -119,9 +122,9 @@
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as disabled.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* The HTML specs do not require browsers to preserve the values of special attributes
|
||||
* such as disabled. (The presence of them means true and absence means false)
|
||||
* This prevents the Angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce the `ngDisabled` directive.
|
||||
*
|
||||
* @example
|
||||
@@ -140,7 +143,8 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {expression} ngDisabled Angular expression that will be evaluated.
|
||||
* @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "disabled" will be set on the element.
|
||||
*/
|
||||
|
||||
|
||||
@@ -170,7 +174,8 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {expression} ngChecked Angular expression that will be evaluated.
|
||||
* @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "checked" will be set on the element.
|
||||
*/
|
||||
|
||||
|
||||
@@ -206,7 +211,8 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element SELECT
|
||||
* @param {expression} ngMultiple Angular expression that will be evaluated.
|
||||
* @param {expression} ngMultiple If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "multiple" will be set on the element.
|
||||
*/
|
||||
|
||||
|
||||
@@ -236,7 +242,8 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
* @param {string} expression If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "readonly" will be set on the element.
|
||||
*/
|
||||
|
||||
|
||||
@@ -269,7 +276,8 @@
|
||||
</doc:example>
|
||||
*
|
||||
* @element OPTION
|
||||
* @param {string} expression Angular expression that will be evaluated.
|
||||
* @param {string} expression If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "selected" will be set on the element.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+56
-10
@@ -40,7 +40,7 @@ function FormController(element, attrs) {
|
||||
errors = form.$error = {};
|
||||
|
||||
// init state
|
||||
form.$name = attrs.name;
|
||||
form.$name = attrs.name || attrs.ngForm;
|
||||
form.$dirty = false;
|
||||
form.$pristine = true;
|
||||
form.$valid = true;
|
||||
@@ -60,12 +60,32 @@ function FormController(element, attrs) {
|
||||
addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:form.FormController#$addControl
|
||||
* @methodOf ng.directive:form.FormController
|
||||
*
|
||||
* @description
|
||||
* Register a control with the form.
|
||||
*
|
||||
* Input elements using ngModelController do this automatically when they are linked.
|
||||
*/
|
||||
form.$addControl = function(control) {
|
||||
if (control.$name && !form.hasOwnProperty(control.$name)) {
|
||||
form[control.$name] = control;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:form.FormController#$removeControl
|
||||
* @methodOf ng.directive:form.FormController
|
||||
*
|
||||
* @description
|
||||
* Deregister a control from the form.
|
||||
*
|
||||
* Input elements using ngModelController do this automatically when they are destroyed.
|
||||
*/
|
||||
form.$removeControl = function(control) {
|
||||
if (control.$name && form[control.$name] === control) {
|
||||
delete form[control.$name];
|
||||
@@ -75,6 +95,16 @@ function FormController(element, attrs) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:form.FormController#$setValidity
|
||||
* @methodOf ng.directive:form.FormController
|
||||
*
|
||||
* @description
|
||||
* Sets the validity of a form control.
|
||||
*
|
||||
* This method will also propagate to parent forms.
|
||||
*/
|
||||
form.$setValidity = function(validationToken, isValid, control) {
|
||||
var queue = errors[validationToken];
|
||||
|
||||
@@ -113,6 +143,17 @@ function FormController(element, attrs) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.directive:form.FormController#$setDirty
|
||||
* @methodOf ng.directive:form.FormController
|
||||
*
|
||||
* @description
|
||||
* Sets the form to a dirty state.
|
||||
*
|
||||
* This method can be called to add the 'ng-dirty' class and set the form to a dirty
|
||||
* state (ng-dirty class). This method will also propagate to parent forms.
|
||||
*/
|
||||
form.$setDirty = function() {
|
||||
element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS);
|
||||
form.$dirty = true;
|
||||
@@ -147,15 +188,19 @@ function FormController(element, attrs) {
|
||||
* Directive that instantiates
|
||||
* {@link ng.directive:form.FormController FormController}.
|
||||
*
|
||||
* If `name` attribute is specified, the form controller is published onto the current scope under
|
||||
* If the `name` attribute is specified, the form controller is published onto the current scope under
|
||||
* this name.
|
||||
*
|
||||
* # Alias: {@link ng.directive:ngForm `ngForm`}
|
||||
*
|
||||
* In angular forms can be nested. This means that the outer form is valid when all of the child
|
||||
* forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this
|
||||
* reason angular provides {@link ng.directive:ngForm `ngForm`} alias
|
||||
* which behaves identical to `<form>` but allows form nesting.
|
||||
* In Angular forms can be nested. This means that the outer form is valid when all of the child
|
||||
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
|
||||
* Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
|
||||
* `<form>` but can be nested. This allows you to have nested forms, which is very useful when
|
||||
* using Angular validation directives in forms that are dynamically generated using the
|
||||
* {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
|
||||
* attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
|
||||
* `ngForm` directive and nest these in an outer `form` element.
|
||||
*
|
||||
*
|
||||
* # CSS classes
|
||||
@@ -165,12 +210,12 @@ function FormController(element, attrs) {
|
||||
* - `ng-dirty` Is set if the form is dirty.
|
||||
*
|
||||
*
|
||||
* # Submitting a form and preventing default action
|
||||
* # Submitting a form and preventing the default action
|
||||
*
|
||||
* Since the role of forms in client-side Angular applications is different than in classical
|
||||
* roundtrip apps, it is desirable for the browser not to translate the form submission into a full
|
||||
* page reload that sends the data to the server. Instead some javascript logic should be triggered
|
||||
* to handle the form submission in application specific way.
|
||||
* to handle the form submission in an application-specific way.
|
||||
*
|
||||
* For this reason, Angular prevents the default action (form submission to the server) unless the
|
||||
* `<form>` element has an `action` attribute specified.
|
||||
@@ -182,8 +227,9 @@ function FormController(element, attrs) {
|
||||
* - {@link ng.directive:ngClick ngClick} directive on the first
|
||||
* button or input field of type submit (input[type=submit])
|
||||
*
|
||||
* To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This
|
||||
* is because of the following form submission rules coming from the html spec:
|
||||
* To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
|
||||
* or {@link ng.directive:ngClick ngClick} directives.
|
||||
* This is because of the following form submission rules in the HTML specification:
|
||||
*
|
||||
* - If a form has only one input field then hitting enter in this field triggers form submit
|
||||
* (`ngSubmit`)
|
||||
|
||||
+148
-28
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
||||
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
|
||||
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
|
||||
var inputType = {
|
||||
@@ -112,9 +112,9 @@ var inputType = {
|
||||
<form name="myForm" ng-controller="Ctrl">
|
||||
Number: <input type="number" name="input" ng-model="value"
|
||||
min="0" max="99" required>
|
||||
<span class="error" ng-show="myForm.list.$error.required">
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.list.$error.number">
|
||||
<span class="error" ng-show="myForm.input.$error.number">
|
||||
Not valid number!</span>
|
||||
<tt>value = {{value}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
@@ -235,6 +235,8 @@ var inputType = {
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
|
||||
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
|
||||
* patterns defined as scope expressions.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -793,12 +795,25 @@ var VALID_CLASS = 'ng-valid',
|
||||
*
|
||||
* @property {string} $viewValue Actual string value in the view.
|
||||
* @property {*} $modelValue The value in the model, that the control is bound to.
|
||||
* @property {Array.<Function>} $parsers Whenever the control reads value from the DOM, it executes
|
||||
* all of these functions to sanitize / convert the value as well as validate.
|
||||
*
|
||||
* @property {Array.<Function>} $formatters Whenever the model value changes, it executes all of
|
||||
* these functions to convert the value as well as validate.
|
||||
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
|
||||
the control reads value from the DOM. Each function is called, in turn, passing the value
|
||||
through to the next. Used to sanitize / convert the value as well as validation.
|
||||
|
||||
For validation, the parsers should update the validity state using
|
||||
{@link ng.directive:ngModel.NgModelController#$setValidity $setValidity()},
|
||||
and return `undefined` for invalid values.
|
||||
*
|
||||
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
|
||||
* the model value changes. Each function is called, in turn, passing the value through to the
|
||||
* next. Used to format / convert values for display in the control and validation.
|
||||
* <pre>
|
||||
* function formatter(value) {
|
||||
* if (value) {
|
||||
* return value.toUpperCase();
|
||||
* }
|
||||
* }
|
||||
* ngModel.$formatters.push(formatter);
|
||||
* </pre>
|
||||
* @property {Object} $error An bject hash with all errors as keys.
|
||||
*
|
||||
* @property {boolean} $pristine True if user has not interacted with the control yet.
|
||||
@@ -809,15 +824,19 @@ var VALID_CLASS = 'ng-valid',
|
||||
* @description
|
||||
*
|
||||
* `NgModelController` provides API for the `ng-model` directive. The controller contains
|
||||
* services for data-binding, validation, CSS update, value formatting and parsing. It
|
||||
* specifically does not contain any logic which deals with DOM rendering or listening to
|
||||
* DOM events. The `NgModelController` is meant to be extended by other directives where, the
|
||||
* directive provides DOM manipulation and the `NgModelController` provides the data-binding.
|
||||
* services for data-binding, validation, CSS updates, and value formatting and parsing. It
|
||||
* purposefully does not contain any logic which deals with DOM rendering or listening to
|
||||
* DOM events. Such DOM related logic should be provided by other directives which make use of
|
||||
* `NgModelController` for data-binding.
|
||||
*
|
||||
* ## Custom Control Example
|
||||
* This example shows how to use `NgModelController` with a custom control to achieve
|
||||
* data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
|
||||
* collaborate together to achieve the desired result.
|
||||
*
|
||||
* Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
|
||||
* contents be edited in place by the user. This will not work on older browsers.
|
||||
*
|
||||
* <example module="customControl">
|
||||
<file name="style.css">
|
||||
[contenteditable] {
|
||||
@@ -853,7 +872,13 @@ var VALID_CLASS = 'ng-valid',
|
||||
|
||||
// Write data to the model
|
||||
function read() {
|
||||
ngModel.$setViewValue(element.html());
|
||||
var html = element.html();
|
||||
// When we clear the content editable the browser leaves a <br> behind
|
||||
// If strip-br attribute is provided then we strip this out
|
||||
if( attrs.stripBr && html == '<br>' ) {
|
||||
html = '';
|
||||
}
|
||||
ngModel.$setViewValue(html);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -863,6 +888,7 @@ var VALID_CLASS = 'ng-valid',
|
||||
<form name="myForm">
|
||||
<div contenteditable
|
||||
name="myWidget" ng-model="userContent"
|
||||
strip-br="true"
|
||||
required>Change me!</div>
|
||||
<span ng-show="myForm.myWidget.$error.required">Required!</span>
|
||||
<hr>
|
||||
@@ -881,6 +907,39 @@ var VALID_CLASS = 'ng-valid',
|
||||
</file>
|
||||
* </example>
|
||||
*
|
||||
* ## Isolated Scope Pitfall
|
||||
*
|
||||
* Note that if you have a directive with an isolated scope, you cannot require `ngModel`
|
||||
* since the model value will be looked up on the isolated scope rather than the outer scope.
|
||||
* When the directive updates the model value, calling `ngModel.$setViewValue()` the property
|
||||
* on the outer scope will not be updated.
|
||||
*
|
||||
* Here is an example of this situation. You'll notice that even though both 'input' and 'div'
|
||||
* seem to be attached to the same model, they are not kept in synch.
|
||||
*
|
||||
* <example module="badIsolatedDirective">
|
||||
<file name="script.js">
|
||||
angular.module('badIsolatedDirective', []).directive('bad', function() {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
scope: { },
|
||||
template: '<input ng-model="innerModel">',
|
||||
link: function(scope, element, attrs, ngModel) {
|
||||
scope.$watch('innerModel', function(value) {
|
||||
console.log(value);
|
||||
ngModel.$setViewValue(value);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<input ng-model="someModel">
|
||||
<div bad ng-model="someModel"></div>
|
||||
</file>
|
||||
* </example>
|
||||
*
|
||||
*
|
||||
*/
|
||||
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse',
|
||||
function($scope, $exceptionHandler, $attr, $element, $parse) {
|
||||
@@ -985,8 +1044,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* For example {@link ng.directive:input input} or
|
||||
* {@link ng.directive:select select} directives call it.
|
||||
*
|
||||
* It internally calls all `parsers` and if resulted value is valid, updates the model and
|
||||
* calls all registered change listeners.
|
||||
* It internally calls all `$parsers` (including validators) and updates the `$modelValue` and the actual model path.
|
||||
* Lastly it calls all registered change listeners.
|
||||
*
|
||||
* @param {string} value Value from the view.
|
||||
*/
|
||||
@@ -1051,17 +1110,26 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* @element input
|
||||
*
|
||||
* @description
|
||||
* Is directive that tells Angular to do two-way data binding. It works together with `input`,
|
||||
* `select`, `textarea`. You can easily write your own directives to use `ngModel` as well.
|
||||
* The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
|
||||
* property on the scope using {@link ng.directive:ngModel.NgModelController NgModelController},
|
||||
* which is created and exposed by this directive.
|
||||
*
|
||||
* `ngModel` is responsible for:
|
||||
*
|
||||
* - binding the view into the model, which other directives such as `input`, `textarea` or `select`
|
||||
* require,
|
||||
* - providing validation behavior (i.e. required, number, email, url),
|
||||
* - keeping state of the control (valid/invalid, dirty/pristine, validation errors),
|
||||
* - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`),
|
||||
* - register the control with parent {@link ng.directive:form form}.
|
||||
* - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
|
||||
* require.
|
||||
* - Providing validation behavior (i.e. required, number, email, url).
|
||||
* - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors).
|
||||
* - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`).
|
||||
* - Registering the control with its parent {@link ng.directive:form form}.
|
||||
*
|
||||
* Note: `ngModel` will try to bind to the property given by evaluating the expression on the
|
||||
* current scope. If the property doesn't already exist on this scope, it will be created
|
||||
* implicitly and added to the scope.
|
||||
*
|
||||
* For best practices on using `ngModel`, see:
|
||||
*
|
||||
* - {@link https://github.com/angular/angular.js/wiki/Understanding-Scopes}
|
||||
*
|
||||
* For basic examples, how to use `ngModel`, see:
|
||||
*
|
||||
@@ -1203,8 +1271,9 @@ var requiredDirective = function() {
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl">
|
||||
List: <input name="namesInput" ng-model="names" ng-list required>
|
||||
<span class="error" ng-show="myForm.list.$error.required">
|
||||
<span class="error" ng-show="myForm.namesInput.$error.required">
|
||||
Required!</span>
|
||||
<br>
|
||||
<tt>names = {{names}}</tt><br/>
|
||||
<tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
|
||||
<tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
|
||||
@@ -1216,12 +1285,14 @@ var requiredDirective = function() {
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('names')).toEqual('["igor","misko","vojta"]');
|
||||
expect(binding('myForm.namesInput.$valid')).toEqual('true');
|
||||
expect(element('span.error').css('display')).toBe('none');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('names').enter('');
|
||||
expect(binding('names')).toEqual('[]');
|
||||
expect(binding('myForm.namesInput.$valid')).toEqual('false');
|
||||
expect(element('span.error').css('display')).not().toBe('none');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -1259,19 +1330,68 @@ var ngListDirective = function() {
|
||||
|
||||
|
||||
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngValue
|
||||
*
|
||||
* @description
|
||||
* Binds the given expression to the value of `input[select]` or `input[radio]`, so
|
||||
* that when the element is selected, the `ngModel` of that element is set to the
|
||||
* bound value.
|
||||
*
|
||||
* `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as
|
||||
* shown below.
|
||||
*
|
||||
* @element input
|
||||
* @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
|
||||
* of the `input` element
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.names = ['pizza', 'unicorns', 'robots'];
|
||||
$scope.my = { favorite: 'unicorns' };
|
||||
}
|
||||
</script>
|
||||
<form ng-controller="Ctrl">
|
||||
<h2>Which is your favorite?</h2>
|
||||
<label ng-repeat="name in names" for="{{name}}">
|
||||
{{name}}
|
||||
<input type="radio"
|
||||
ng-model="my.favorite"
|
||||
ng-value="name"
|
||||
id="{{name}}"
|
||||
name="favorite">
|
||||
</label>
|
||||
</span>
|
||||
<div>You chose {{my.favorite}}</div>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('my.favorite')).toEqual('unicorns');
|
||||
});
|
||||
it('should bind the values to the inputs', function() {
|
||||
input('my.favorite').select('pizza');
|
||||
expect(binding('my.favorite')).toEqual('pizza');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngValueDirective = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function(tpl, tplAttr) {
|
||||
if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
|
||||
return function(scope, elm, attr) {
|
||||
return function ngValueConstantLink(scope, elm, attr) {
|
||||
attr.$set('value', scope.$eval(attr.ngValue));
|
||||
};
|
||||
} else {
|
||||
return function(scope, elm, attr) {
|
||||
return function ngValueLink(scope, elm, attr) {
|
||||
scope.$watch(attr.ngValue, function valueWatchAction(value) {
|
||||
attr.$set('value', value, false);
|
||||
attr.$set('value', value);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
||||
* `{{ expression }}` which is similar but less verbose.
|
||||
*
|
||||
* One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
|
||||
* it's desirable to put bindings into template that 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.
|
||||
* It is preferrable 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.
|
||||
*
|
||||
* An alternative solution to this problem would be using the
|
||||
* {@link ng.directive:ngCloak ngCloak} directive.
|
||||
@@ -61,10 +60,11 @@ var ngBindDirective = ngDirective(function(scope, element, attr) {
|
||||
*
|
||||
* @description
|
||||
* The `ngBindTemplate` directive specifies that the element
|
||||
* text should be replaced with the template in ngBindTemplate.
|
||||
* Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}`
|
||||
* expressions. (This is required since some HTML elements
|
||||
* can not have SPAN elements such as TITLE, or OPTION to name a few.)
|
||||
* text content should be replaced with the interpolation of the template
|
||||
* in the `ngBindTemplate` attribute.
|
||||
* Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
|
||||
* expressions. This directive is needed since some HTML elements
|
||||
* (such as TITLE and OPTION) cannot contain SPAN elements.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {string} ngBindTemplate template of form
|
||||
|
||||
@@ -62,8 +62,8 @@ function classDirective(name, selector) {
|
||||
* @name ng.directive:ngClass
|
||||
*
|
||||
* @description
|
||||
* The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an
|
||||
* expression that represents all classes to be added.
|
||||
* The `ngClass` allows you to set CSS classes on HTML an element, dynamically, by databinding
|
||||
* an expression that represents all classes to be added.
|
||||
*
|
||||
* The directive won't add duplicate classes if a particular class was already set.
|
||||
*
|
||||
@@ -73,7 +73,9 @@ function classDirective(name, selector) {
|
||||
* @element ANY
|
||||
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
|
||||
* of the evaluation can be a string representing space delimited class
|
||||
* names, an array, or a map of class names to boolean values.
|
||||
* names, an array, or a map of class names to boolean values. In the case of a map, the
|
||||
* names of the properties whose values are truthy will be added as css classes to the
|
||||
* element.
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*
|
||||
* <pre>
|
||||
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
* display: none;
|
||||
* display: none !important;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
* @name ng.directive:ngController
|
||||
*
|
||||
* @description
|
||||
* The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular
|
||||
* The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
|
||||
* supports the principles behind the Model-View-Controller design pattern.
|
||||
*
|
||||
* MVC components in angular:
|
||||
*
|
||||
* * Model — The Model is data in scope properties; scopes are attached to the DOM.
|
||||
* * View — The template (HTML with data bindings) is rendered into the View.
|
||||
* * Controller — The `ngController` directive specifies a Controller class; the class has
|
||||
* methods that typically express the business logic behind the application.
|
||||
* * Model — The Model is scope properties; scopes are attached to DOM where scope properties
|
||||
* are accessed through bindings.
|
||||
* * View — The template (HTML with data bindings) that is rendered into the View.
|
||||
* * Controller — The `ngController` directive specifies a Controller class; the class contains business
|
||||
* logic behind the application to decorate the scope with functions and values
|
||||
*
|
||||
* Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
|
||||
*
|
||||
@@ -25,11 +26,9 @@
|
||||
*
|
||||
* @example
|
||||
* Here is a simple form for editing user contact information. Adding, removing, clearing, and
|
||||
* greeting are methods declared on the controller (see source tab). These methods can
|
||||
* easily be called from the angular markup. Notice that the scope becomes the `this` for the
|
||||
* controller's instance. This allows for easy access to the view data from the controller. Also
|
||||
* notice that any changes to the data are automatically reflected in the View without the need
|
||||
* for a manual update.
|
||||
* greeting are methods declared on the $scope by the controller (see source tab). These methods can
|
||||
* easily be called from the angular markup. Notice that any changes to the data are automatically
|
||||
* reflected in the View without the need for a manual update.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
* For us to be compatible, we just need to implement the "getterFn" in $parse without violating
|
||||
* any of these restrictions.
|
||||
*
|
||||
* AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
|
||||
* it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
|
||||
* AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
|
||||
* directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
|
||||
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
|
||||
* be raised.
|
||||
*
|
||||
* In order to use this feature put `ngCsp` directive on the root element of the application.
|
||||
* In order to use this feature put the `ngCsp` directive on the root element of the application.
|
||||
*
|
||||
* @example
|
||||
* This example shows how to apply the `ngCsp` directive to the `html` tag.
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* @name ng.directive:ngClick
|
||||
*
|
||||
* @description
|
||||
* The ngClick allows you to specify custom behavior when
|
||||
* element is clicked.
|
||||
* The ngClick directive allows you to specify custom behavior when
|
||||
* an element is clicked.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
|
||||
@@ -37,7 +37,7 @@
|
||||
*/
|
||||
var ngEventDirectives = {};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave submit'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
@@ -58,11 +58,11 @@ forEach(
|
||||
* @name ng.directive:ngDblclick
|
||||
*
|
||||
* @description
|
||||
* The `ngDblclick` directive allows you to specify custom behavior on dblclick event.
|
||||
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
|
||||
* dblclick. (Event object is available as `$event`)
|
||||
* a dblclick. (The Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
@@ -164,6 +164,54 @@ forEach(
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeydown
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keydown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
|
||||
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeyup
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keyup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
|
||||
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngKeypress
|
||||
*
|
||||
* @description
|
||||
* Specify custom behavior on keypress event.
|
||||
*
|
||||
* @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.)
|
||||
*
|
||||
* @example
|
||||
* See {@link ng.directive:ngClick ngClick}
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ng.directive:ngSubmit
|
||||
@@ -172,10 +220,11 @@ forEach(
|
||||
* Enables binding angular expressions to onsubmit events.
|
||||
*
|
||||
* Additionally it prevents the default action (which for form means sending the request to the
|
||||
* server and reloading the current page).
|
||||
* server and reloading the current page) **but only if the form does not contain an `action`
|
||||
* attribute**.
|
||||
*
|
||||
* @element form
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -215,8 +264,3 @@ forEach(
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
|
||||
element.bind('submit', function() {
|
||||
scope.$apply(attrs.ngSubmit);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @description
|
||||
* # Overview
|
||||
* `ngPluralize` is a directive that displays messages according to en-US localization rules.
|
||||
* These rules are bundled with angular.js and the rules can be overridden
|
||||
* These rules are bundled with angular.js, but can be overridden
|
||||
* (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
|
||||
* by specifying the mappings between
|
||||
* {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
|
||||
@@ -21,8 +21,8 @@
|
||||
*
|
||||
* While a pural category may match many numbers (for example, in en-US locale, "other" can match
|
||||
* any number that is not 1), an explicit number rule can only match one number. For example, the
|
||||
* explicit number rule for "3" matches the number 3. You will see the use of plural categories
|
||||
* and explicit number rules throughout later parts of this documentation.
|
||||
* explicit number rule for "3" matches the number 3. There are examples of plural categories
|
||||
* and explicit number rules throughout the rest of this documentation.
|
||||
*
|
||||
* # Configuring ngPluralize
|
||||
* You configure ngPluralize by providing 2 attributes: `count` and `when`.
|
||||
@@ -32,8 +32,7 @@
|
||||
* Angular expression}; these are evaluated on the current scope for its bound value.
|
||||
*
|
||||
* The `when` attribute specifies the mappings between plural categories and the actual
|
||||
* string to be displayed. The value of the attribute should be a JSON object so that Angular
|
||||
* can interpret it correctly.
|
||||
* string to be displayed. The value of the attribute should be a JSON object.
|
||||
*
|
||||
* The following example shows how to configure ngPluralize:
|
||||
*
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: 'isolate',
|
||||
locals: { title:'bind' },
|
||||
scope: { title:'@' },
|
||||
template: '<div style="border: 1px solid black;">' +
|
||||
'<div style="background-color: gray">{{title}}</div>' +
|
||||
'<div ng-transclude></div>' +
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
* Optionally `ngOptions` attribute can be used to dynamically generate a list of `<option>`
|
||||
* elements for a `<select>` element using an array or an object obtained by evaluating the
|
||||
* `ngOptions` expression.
|
||||
*˝˝
|
||||
* When an item in the select menu is select, the value of array element or object property
|
||||
*
|
||||
* When an item in the `<select>` menu is selected, the value of array element or object property
|
||||
* represented by the selected option will be bound to the model identified by the `ngModel`
|
||||
* directive of the parent select element.
|
||||
*
|
||||
@@ -384,6 +384,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
modelValue = ctrl.$modelValue,
|
||||
values = valuesFn(scope) || [],
|
||||
keys = keyName ? sortedKeys(values) : values,
|
||||
key,
|
||||
groupLength, length,
|
||||
groupIndex, index,
|
||||
locals = {},
|
||||
@@ -399,8 +400,17 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
locals[valueName] = values[keyName ? locals[keyName]=keys[index]:index];
|
||||
optionGroupName = groupByFn(scope, locals) || '';
|
||||
|
||||
key = index;
|
||||
if (keyName) {
|
||||
key = keys[index];
|
||||
if ( key.charAt(0) === '$' ) continue;
|
||||
locals[keyName] = key;
|
||||
}
|
||||
|
||||
locals[valueName] = values[key];
|
||||
|
||||
optionGroupName = groupByFn(scope, locals) || '';
|
||||
if (!(optionGroup = optionGroups[optionGroupName])) {
|
||||
optionGroup = optionGroups[optionGroupName] = [];
|
||||
optionGroupNames.push(optionGroupName);
|
||||
|
||||
@@ -9,10 +9,24 @@
|
||||
* Any uncaught exception in angular expressions is delegated to this service.
|
||||
* The default implementation simply delegates to `$log.error` which logs it into
|
||||
* the browser console.
|
||||
*
|
||||
*
|
||||
* In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
|
||||
* {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* <pre>
|
||||
* angular.module('exceptionOverride', []).factory('$exceptionHandler', function () {
|
||||
* return function (exception, cause) {
|
||||
* exception.message += ' (caused by "' + cause + '")';
|
||||
* throw exception;
|
||||
* };
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* This example will override the normal action of `$exceptionHandler`, to make angular
|
||||
* exceptions fail hard when they happen, instead of just logging to the console.
|
||||
*
|
||||
* @param {Error} exception Exception associated with the error.
|
||||
* @param {string=} cause optional information about the context in which
|
||||
* the error was thrown.
|
||||
|
||||
@@ -62,7 +62,9 @@ function currencyFilter($locale) {
|
||||
* If the input is not a number an empty string is returned.
|
||||
*
|
||||
* @param {number|string} number Number to format.
|
||||
* @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to.
|
||||
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
|
||||
* If this is not provided then the fraction size is computed from the current locale's number
|
||||
* formatting pattern. In the case of the default locale, it will be 3.
|
||||
* @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
|
||||
*
|
||||
* @example
|
||||
@@ -169,6 +171,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
}
|
||||
|
||||
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
|
||||
} else {
|
||||
|
||||
if (fractionSize > 0 && number > -1 && number < 1) {
|
||||
formatedText = number.toFixed(fractionSize);
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(isNegative ? pattern.negPre : pattern.posPre);
|
||||
@@ -292,7 +299,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm)
|
||||
* * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale
|
||||
* (e.g. Friday, September 3, 2010)
|
||||
* * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010
|
||||
* * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
|
||||
* * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
|
||||
* * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
|
||||
* * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
|
||||
@@ -300,7 +307,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
*
|
||||
* `format` string can contain literal values. These need to be quoted with single quotes (e.g.
|
||||
* `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
|
||||
* (e.g. `"h o''clock"`).
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<table class="friend">
|
||||
<tr>
|
||||
<th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
|
||||
(<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th>
|
||||
(<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
|
||||
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
|
||||
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
|
||||
</tr>
|
||||
@@ -125,8 +125,10 @@ function orderByFilter($parse){
|
||||
var t1 = typeof v1;
|
||||
var t2 = typeof v2;
|
||||
if (t1 == t2) {
|
||||
if (t1 == "string") v1 = v1.toLowerCase();
|
||||
if (t1 == "string") v2 = v2.toLowerCase();
|
||||
if (t1 == "string") {
|
||||
v1 = v1.toLowerCase();
|
||||
v2 = v2.toLowerCase();
|
||||
}
|
||||
if (v1 === v2) return 0;
|
||||
return v1 < v2 ? -1 : 1;
|
||||
} else {
|
||||
|
||||
+59
-8
@@ -185,6 +185,33 @@ function $HttpProvider() {
|
||||
* will result in the success callback being called. Note that if the response is a redirect,
|
||||
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
||||
* called for such responses.
|
||||
*
|
||||
* # Calling $http from outside AngularJS
|
||||
* The `$http` service will not actually send the request until the next `$digest()` is executed.
|
||||
* Normally this is not an issue, since almost all the time your call to `$http` will be from within
|
||||
* a `$apply()` block.
|
||||
* If you are calling `$http` from outside Angular, then you should wrap it in a call to `$apply`
|
||||
* to cause a $digest to occur and also to handle errors in the block correctly.
|
||||
*
|
||||
* ```
|
||||
* $scope.$apply(function() {
|
||||
* $http(...);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* # Writing Unit Tests that use $http
|
||||
* When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do not
|
||||
* trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have been
|
||||
* made and `$httpBackend.expect(...)` expectations will fail. The solution is to run the code
|
||||
* that calls the `$http()` method inside a $apply block as explained in the previous section.
|
||||
*
|
||||
* ```
|
||||
* $httpBackend.expectGET(...);
|
||||
* $scope.$apply(function() {
|
||||
* $http.get(...);
|
||||
* });
|
||||
* $httpBackend.flush();
|
||||
* ```
|
||||
*
|
||||
* # Shortcut methods
|
||||
*
|
||||
@@ -224,7 +251,7 @@ function $HttpProvider() {
|
||||
* To add or overwrite these defaults, simply add or remove a property from these configuration
|
||||
* objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
|
||||
* with the lowercased HTTP method name as the key, e.g.
|
||||
* `$httpProvider.defaults.headers.get['My-Header']='value'`.
|
||||
* `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
|
||||
*
|
||||
* Additionally, the defaults can be set at runtime via the `$http.defaults` object in the same
|
||||
* fashion.
|
||||
@@ -291,6 +318,7 @@ function $HttpProvider() {
|
||||
* return function(promise) {
|
||||
* return promise.then(function(response) {
|
||||
* // do something on success
|
||||
* return response;
|
||||
* }, function(response) {
|
||||
* // do something on error
|
||||
* if (canRecover(response)) {
|
||||
@@ -482,17 +510,40 @@ function $HttpProvider() {
|
||||
|
||||
var reqTransformFn = config.transformRequest || $config.transformRequest,
|
||||
respTransformFn = config.transformResponse || $config.transformResponse,
|
||||
defHeaders = $config.headers,
|
||||
reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
||||
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
||||
reqHeaders = extend({}, config.headers),
|
||||
defHeaders = extend(
|
||||
{'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
||||
$config.headers.common,
|
||||
$config.headers[lowercase(config.method)]
|
||||
),
|
||||
reqData,
|
||||
defHeaderName, lowercaseDefHeaderName, headerName,
|
||||
promise;
|
||||
|
||||
// using for-in instead of forEach to avoid unecessary iteration after header has been found
|
||||
defaultHeadersIteration:
|
||||
for(defHeaderName in defHeaders) {
|
||||
lowercaseDefHeaderName = lowercase(defHeaderName);
|
||||
for(headerName in config.headers) {
|
||||
if (lowercase(headerName) === lowercaseDefHeaderName) {
|
||||
continue defaultHeadersIteration;
|
||||
}
|
||||
}
|
||||
reqHeaders[defHeaderName] = defHeaders[defHeaderName];
|
||||
}
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(config.data)) {
|
||||
delete reqHeaders['Content-Type'];
|
||||
for(var header in reqHeaders) {
|
||||
if (lowercase(header) === 'content-type') {
|
||||
delete reqHeaders[header];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn);
|
||||
|
||||
// send request
|
||||
promise = sendReq(config, reqData, reqHeaders);
|
||||
|
||||
@@ -682,7 +733,7 @@ function $HttpProvider() {
|
||||
|
||||
if (cache) {
|
||||
cachedResp = cache.get(url);
|
||||
if (cachedResp) {
|
||||
if (isDefined(cachedResp)) {
|
||||
if (cachedResp.then) {
|
||||
// cached request has already been sent, but there is no response yet
|
||||
cachedResp.then(removePendingReq, removePendingReq);
|
||||
@@ -702,7 +753,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
// if we won't have the response in cache, send the request to the backend
|
||||
if (!cachedResp) {
|
||||
if (isUndefined(cachedResp)) {
|
||||
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
|
||||
config.withCredentials);
|
||||
}
|
||||
|
||||
+1
-1
@@ -494,7 +494,7 @@ function $LocationProvider(){
|
||||
* @name ng.$locationProvider#html5Mode
|
||||
* @methodOf ng.$locationProvider
|
||||
* @description
|
||||
* @param {string=} mode Use HTML5 strategy if available.
|
||||
* @param {boolean=} mode Use HTML5 strategy if available.
|
||||
* @returns {*} current value if used as getter or itself (chaining) if used as setter
|
||||
*/
|
||||
this.html5Mode = function(mode) {
|
||||
|
||||
@@ -663,33 +663,6 @@ function setter(obj, path, setValue) {
|
||||
return setValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value accesible from the object by path. Any undefined traversals are ignored
|
||||
* @param {Object} obj starting object
|
||||
* @param {string} path path to traverse
|
||||
* @param {boolean=true} bindFnToScope
|
||||
* @returns value as accesbile by path
|
||||
*/
|
||||
//TODO(misko): this function needs to be removed
|
||||
function getter(obj, path, bindFnToScope) {
|
||||
if (!path) return obj;
|
||||
var keys = path.split('.');
|
||||
var key;
|
||||
var lastInstance = obj;
|
||||
var len = keys.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
key = keys[i];
|
||||
if (obj) {
|
||||
obj = (lastInstance = obj)[key];
|
||||
}
|
||||
}
|
||||
if (!bindFnToScope && isFunction(obj)) {
|
||||
return bind(lastInstance, obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var getterFnCache = {};
|
||||
|
||||
/**
|
||||
|
||||
+10
-10
@@ -85,8 +85,8 @@
|
||||
* **Methods**
|
||||
*
|
||||
* - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved
|
||||
* or rejected calls one of the success or error callbacks asynchronously as soon as the result
|
||||
* is available. The callbacks are called with a single argument the result or rejection reason.
|
||||
* or rejected, `then` calls one of the success or error callbacks asynchronously as soon as the result
|
||||
* is available. The callbacks are called with a single argument: the result or rejection reason.
|
||||
*
|
||||
* This method *returns a new promise* which is resolved or rejected via the return value of the
|
||||
* `successCallback` or `errorCallback`.
|
||||
@@ -94,7 +94,7 @@
|
||||
*
|
||||
* # Chaining promises
|
||||
*
|
||||
* Because calling `then` api of a promise returns a new derived promise, it is easily possible
|
||||
* Because calling the `then` method of a promise returns a new derived promise, it is easily possible
|
||||
* to create a chain of promises:
|
||||
*
|
||||
* <pre>
|
||||
@@ -102,13 +102,13 @@
|
||||
* return result + 1;
|
||||
* });
|
||||
*
|
||||
* // promiseB will be resolved immediately after promiseA is resolved and its value will be
|
||||
* // the result of promiseA incremented by 1
|
||||
* // promiseB will be resolved immediately after promiseA is resolved and its value
|
||||
* // will be the result of promiseA incremented by 1
|
||||
* </pre>
|
||||
*
|
||||
* It is possible to create chains of any length and since a promise can be resolved with another
|
||||
* promise (which will defer its resolution further), it is possible to pause/defer resolution of
|
||||
* the promises at any point in the chain. This makes it possible to implement powerful apis like
|
||||
* the promises at any point in the chain. This makes it possible to implement powerful APIs like
|
||||
* $http's response interceptors.
|
||||
*
|
||||
*
|
||||
@@ -215,8 +215,8 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
try {
|
||||
result.resolve((callback || defaultCallback)(value));
|
||||
} catch(e) {
|
||||
exceptionHandler(e);
|
||||
result.reject(e);
|
||||
exceptionHandler(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -224,8 +224,8 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
try {
|
||||
result.resolve((errback || defaultErrback)(reason));
|
||||
} catch(e) {
|
||||
exceptionHandler(e);
|
||||
result.reject(e);
|
||||
exceptionHandler(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -377,8 +377,8 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
* @param {Array.<Promise>} promises An array of promises.
|
||||
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
|
||||
* each value corresponding to the promise at the same index in the `promises` array. If any of
|
||||
* the promises is resolved with a rejection, this resulting promise will be resolved with the
|
||||
* same rejection.
|
||||
* the promises is resolved with a rejection, this resulting promise will be rejected with the
|
||||
* same rejection value.
|
||||
*/
|
||||
function all(promises) {
|
||||
var deferred = defer(),
|
||||
|
||||
+11
-3
@@ -55,8 +55,10 @@
|
||||
* @description
|
||||
*
|
||||
* Every application has a single root {@link ng.$rootScope.Scope scope}.
|
||||
* All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide
|
||||
* event processing life-cycle. See {@link guide/scope developer guide on scopes}.
|
||||
* All other scopes are descendant scopes of the root scope. Scopes provide separation
|
||||
* between the model and the view, via a mechanism for watching the model for changes.
|
||||
* They also provide an event emission/broadcast and subscription facility. See the
|
||||
* {@link guide/scope developer guide on scopes}.
|
||||
*/
|
||||
function $RootScopeProvider(){
|
||||
var TTL = 10;
|
||||
@@ -393,7 +395,7 @@ function $RootScopeProvider(){
|
||||
watch = watchers[length];
|
||||
// Most common watches are on primitives, in which case we can short
|
||||
// circuit it with === operator, only when === fails do we use .equals
|
||||
if ((value = watch.get(current)) !== (last = watch.last) &&
|
||||
if (watch && (value = watch.get(current)) !== (last = watch.last) &&
|
||||
!(watch.eq
|
||||
? equals(value, last)
|
||||
: (typeof value == 'number' && typeof last == 'number'
|
||||
@@ -446,6 +448,9 @@ function $RootScopeProvider(){
|
||||
*
|
||||
* @description
|
||||
* Broadcasted when a scope and its children are being destroyed.
|
||||
*
|
||||
* Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
|
||||
* clean up DOM bindings before an element is removed from the DOM.
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -467,6 +472,9 @@ function $RootScopeProvider(){
|
||||
* Just before a scope is destroyed a `$destroy` event is broadcasted on this scope.
|
||||
* Application code can register a `$destroy` event handler that will give it chance to
|
||||
* perform any necessary cleanup.
|
||||
*
|
||||
* Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
|
||||
* clean up DOM bindings before an element is removed from the DOM.
|
||||
*/
|
||||
$destroy: function() {
|
||||
// we can't destroy the root scope or a scope that has been already destroyed
|
||||
|
||||
+5
-3
@@ -24,8 +24,8 @@ function $RouteProvider(){
|
||||
* route definition.
|
||||
*
|
||||
* `path` can contain named groups starting with a colon (`:name`). All characters up to the
|
||||
* next slash are matched and stored in `$routeParams` under the given `name` when the route
|
||||
* matches.
|
||||
* next slash are matched and stored in `$routeParams` under the given `name` after the route
|
||||
* is resolved.
|
||||
*
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
@@ -50,7 +50,9 @@ function $RouteProvider(){
|
||||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
|
||||
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
|
||||
* and the return value is treated as the dependency. If the result is a promise, it is resolved
|
||||
* before its value is injected into the controller.
|
||||
* before its value is injected into the controller. Be aware that `ngRoute.$routeParams` will
|
||||
* still refer to the previous route within these resolve functions. Use `$route.current.params`
|
||||
* to access the new route parameters, instead.
|
||||
*
|
||||
* - `redirectTo` – {(string|function())=} – value to update
|
||||
* {@link ng.$location $location} path with and trigger route redirection.
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
|
||||
* (but its properties will likely change) even when a route change occurs.
|
||||
*
|
||||
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
|
||||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
|
||||
* Instead you can use `$route.current.params` to access the new route's parameters.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* // Given:
|
||||
|
||||
+92
-5
@@ -31,6 +31,94 @@ function $TimeoutProvider() {
|
||||
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
|
||||
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
|
||||
* promise will be resolved with is the return value of the `fn` function.
|
||||
*
|
||||
* @example
|
||||
<doc:example module="time">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl2($scope,$timeout) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
|
||||
var stop;
|
||||
$scope.fight = function() {
|
||||
stop = $timeout(function() {
|
||||
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
|
||||
$scope.blood_1 = $scope.blood_1 - 3;
|
||||
$scope.blood_2 = $scope.blood_2 - 4;
|
||||
$scope.fight();
|
||||
} else {
|
||||
$timeout.cancel(stop);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.stopFight = function() {
|
||||
$timeout.cancel(stop);
|
||||
};
|
||||
|
||||
$scope.resetFight = function() {
|
||||
$scope.blood_1 = 100;
|
||||
$scope.blood_2 = 120;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('time', [])
|
||||
// Register the 'myCurrentTime' directive factory method.
|
||||
// We inject $timeout and dateFilter service since the factory method is DI.
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
// return the directive link function. (compile function not needed)
|
||||
return function(scope, element, attrs) {
|
||||
var format, // date format
|
||||
timeoutId; // timeoutId, so that we can cancel the time updates
|
||||
|
||||
// used to update the UI
|
||||
function updateTime() {
|
||||
element.text(dateFilter(new Date(), format));
|
||||
}
|
||||
|
||||
// watch the expression, and update the UI on change.
|
||||
scope.$watch(attrs.myCurrentTime, function(value) {
|
||||
format = value;
|
||||
updateTime();
|
||||
});
|
||||
|
||||
// schedule update in one second
|
||||
function updateLater() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
updateLater(); // schedule another update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// listen on DOM destroy (removal) event, and cancel the next UI update
|
||||
// to prevent updating time ofter the DOM element was removed.
|
||||
element.bind('$destroy', function() {
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
updateLater(); // kick off the UI update process.
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div ng-controller="Ctrl2">
|
||||
Date format: <input ng-model="format"> <hr/>
|
||||
Current time is: <span my-current-time="format"></span>
|
||||
<hr/>
|
||||
Blood 1 : <font color='red'>{{blood_1}}</font>
|
||||
Blood 2 : <font color='red'>{{blood_2}}</font>
|
||||
<button type="button" data-ng-click="fight()">Fight</button>
|
||||
<button type="button" data-ng-click="stopFight()">StopFight</button>
|
||||
<button type="button" data-ng-click="resetFight()">resetFight</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
function timeout(fn, delay, invokeApply) {
|
||||
var deferred = $q.defer(),
|
||||
@@ -45,17 +133,15 @@ function $TimeoutProvider() {
|
||||
deferred.reject(e);
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
finally {
|
||||
delete deferreds[promise.$$timeoutId];
|
||||
}
|
||||
|
||||
if (!skipApply) $rootScope.$apply();
|
||||
}, delay);
|
||||
|
||||
cleanup = function() {
|
||||
delete deferreds[promise.$$timeoutId];
|
||||
};
|
||||
|
||||
promise.$$timeoutId = timeoutId;
|
||||
deferreds[timeoutId] = deferred;
|
||||
promise.then(cleanup, cleanup);
|
||||
|
||||
return promise;
|
||||
}
|
||||
@@ -77,6 +163,7 @@ function $TimeoutProvider() {
|
||||
timeout.cancel = function(promise) {
|
||||
if (promise && promise.$$timeoutId in deferreds) {
|
||||
deferreds[promise.$$timeoutId].reject('canceled');
|
||||
delete deferreds[promise.$$timeoutId];
|
||||
return $browser.defer.cancel(promise.$$timeoutId);
|
||||
}
|
||||
return false;
|
||||
|
||||
+4
-2
@@ -10,8 +10,10 @@
|
||||
* it is a global variable. In angular we always refer to it through the
|
||||
* `$window` service, so it may be overriden, removed or mocked for testing.
|
||||
*
|
||||
* All expressions are evaluated with respect to current scope so they don't
|
||||
* suffer from window globality.
|
||||
* Expressions, like the one defined for the `ngClick` directive in the example
|
||||
* below, are evaluated with respect to the current scope. Therefore, there is
|
||||
* no risk of inadvertently coding in a dependency on a global value in such an
|
||||
* expression.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
|
||||
@@ -18,6 +18,17 @@ angular.module('ngCookies', ['ng']).
|
||||
* Only a simple Object is exposed and by adding or removing properties to/from
|
||||
* this object, new cookies are created/deleted at the end of current $eval.
|
||||
*
|
||||
* # Installation
|
||||
* To use $cookies make sure you have included the `angular-cookies.js` that comes in Angular
|
||||
* package. You can also find this file on Google CDN, bower as well as at
|
||||
* {@link http://code.angularjs.org/ code.angularjs.org}.
|
||||
*
|
||||
* Finally load the module in your application:
|
||||
*
|
||||
* angular.module('app', ['ngCookies']);
|
||||
*
|
||||
* and you are ready to get started!
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
||||
Vendored
+54
-54
@@ -2,56 +2,56 @@ angular.module("ngLocale", [], ["$provide", function($provide) {
|
||||
var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
|
||||
$provide.value("$locale", {
|
||||
"DATETIME_FORMATS": {
|
||||
"AMPMS": {
|
||||
"0": "vm.",
|
||||
"1": "nm."
|
||||
},
|
||||
"DAY": {
|
||||
"0": "Sondag",
|
||||
"1": "Maandag",
|
||||
"2": "Dinsdag",
|
||||
"3": "Woensdag",
|
||||
"4": "Donderdag",
|
||||
"5": "Vrydag",
|
||||
"6": "Saterdag"
|
||||
},
|
||||
"MONTH": {
|
||||
"0": "Januarie",
|
||||
"1": "Februarie",
|
||||
"2": "Maart",
|
||||
"3": "April",
|
||||
"4": "Mei",
|
||||
"5": "Junie",
|
||||
"6": "Julie",
|
||||
"7": "Augustus",
|
||||
"8": "September",
|
||||
"9": "Oktober",
|
||||
"10": "November",
|
||||
"11": "Desember"
|
||||
},
|
||||
"SHORTDAY": {
|
||||
"0": "So",
|
||||
"1": "Ma",
|
||||
"2": "Di",
|
||||
"3": "Wo",
|
||||
"4": "Do",
|
||||
"5": "Vr",
|
||||
"6": "Sa"
|
||||
},
|
||||
"SHORTMONTH": {
|
||||
"0": "Jan",
|
||||
"1": "Feb",
|
||||
"2": "Mar",
|
||||
"3": "Apr",
|
||||
"4": "Mei",
|
||||
"5": "Jun",
|
||||
"6": "Jul",
|
||||
"7": "Aug",
|
||||
"8": "Sep",
|
||||
"9": "Okt",
|
||||
"10": "Nov",
|
||||
"11": "Des"
|
||||
},
|
||||
"AMPMS": [
|
||||
"vm.",
|
||||
"nm."
|
||||
],
|
||||
"DAY": [
|
||||
"Sondag",
|
||||
"Maandag",
|
||||
"Dinsdag",
|
||||
"Woensdag",
|
||||
"Donderdag",
|
||||
"Vrydag",
|
||||
"Saterdag"
|
||||
],
|
||||
"MONTH": [
|
||||
"Januarie",
|
||||
"Februarie",
|
||||
"Maart",
|
||||
"April",
|
||||
"Mei",
|
||||
"Junie",
|
||||
"Julie",
|
||||
"Augustus",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Desember"
|
||||
],
|
||||
"SHORTDAY": [
|
||||
"So",
|
||||
"Ma",
|
||||
"Di",
|
||||
"Wo",
|
||||
"Do",
|
||||
"Vr",
|
||||
"Sa"
|
||||
],
|
||||
"SHORTMONTH": [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"Mei",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Okt",
|
||||
"Nov",
|
||||
"Des"
|
||||
],
|
||||
"fullDate": "EEEE d MMMM y",
|
||||
"longDate": "d MMMM y",
|
||||
"medium": "d MMM y HH:mm:ss",
|
||||
@@ -65,8 +65,8 @@ $provide.value("$locale", {
|
||||
"CURRENCY_SYM": "R",
|
||||
"DECIMAL_SEP": ",",
|
||||
"GROUP_SEP": "\u00a0",
|
||||
"PATTERNS": {
|
||||
"0": {
|
||||
"PATTERNS": [
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
@@ -78,7 +78,7 @@ $provide.value("$locale", {
|
||||
"posPre": "",
|
||||
"posSuf": ""
|
||||
},
|
||||
"1": {
|
||||
{
|
||||
"gSize": 3,
|
||||
"lgSize": 3,
|
||||
"macFrac": 0,
|
||||
@@ -90,7 +90,7 @@ $provide.value("$locale", {
|
||||
"posPre": "\u00a4",
|
||||
"posSuf": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "af-na",
|
||||
"pluralCat": function (n) { if (n == 1) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user