Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eabedba34d | |||
| b4add97c17 | |||
| bacc31bea9 | |||
| ad90c3574f | |||
| e28171d5e4 | |||
| ce73ed091b | |||
| 90ac8d57b0 | |||
| 6eb1179505 | |||
| 9b85757102 | |||
| c6c3949b14 | |||
| e175db37c6 | |||
| f38010d3a2 | |||
| 7fc18b263d | |||
| fabc9f77a3 | |||
| c17c731fdc | |||
| 3692885810 | |||
| 5d43439dbe | |||
| a46f2a0db3 | |||
| 3217a249e1 | |||
| 78f394fd17 | |||
| e82e64d57b | |||
| 8978e066b5 | |||
| 833eb3c844 | |||
| 07926ff1ef | |||
| e801faba2e | |||
| ee6af9a978 | |||
| 74379df6c4 | |||
| fe65dd926c | |||
| 669b53ede2 | |||
| b0c3f28e8f | |||
| 9810dc0993 | |||
| ab5df20dfa | |||
| d83a92c121 | |||
| d0425de29e | |||
| ad5e42cf82 | |||
| 9ed1126adb | |||
| 7a19eb84aa | |||
| 718741acab | |||
| ec8bb675b4 | |||
| 8e32f3fd35 | |||
| 02332107e5 | |||
| afc81b554e | |||
| 26e8ab3693 | |||
| 28ccc76aa1 | |||
| b3c4cb7cff | |||
| 4af4378b11 | |||
| 8611ebe6a0 | |||
| 8f46a3c9ac | |||
| 66fdb36ecb | |||
| f0f5ffa9aa | |||
| 2bc7afd3ba | |||
| a4b45397e0 | |||
| de4e06ed73 | |||
| fd822bdaf9 | |||
| 4f78fd692c | |||
| df6d2ba326 | |||
| ccda436f94 | |||
| e86c435349 | |||
| 1942861472 | |||
| b96e978178 | |||
| bda2bba2be | |||
| ca08c004c8 | |||
| 25a62b58db | |||
| 97e3ec4d1b | |||
| 75f11f1fc4 | |||
| e134a8335f | |||
| 8ee32a75f0 |
@@ -11,3 +11,5 @@ performance/temp*.html
|
||||
angular.js.tmproj
|
||||
node_modules
|
||||
jsTestDriver*.conf
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
|
||||
+134
-29
@@ -1,7 +1,109 @@
|
||||
- The Latest Stable Release: <a href="#0.9.19">0.9.19 canine-psychokinesis</a>
|
||||
- The Latest Unstable Release: <a href="#0.10.2">0.10.2 sneaky-seagull</a>
|
||||
- The Latest Unstable Release: <a href="#0.10.4">0.10.4 human-torch</a>
|
||||
|
||||
<a name="0.10.2"><a/>
|
||||
<a name="0.10.4"></a>
|
||||
# 0.10.4 human-torch (2011-10-22) #
|
||||
|
||||
## Features:
|
||||
|
||||
- New validation options for
|
||||
[input widgets](http://docs-next.angularjs.org/api/angular.widget.input): `ng:minlength` and
|
||||
`ng:maxlength`
|
||||
([commit](https://github.com/angular/angular.js/commit/78f394fd17be581c84ecd526bb786ed1681d35cb))
|
||||
(contributed by Konstantin Stepanov)
|
||||
- HTML sanitizer was updated to recognize all safe HTML5 elements
|
||||
(Issue [#89](https://github.com/angular/angular.js/issues/89))
|
||||
- [ng:options]' blank option is now compiled and data-bound as any other template
|
||||
(Issue [#562](https://github.com/angular/angular.js/issues/562))
|
||||
(contributed by tehek)
|
||||
- [$defer](http://docs-next.angularjs.org/api/angular.service.$defer) service now exposes `cancel`
|
||||
method for task cancellation
|
||||
([commit](https://github.com/angular/angular.js/commit/ad90c3574f8365ee4a1a973d5e43c64fe9fcda2c))
|
||||
|
||||
|
||||
## Bug Fixes:
|
||||
|
||||
- [ng:options] should select correct element when '?'-option (invalid value) was previously selected
|
||||
(Issue [#599](https://github.com/angular/angular.js/issues/599)) (contributed by Tehek)
|
||||
- Fix data-binding of radio button's value property
|
||||
(Issue [#316](https://github.com/angular/angular.js/issues/316))
|
||||
- Input with type `password` should no be turned into a readable text field
|
||||
([commit](https://github.com/angular/angular.js/commit/e82e64d57b65d9f3c4f2e8831f30b615a069b7f6))
|
||||
(contributed by Konstantin Stepanov)
|
||||
- [ng:repeat] should ignore object properties starting with `$`
|
||||
([commit](https://github.com/angular/angular.js/commit/833eb3c84445110dc1dad238120573f08ed8d102))
|
||||
- Correctly parse out inlined regexp from the input field's `ng:pattern` attribute.
|
||||
([commit](https://github.com/angular/angular.js/commit/5d43439dbe764a4c7227f51b34a81b044f13901b))
|
||||
- $location service in html5 mode should correctly rewrite links that contain nested elements
|
||||
([commit](https://github.com/angular/angular.js/commit/9b85757102fbd44e88d0a3909fdf8b90f191b593))
|
||||
|
||||
|
||||
## Breaking Changes:
|
||||
|
||||
- the [date] filter now uses 'mediumDate' format if none is specified. This was done to deal with
|
||||
browser inconsistencies (each browser used to use different format)
|
||||
(Issue [#605](https://github.com/angular/angular.js/issues/605),
|
||||
[commit](https://github.com/angular/angular.js/commit/c6c3949b14f4003ecab291243edfca61262f2c3d),
|
||||
[commit](https://github.com/angular/angular.js/commit/e175db37c6f52bba4080efeec22a7120a896099e))
|
||||
- calling the linker function returned by [angular.compile][compile] doesn't automatically run
|
||||
`$digest` on the linked scope any more. This behavior was briefly introduced in 0.10.3 but was
|
||||
causing issues and inefficiencies in production apps so we reverted it. See:
|
||||
[commit](https://github.com/angular/angular.js/commit/f38010d3a2f457a53798212ef72418637dabe189)
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.10.3"></a>
|
||||
# 0.10.3 shattering-heartbeat (2011-10-13) #
|
||||
|
||||
## Features:
|
||||
|
||||
- New forms, validation, support for HTML5 input widgets. Please check out:
|
||||
- [Forms overview](http://docs-next.angularjs.org/guide/dev_guide.forms)
|
||||
- [form widget](http://docs-next.angularjs.org/api/angular.widget.form)
|
||||
- [input widget](http://docs-next.angularjs.org/api/angular.widget.input)
|
||||
- [$formFactory service](http://docs-next.angularjs.org/api/angular.service.$formFactory)
|
||||
- [angular.inputType](http://docs-next.angularjs.org/api/angular.inputType)
|
||||
- [commit](https://github.com/angular/angular.js/commit/4f78fd692c0ec51241476e6be9a4df06cd62fdd6)
|
||||
|
||||
- [ng:repeat] now has element-model affinity, which makes it more friendly to third-party code that
|
||||
is not aware of angular's DOM manipulation. This is also the pre-requisite for supporting
|
||||
animations.
|
||||
([commit](https://github.com/angular/angular.js/commit/75f11f1fc46c35a28c0905f7316ea6779145e2fb))
|
||||
|
||||
|
||||
## Bug Fixes:
|
||||
|
||||
- The select widget with [ng:options] directive now correctly displays selected option (regression
|
||||
from 0.10.2).
|
||||
- Fix for jqLite's removeClass, which under certain circumstances could clobber class names.
|
||||
([commit](https://github.com/angular/angular.js/commit/b96e978178a6acbf048aa6db466ed845e1395445))
|
||||
- Other small fixes and documentation improvements.
|
||||
|
||||
|
||||
## Breaking Changes:
|
||||
|
||||
- Due to changes in how forms and validation works the following were replaced with new apis:
|
||||
- `angular.formatter` - use `angular.inputType` or form's `$createWidget`
|
||||
- `angular.validator` - use `angular.inputType` or form's `$createWidget`
|
||||
- changes to `<input>` and `<select>` elements
|
||||
- `ng:model` directive is now required for data-binding to kick in
|
||||
- the `name` attribute is now optional and is used only as an alias when accessing the input
|
||||
widget via the form object.
|
||||
- view can't affect the model without a user interaction, so the `value` attribute of the
|
||||
`<input>` element and `selected` attribute of the `<option>` element if specified in the
|
||||
template is ignored.
|
||||
- Removed decoration of DOM elements when:
|
||||
- an exception occurs - when an exception happens, it will be passed to the $exceptionHandler
|
||||
service, which can decide what to do with it.
|
||||
- an input widget contains invalid input - in this case the forms validation apis can be used to
|
||||
display a customized error message.
|
||||
- The $hover service was removed (it was needed only for the DOM decoration described above).
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="0.10.2"></a>
|
||||
# 0.10.2 sneaky-seagull (2011-10-08) #
|
||||
|
||||
## Features:
|
||||
@@ -46,10 +148,13 @@
|
||||
- If Angular is being used with jQuery older than 1.6, some features might not work properly. Please
|
||||
upgrade to jQuery version 1.6.4.
|
||||
|
||||
## Breaking Changes
|
||||
- ng:repeat no longer has ng:repeat-index property. This is because the elements now have
|
||||
affinity to the underlying collection, and moving items around in the collection would move
|
||||
ng:repeat-index property rendering it meaningless.
|
||||
|
||||
|
||||
|
||||
<a name="0.10.1"><a/>
|
||||
<a name="0.10.1"></a>
|
||||
# 0.10.1 inexorable-juggernaut (2011-09-09) #
|
||||
|
||||
## Features
|
||||
@@ -82,7 +187,7 @@
|
||||
- $location.hashPath -> $location.path()
|
||||
- $location.hashSearch -> $location.search()
|
||||
- $location.search -> no equivalent, use $window.location.search (this is so that we can work in
|
||||
hashBang and html5 mode at the same time, check out the docs)
|
||||
hashBang and html5 mode at the same time, check out the docs)
|
||||
- $location.update() / $location.updateHash() -> use $location.url()
|
||||
- n/a -> $location.replace() - new api for replacing history record instead of creating a new one
|
||||
|
||||
@@ -95,7 +200,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.10.0"><a/>
|
||||
<a name="0.10.0"></a>
|
||||
# 0.10.0 chicken-hands (2011-09-02) #
|
||||
|
||||
## Features
|
||||
@@ -150,7 +255,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.19"><a/>
|
||||
<a name="0.9.19"></a>
|
||||
# 0.9.19 canine-psychokinesis (2011-08-20) #
|
||||
|
||||
## Features
|
||||
@@ -186,7 +291,7 @@
|
||||
`css('display', 'block'/'inline'/..)` instead
|
||||
|
||||
|
||||
<a name="0.9.18"><a/>
|
||||
<a name="0.9.18"></a>
|
||||
# 0.9.18 jiggling-armfat (2011-07-29) #
|
||||
|
||||
### Features
|
||||
@@ -219,7 +324,7 @@
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
- make injector compatible with Rhino (HtmlUnit) (contributed by Mårten Dolk)
|
||||
- make injector compatible with Rhino (HtmlUnit) (contributed by Mårten Dolk)
|
||||
[commit](https://github.com/angular/angular.js/commit/77ba539f630c57b17d71dbf1e9c5667a7eb603b7)
|
||||
- `ie-compat.js` fixes and improvements related to fetching this file on the fly on legacy browsers
|
||||
- [jqLite]
|
||||
@@ -256,7 +361,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.17"><a/>
|
||||
<a name="0.9.17"></a>
|
||||
# <angular/> 0.9.17 vegetable-reanimation (2011-06-30) #
|
||||
|
||||
### New Features
|
||||
@@ -291,7 +396,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.16"><a/>
|
||||
<a name="0.9.16"></a>
|
||||
# <angular/> 0.9.16 weather-control (2011-06-07) #
|
||||
|
||||
### Features
|
||||
@@ -322,7 +427,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.15"><a/>
|
||||
<a name="0.9.15"></a>
|
||||
# <angular/> 0.9.15 lethal-stutter (2011-04-11) #
|
||||
|
||||
### Features
|
||||
@@ -341,7 +446,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.14"><a/>
|
||||
<a name="0.9.14"></a>
|
||||
# <angular/> 0.9.14 key-maker (2011-04-01) #
|
||||
|
||||
### Performance
|
||||
@@ -362,12 +467,12 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.13"><a/>
|
||||
<a name="0.9.13"></a>
|
||||
# <angular/> 0.9.13 curdling-stare (2011-03-13) #
|
||||
|
||||
### New Features
|
||||
- Added XSRF protection for the [$xhr] service. (commit c578f8c3)
|
||||
- Targeted auto-bootstrap — [ng:autobind] now takes an optional value which specifies an element id
|
||||
- Targeted auto-bootstrap — [ng:autobind] now takes an optional value which specifies an element id
|
||||
to be compiled instead of compiling the entire html document. (commit 9d5c5337)
|
||||
|
||||
|
||||
@@ -389,7 +494,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.12"><a/>
|
||||
<a name="0.9.12"></a>
|
||||
# <angular/> 0.9.12 thought-implanter (2011-03-03) #
|
||||
|
||||
### API
|
||||
@@ -446,7 +551,7 @@
|
||||
|
||||
|
||||
|
||||
<a name="0.9.11"><a/>
|
||||
<a name="0.9.11"></a>
|
||||
# <angular/> 0.9.11 snow-maker (2011-02-08) #
|
||||
|
||||
### Documentation
|
||||
@@ -477,7 +582,7 @@
|
||||
request via the `$xhr` service or remove all unneeded `flush()` calls.
|
||||
|
||||
|
||||
<a name="0.9.10"><a/>
|
||||
<a name="0.9.10"></a>
|
||||
# <angular/> 0.9.10 flea-whisperer (2011-01-26) #
|
||||
|
||||
### Features
|
||||
@@ -501,7 +606,7 @@ with the `$route` service
|
||||
- lots of improvements related to formatting of the content of docs.anguarjs.org
|
||||
|
||||
|
||||
<a name="0.9.9"><a/>
|
||||
<a name="0.9.9"></a>
|
||||
# <angular/> 0.9.9 time-shift (2011-01-13) #
|
||||
|
||||
### Security
|
||||
@@ -532,7 +637,7 @@ with the `$route` service
|
||||
- angular.filter.date now properly handles some corner-cases (issue #159 - fix contributed by Vojta)
|
||||
|
||||
### Breaking changes
|
||||
- API for accessing registered services — `scope.$inject` — was renamed to
|
||||
- API for accessing registered services — `scope.$inject` — was renamed to
|
||||
[`scope.$service`](http://docs.angularjs.org/#!/api/angular.scope.$service). (commit b2631f61)
|
||||
|
||||
- Support for `eager-published` services was removed. This change was done to make explicit
|
||||
@@ -577,7 +682,7 @@ with the `$route` service
|
||||
- The `toString` method of the `angular.service.$location` service was removed. (commit 23875cb3)
|
||||
|
||||
|
||||
<a name="0.9.8"><a/>
|
||||
<a name="0.9.8"></a>
|
||||
# <angular/> 0.9.8 astral-projection (2010-12-23) #
|
||||
|
||||
### Docs/Getting started
|
||||
@@ -591,7 +696,7 @@ with the `$route` service
|
||||
- Ignore input widgets which have no name (issue #153)
|
||||
|
||||
|
||||
<a name="0.9.7"><a/>
|
||||
<a name="0.9.7"></a>
|
||||
# <angular/> 0.9.7 sonic-scream (2010-12-10) #
|
||||
|
||||
### Bug Fixes
|
||||
@@ -610,7 +715,7 @@ with the `$route` service
|
||||
your controllers. (commit e5e69d9b90850eb653883f52c76e28dd870ee067)
|
||||
|
||||
|
||||
<a name="0.9.6"><a/>
|
||||
<a name="0.9.6"></a>
|
||||
# <angular/> 0.9.6 night-vision (2010-12-06) #
|
||||
|
||||
### Security
|
||||
@@ -640,7 +745,7 @@ with the `$route` service
|
||||
- The HTML sanitizer is slightly more strinct now. Please see info in the "Security" section above.
|
||||
|
||||
|
||||
<a name="0.9.5"><a/>
|
||||
<a name="0.9.5"></a>
|
||||
# <angular/> 0.9.5 turkey-blast (2010-11-25) #
|
||||
|
||||
### Docs
|
||||
@@ -650,7 +755,7 @@ with the `$route` service
|
||||
- added `angular.Array.limitTo` to make it easy to select first or last few items of an array
|
||||
|
||||
|
||||
<a name="0.9.4"><a/>
|
||||
<a name="0.9.4"></a>
|
||||
# <angular/> 0.9.4 total-recall (2010-11-18) #
|
||||
|
||||
### Docs
|
||||
@@ -667,7 +772,7 @@ with the `$route` service
|
||||
- Better error handling - compilation exception now contain stack trace (commit b2d63ac4)
|
||||
|
||||
|
||||
<a name="0.9.3"><a/>
|
||||
<a name="0.9.3"></a>
|
||||
# <angular/> 0.9.3 cold-resistance (2010-11-10) #
|
||||
|
||||
### Docs
|
||||
@@ -695,7 +800,7 @@ with the `$route` service
|
||||
simple RegExp validator.
|
||||
|
||||
|
||||
<a name="0.9.2"><a/>
|
||||
<a name="0.9.2"></a>
|
||||
# <angular/> 0.9.2 faunal-mimicry (2010-11-03) #
|
||||
|
||||
### Docs
|
||||
@@ -733,7 +838,7 @@ with the `$route` service
|
||||
implements HEAD
|
||||
|
||||
|
||||
<a name="0.9.1"><a/>
|
||||
<a name="0.9.1"></a>
|
||||
# <angular/> 0.9.1 repulsion-field (2010-10-26) #
|
||||
|
||||
### Security
|
||||
@@ -760,7 +865,7 @@ with the `$route` service
|
||||
- html filter now sanitizes html content for XSS attacks which may result in different behavior
|
||||
|
||||
|
||||
<a name="0.9.0"><a/>
|
||||
<a name="0.9.0"></a>
|
||||
# <angular/> 0.9.0 dragon-breath (2010-10-20) #
|
||||
|
||||
### Security
|
||||
|
||||
@@ -77,66 +77,8 @@ task :compile_jstd_scenario_adapter => :init do
|
||||
end
|
||||
|
||||
|
||||
desc 'Generate IE css js patch'
|
||||
task :generate_ie_compat => :init do
|
||||
css = File.open('css/angular.css', 'r') {|f| f.read }
|
||||
|
||||
# finds all css rules that contain backround images and extracts the rule name(s), content type of
|
||||
# the image and base64 encoded image data
|
||||
r = /\n([^\{\n]+)\s*\{[^\}]*background-image:\s*url\("data:([^;]+);base64,([^"]+)"\);[^\}]*\}/
|
||||
|
||||
images = css.scan(r)
|
||||
|
||||
# create a js file with multipart header containing the extracted images. the entire file *must*
|
||||
# be CRLF (\r\n) delimited
|
||||
File.open(path_to('angular-ie-compat.js'), 'w') do |f|
|
||||
f.write("/*\r\n" +
|
||||
"Content-Type: multipart/related; boundary=\"_\"\r\n" +
|
||||
"\r\n")
|
||||
|
||||
images.each_index do |idx|
|
||||
f.write("--_\r\n" +
|
||||
"Content-Location:img#{idx}\r\n" +
|
||||
"Content-Transfer-Encoding:base64\r\n" +
|
||||
"\r\n" +
|
||||
images[idx][2] + "\r\n")
|
||||
end
|
||||
|
||||
f.write("--_--\r\n" +
|
||||
"*/\r\n")
|
||||
|
||||
# generate a css string containing *background-image rules for IE that point to the mime type
|
||||
# images in the header
|
||||
cssString = ''
|
||||
images.each_index do |idx|
|
||||
cssString += "#{images[idx][0]}{*background-image:url(\"mhtml:' + jsUri + '!img#{idx}\")}"
|
||||
end
|
||||
|
||||
# generate a javascript closure that contains a function which will append the generated css
|
||||
# string as a stylesheet to the current html document
|
||||
jsString = "(function(){ \r\n" +
|
||||
" var jsUri = document.location.href.replace(/\\/[^\\\/]+(#.*)?$/, '/') + \r\n" +
|
||||
" document.getElementById('ng-ie-compat').src,\r\n" +
|
||||
" css = '#{cssString}',\r\n" +
|
||||
" s = document.createElement('style'); \r\n" +
|
||||
"\r\n" +
|
||||
" s.setAttribute('type', 'text/css'); \r\n" +
|
||||
"\r\n" +
|
||||
" if (s.styleSheet) { \r\n" +
|
||||
" s.styleSheet.cssText = css; \r\n" +
|
||||
" } else { \r\n" +
|
||||
" s.appendChild(document.createTextNode(css)); \r\n" +
|
||||
" } \r\n" +
|
||||
" document.getElementsByTagName('head')[0].appendChild(s); \r\n" +
|
||||
"})();\r\n"
|
||||
|
||||
f.write(jsString)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
desc 'Compile JavaScript'
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter, :generate_ie_compat] do
|
||||
task :compile => [:init, :compile_scenario, :compile_jstd_scenario_adapter] do
|
||||
|
||||
deps = [
|
||||
'src/angular.prefix',
|
||||
@@ -193,7 +135,6 @@ task :package => [:clean, :compile, :docs] do
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-ie-compat.js'),
|
||||
path_to('angular-scenario.js'),
|
||||
path_to('jstd-scenario-adapter.js'),
|
||||
path_to('jstd-scenario-adapter-config.js'),
|
||||
|
||||
Vendored
+9
-7
@@ -12,15 +12,12 @@ angularFiles = {
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
'src/filters.js',
|
||||
'src/formatters.js',
|
||||
'src/validators.js',
|
||||
'src/service/cookieStore.js',
|
||||
'src/service/cookies.js',
|
||||
'src/service/defer.js',
|
||||
'src/service/document.js',
|
||||
'src/service/exceptionHandler.js',
|
||||
'src/service/hover.js',
|
||||
'src/service/invalidWidgets.js',
|
||||
'src/service/formFactory.js',
|
||||
'src/service/location.js',
|
||||
'src/service/log.js',
|
||||
'src/service/resource.js',
|
||||
@@ -36,6 +33,9 @@ angularFiles = {
|
||||
'src/directives.js',
|
||||
'src/markups.js',
|
||||
'src/widgets.js',
|
||||
'src/widget/form.js',
|
||||
'src/widget/input.js',
|
||||
'src/widget/select.js',
|
||||
'src/AngularPublic.js',
|
||||
],
|
||||
|
||||
@@ -57,7 +57,7 @@ angularFiles = {
|
||||
],
|
||||
|
||||
'jstd': [
|
||||
'lib/jasmine-1.0.1/jasmine.js',
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
@@ -75,6 +75,7 @@ angularFiles = {
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/widget/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
@@ -93,7 +94,7 @@ angularFiles = {
|
||||
],
|
||||
|
||||
'jstdPerf': [
|
||||
'lib/jasmine-1.0.1/jasmine.js',
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'angularSrc',
|
||||
'src/angular-mocks.js',
|
||||
@@ -109,7 +110,7 @@ angularFiles = {
|
||||
],
|
||||
|
||||
'jstdJquery': [
|
||||
'lib/jasmine-1.0.1/jasmine.js',
|
||||
'lib/jasmine/jasmine.js',
|
||||
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
|
||||
'lib/jquery/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
@@ -127,6 +128,7 @@ angularFiles = {
|
||||
'test/jstd-scenario-adapter/*.js',
|
||||
'test/*.js',
|
||||
'test/service/*.js',
|
||||
'test/widget/*.js',
|
||||
'example/personalLog/test/*.js'
|
||||
],
|
||||
|
||||
|
||||
@@ -7,85 +7,3 @@
|
||||
.ng-format-negative {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.ng-exception {
|
||||
border: 2px solid #FF0000;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.ng-validation-error {
|
||||
border: 2px solid #FF0000;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* TIP
|
||||
*****************/
|
||||
#ng-callout {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
vertical-align: baseline;
|
||||
background: transparent;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#ng-callout .ng-arrow-left{
|
||||
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrSLoc/AG8FeUUIN+sGebWAnbKSJodqqlsOxJtqYooU9vvk+vcJIcTkg+QAAA7");
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
position: absolute;
|
||||
z-index:101;
|
||||
left:-12px;
|
||||
height:23px;
|
||||
width:10px;
|
||||
top:-3px;
|
||||
}
|
||||
|
||||
#ng-callout .ng-arrow-right{
|
||||
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrCLTcoM29yN6k9socs91e5X3EyJloipYrO4ohTMqA0Fn2XVNswJe+H+SXAAA7");
|
||||
background-repeat: no-repeat;
|
||||
background-position: left top;
|
||||
position: absolute;
|
||||
z-index:101;
|
||||
height:23px;
|
||||
width:11px;
|
||||
top:-2px;
|
||||
}
|
||||
|
||||
#ng-callout {
|
||||
position: absolute;
|
||||
z-index:100;
|
||||
border: 2px solid #CCCCCC;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#ng-callout .ng-content{
|
||||
padding:10px 10px 10px 10px;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
#ng-callout .ng-title{
|
||||
background-color: #CCCCCC;
|
||||
text-align: left;
|
||||
padding-left: 8px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 2px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* indicators
|
||||
*****************/
|
||||
.ng-input-indicator-wait {
|
||||
background-image: url("data:image/png;base64,R0lGODlhEAAQAPQAAP///wAAAPDw8IqKiuDg4EZGRnp6egAAAFhYWCQkJKysrL6+vhQUFJycnAQEBDY2NmhoaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==");
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
@ngdoc overview
|
||||
@name angular.inputType
|
||||
@description
|
||||
|
||||
Angular {@link guide/dev_guide.forms forms} allow you to build complex widgets. However for
|
||||
simple widget which are based on HTML input text element a simpler way of providing the validation
|
||||
and parsing is also provided. `angular.inputType` is a short hand for creating a widget which
|
||||
already has the DOM listeners and `$render` method supplied. The only thing which needs to
|
||||
be provided by the developer are the optional `$validate` listener and
|
||||
`$parseModel` or `$parseModel` methods.
|
||||
|
||||
All `inputType` widgets support:
|
||||
|
||||
- CSS classes:
|
||||
- **`ng-valid`**: when widget is valid.
|
||||
- **`ng-invalid`**: when widget is invalid.
|
||||
- **`ng-pristine`**: when widget has not been modified by user action.
|
||||
- **`ng-dirty`**: when has been modified do to user action.
|
||||
|
||||
- Widget properties:
|
||||
- **`$valid`**: When widget is valid.
|
||||
- **`$invalid`**: When widget is invalid.
|
||||
- **`$pristine`**: When widget has not been modified by user interaction.
|
||||
- **`$dirty`**: When user has been modified do to user interaction.
|
||||
- **`$required`**: When the `<input>` element has `required` attribute. This means that the
|
||||
widget will have `REQUIRED` validation error if empty.
|
||||
- **`$disabled`**: When the `<input>` element has `disabled` attribute.
|
||||
- **`$readonly`**: When the `<input>` element has `readonly` attribute.
|
||||
|
||||
- Widget Attribute Validators:
|
||||
- **`required`**: Sets `REQUIRED` validation error key if the input is empty
|
||||
- **`ng:pattern`** 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.
|
||||
|
||||
|
||||
|
||||
# Example
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.inputType('json', function() {
|
||||
this.$parseView = function() {
|
||||
try {
|
||||
this.$modelValue = angular.fromJson(this.$viewValue);
|
||||
if (this.$error.JSON) {
|
||||
this.$emit('$valid', 'JSON');
|
||||
}
|
||||
} catch (e) {
|
||||
this.$emit('$invalid', 'JSON');
|
||||
}
|
||||
}
|
||||
|
||||
this.$parseModel = function() {
|
||||
this.$viewValue = angular.toJson(this.$modelValue);
|
||||
}
|
||||
});
|
||||
|
||||
function Ctrl() {
|
||||
this.data = {
|
||||
framework:'angular',
|
||||
codenames:'supper-powers'
|
||||
}
|
||||
this.required = false;
|
||||
this.disabled = false;
|
||||
this.readonly = false;
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="Ctrl">
|
||||
<form name="myForm">
|
||||
<input type="json" ng:model="data" size="80"
|
||||
ng:required="{{required}}" ng:disabled="{{disabled}}"
|
||||
ng:readonly="{{readonly}}"/><br/>
|
||||
Required: <input type="checkbox" ng:model="required"> <br/>
|
||||
Disabled: <input type="checkbox" ng:model="disabled"> <br/>
|
||||
Readonly: <input type="checkbox" ng:model="readonly"> <br/>
|
||||
<pre>data={{data}}</pre>
|
||||
<pre>myForm={{myForm}}</pre>
|
||||
</form>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should invalidate on wrong input', function() {
|
||||
expect(element('form[name=myForm]').prop('className')).toMatch('ng-valid');
|
||||
input('data').enter('{}');
|
||||
expect(binding('data')).toEqual('data={\n }');
|
||||
input('data').enter('{');
|
||||
expect(element('form[name=myForm]').prop('className')).toMatch('ng-invalid');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
@description
|
||||
@@ -14,8 +13,6 @@ session cookies
|
||||
* {@link angular.service.$document $document } - Provides reference to `window.document` element
|
||||
* {@link angular.service.$exceptionHandler $exceptionHandler } - Receives uncaught angular
|
||||
exceptions
|
||||
* {@link angular.service.$hover $hover } -
|
||||
* {@link angular.service.$invalidWidgets $invalidWidgets } - Holds references to invalid widgets
|
||||
* {@link angular.service.$location $location } - Parses the browser location URL
|
||||
* {@link angular.service.$log $log } - Provides logging service
|
||||
* {@link angular.service.$resource $resource } - Creates objects for interacting with RESTful
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
* {@link angular.directive Directives} - Angular DOM element attributes
|
||||
* {@link angular.markup Markup} and {@link angular.attrMarkup Attribute Markup}
|
||||
* {@link angular.filter Filters} - Angular output filters
|
||||
* {@link angular.formatter Formatters} - Angular converters for form elements
|
||||
* {@link angular.validator Validators} - Angular input validators
|
||||
* {@link angular.compile angular.compile()} - Template compiler
|
||||
|
||||
## Angular Scope API
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Advanced Form
|
||||
@description
|
||||
@@ -9,9 +8,7 @@ detection, and preventing invalid form submission.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
UserForm.$inject = ['$invalidWidgets'];
|
||||
function UserForm($invalidWidgets){
|
||||
this.$invalidWidgets = $invalidWidgets;
|
||||
function UserForm() {
|
||||
this.state = /^\w\w$/;
|
||||
this.zip = /^\d\d\d\d\d$/;
|
||||
this.master = {
|
||||
@@ -30,11 +27,11 @@ detection, and preventing invalid form submission.
|
||||
}
|
||||
|
||||
UserForm.prototype = {
|
||||
cancel: function(){
|
||||
cancel: function() {
|
||||
this.form = angular.copy(this.master);
|
||||
},
|
||||
|
||||
save: function(){
|
||||
save: function() {
|
||||
this.master = this.form;
|
||||
this.cancel();
|
||||
}
|
||||
@@ -42,31 +39,34 @@ detection, and preventing invalid form submission.
|
||||
</script>
|
||||
<div ng:controller="UserForm">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="form.name" ng:required/> <br/><br/>
|
||||
<form name="myForm">
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="form.address.line1" size="33" ng:required/> <br/>
|
||||
<input type="text" name="form.address.city" size="12" ng:required/>,
|
||||
<input type="text" name="form.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="form.address.zip" size="5" ng:required
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" ng:model="form.name" required/> <br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select name="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" name="contact.value" ng:required/>
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng:click="cancel()" ng:disabled="{{master.$equals(form)}}">Cancel</button>
|
||||
<button ng:click="save()" ng:disabled="{{$invalidWidgets.visible() ||
|
||||
master.$equals(form)}}">Save</button>
|
||||
<label>Address:</label> <br/>
|
||||
<input type="text" ng:model="form.address.line1" size="33" required/> <br/>
|
||||
<input type="text" ng:model="form.address.city" size="12" required/>,
|
||||
<input type="text" ng:model="form.address.state" size="2"
|
||||
ng:pattern="state" required/>
|
||||
<input type="text" ng:model="form.address.zip" size="5"
|
||||
ng:pattern="zip" required/><br/><br/>
|
||||
|
||||
<label>Contacts:</label>
|
||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select ng:model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" ng:model="contact.value" required/>
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<button ng:click="cancel()" ng:disabled="{{master.$equals(form)}}">Cancel</button>
|
||||
<button ng:click="save()" ng:disabled="{{myForm.$invalid || master.$equals(form)}}">Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
@@ -75,7 +75,7 @@ master.$equals(form)}}">Save</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function(){
|
||||
it('should enable save button', function() {
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
@@ -84,13 +84,13 @@ master.$equals(form)}}">Save</button>
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function(){
|
||||
it('should enable cancel button', function() {
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.name').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[name="form.name"]').val()).toEqual('John Smith');
|
||||
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Resources - Buzz
|
||||
@description
|
||||
@@ -15,6 +14,7 @@ to retrieve Buzz activity and comments.
|
||||
<script>
|
||||
BuzzController.$inject = ['$resource'];
|
||||
function BuzzController($resource) {
|
||||
this.userId = 'googlebuzz';
|
||||
this.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt: 'json', callback: 'JSON_CALLBACK'},
|
||||
@@ -32,7 +32,7 @@ to retrieve Buzz activity and comments.
|
||||
};
|
||||
</script>
|
||||
<div ng:controller="BuzzController">
|
||||
<input name="userId" value="googlebuzz"/>
|
||||
<input ng:model="userId"/>
|
||||
<button ng:click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div class="buzz" ng:repeat="item in activities.data.items">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Deep Linking
|
||||
@description
|
||||
@@ -38,7 +37,7 @@ The two partials are defined in the following URLs:
|
||||
* <a href="./examples/welcome.html" ng:ext-link>./examples/welcome.html</a>
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
AppCntl.$inject = ['$route']
|
||||
function AppCntl($route) {
|
||||
@@ -56,7 +55,7 @@ The two partials are defined in the following URLs:
|
||||
|
||||
function WelcomeCntl($route){}
|
||||
WelcomeCntl.prototype = {
|
||||
greet: function(){
|
||||
greet: function() {
|
||||
alert("Hello " + this.person.name);
|
||||
}
|
||||
};
|
||||
@@ -67,11 +66,11 @@ The two partials are defined in the following URLs:
|
||||
this.cancel();
|
||||
}
|
||||
SettingsCntl.prototype = {
|
||||
cancel: function(){
|
||||
cancel: function() {
|
||||
this.form = angular.copy(this.person);
|
||||
},
|
||||
|
||||
save: function(){
|
||||
save: function() {
|
||||
angular.copy(this.form, this.person);
|
||||
this.$location.path('/welcome');
|
||||
}
|
||||
@@ -89,7 +88,7 @@ The two partials are defined in the following URLs:
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should navigate to URL', function(){
|
||||
it('should navigate to URL', function() {
|
||||
element('a:contains(Welcome)').click();
|
||||
expect(element('ng\\:view').text()).toMatch(/Hello anonymous/);
|
||||
element('a:contains(Settings)').click();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Form
|
||||
@description
|
||||
@@ -11,7 +10,7 @@ allow a user to enter data.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function FormController(){
|
||||
function FormController() {
|
||||
this.user = {
|
||||
name: 'John Smith',
|
||||
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
|
||||
@@ -24,25 +23,26 @@ allow a user to enter data.
|
||||
<div ng:controller="FormController" class="example">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="user.name" ng:required/> <br/><br/>
|
||||
<input type="text" ng:model="user.name" required/> <br/><br/>
|
||||
|
||||
<label>Address:</label><br/>
|
||||
<input type="text" name="user.address.line1" size="33" ng:required/> <br/>
|
||||
<input type="text" name="user.address.city" size="12" ng:required/>,
|
||||
<input type="text" name="user.address.state" size="2" ng:required ng:validate="regexp:state"/>
|
||||
<input type="text" name="user.address.zip" size="5" ng:required
|
||||
ng:validate="regexp:zip"/><br/><br/>
|
||||
<input type="text" ng:model="user.address.line1" size="33" required> <br/>
|
||||
<input type="text" ng:model="user.address.city" size="12" required>,
|
||||
<input type="text" ng:model="user.address.state" size="2"
|
||||
ng:pattern="state" required>
|
||||
<input type="text" ng:model="user.address.zip" size="5"
|
||||
ng:pattern="zip" required><br/><br/>
|
||||
|
||||
<label>Phone:</label>
|
||||
[ <a href="" ng:click="user.contacts.$add()">add</a> ]
|
||||
<div ng:repeat="contact in user.contacts">
|
||||
<select name="contact.type">
|
||||
<select ng:model="contact.type">
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
<option>pager</option>
|
||||
<option>IM</option>
|
||||
</select>
|
||||
<input type="text" name="contact.value" ng:required/>
|
||||
<input type="text" ng:model="contact.value" required/>
|
||||
[ <a href="" ng:click="user.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<hr/>
|
||||
@@ -52,35 +52,37 @@ ng:validate="regexp:zip"/><br/><br/>
|
||||
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show debug', function(){
|
||||
it('should show debug', function() {
|
||||
expect(binding('user')).toMatch(/John Smith/);
|
||||
});
|
||||
it('should add contact', function(){
|
||||
it('should add contact', function() {
|
||||
using('.example').element('a:contains(add)').click();
|
||||
using('.example div:last').input('contact.value').enter('you@example.org');
|
||||
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
|
||||
expect(binding('user')).toMatch(/you@example.org/);
|
||||
});
|
||||
|
||||
it('should remove contact', function(){
|
||||
it('should remove contact', function() {
|
||||
using('.example').element('a:contains(X)').click();
|
||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
||||
});
|
||||
|
||||
it('should validate zip', function(){
|
||||
expect(using('.example').element(':input[name="user.address.zip"]').prop('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
it('should validate zip', function() {
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.zip').enter('abc');
|
||||
expect(using('.example').element(':input[name="user.address.zip"]').prop('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
expect(using('.example').
|
||||
element(':input[ng\\:model="user.address.zip"]').
|
||||
prop('className')).toMatch(/ng-invalid/);
|
||||
});
|
||||
|
||||
it('should validate state', function(){
|
||||
expect(using('.example').element(':input[name="user.address.state"]').prop('className'))
|
||||
.not().toMatch(/ng-validation-error/);
|
||||
it('should validate state', function() {
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.not().toMatch(/ng-invalid/);
|
||||
using('.example').input('user.address.state').enter('XXX');
|
||||
expect(using('.example').element(':input[name="user.address.state"]').prop('className'))
|
||||
.toMatch(/ng-validation-error/);
|
||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
||||
.toMatch(/ng-invalid/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -93,8 +95,8 @@ available in
|
||||
the {@link api/angular.scope scope} with the initial data.
|
||||
* For debugging purposes we have included a debug view of the model to better understand what
|
||||
is going on.
|
||||
* The {@link api/angular.widget.HTML input widgets} simply refer to the model and are auto bound.
|
||||
* The inputs {@link api/angular.validator validate}. (Try leaving them blank or entering non digits
|
||||
* The {@link api/angular.widget.input input widgets} simply refer to the model and are data-bound.
|
||||
* The inputs {@link guide/dev_guide.forms validate}. (Try leaving them blank or entering non digits
|
||||
in the zip field)
|
||||
* In your application you can simply read from or write to the model and the form will be updated.
|
||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: Hello World
|
||||
@description
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="name" value="World"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
<script>
|
||||
function HelloCntl() {
|
||||
this.name = 'World';
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="HelloCntl">
|
||||
Your name: <input type="text" ng:model="name" value="World"/>
|
||||
<hr/>
|
||||
Hello {{name}}!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should change the binding when user enters text', function(){
|
||||
it('should change the binding when user enters text', function() {
|
||||
expect(binding('name')).toEqual('World');
|
||||
input('name').enter('angular');
|
||||
expect(binding('name')).toEqual('angular');
|
||||
@@ -23,7 +29,7 @@
|
||||
Take a look through the source and note:
|
||||
|
||||
* The script tag that {@link guide/dev_guide.bootstrap bootstraps} the angular environment.
|
||||
* The text {@link api/angular.widget.HTML input widget} which is bound to the greeting name text.
|
||||
* The text {@link api/angular.widget.input input widget} which is bound to the greeting name text.
|
||||
* No need for listener registration and event firing on change events.
|
||||
* The implicit presence of the `name` variable which is in the root {@link api/angular.scope scope}.
|
||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Cookbook: MVC
|
||||
@description
|
||||
@@ -36,7 +35,7 @@ no connection between the controller and the view.
|
||||
this.setUrl();
|
||||
}
|
||||
},
|
||||
reset: function(){
|
||||
reset: function() {
|
||||
this.board = [
|
||||
['', '', ''],
|
||||
['', '', ''],
|
||||
@@ -46,7 +45,7 @@ no connection between the controller and the view.
|
||||
this.winner = '';
|
||||
this.setUrl();
|
||||
},
|
||||
grade: function(){
|
||||
grade: function() {
|
||||
var b = this.board;
|
||||
this.winner =
|
||||
row(0) || row(1) || row(2) ||
|
||||
@@ -57,7 +56,7 @@ no connection between the controller and the view.
|
||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
||||
},
|
||||
setUrl: function(){
|
||||
setUrl: function() {
|
||||
var rows = [];
|
||||
angular.forEach(this.board, function(row){
|
||||
rows.push(row.join(','));
|
||||
@@ -91,7 +90,7 @@ no connection between the controller and the view.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function(){
|
||||
it('should play a game', function() {
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular: Automatic Initiialization
|
||||
@name Developer Guide: Initializing Angular: Automatic Initialization
|
||||
@description
|
||||
|
||||
Angular initializes automatically when you load the angular script into your page, specifying
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular: Manual Initialization
|
||||
@description
|
||||
@@ -18,7 +17,7 @@ explicitly.
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
<script>
|
||||
angular.element(document).ready(function() {
|
||||
angular.compile(document)();
|
||||
angular.compile(document)().$apply();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Initializing Angular
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Directives: Creating Custom Angular Directives
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Directives
|
||||
@description
|
||||
@@ -16,7 +15,7 @@ directives per element.
|
||||
You add angular directives to a standard HTML tag as in the following example, in which we have
|
||||
added the {@link api/angular.directive.ng:click ng:click} directive to a button tag:
|
||||
|
||||
<button name="button1" ng:click="foo()">Click This</button>
|
||||
<button ng:model="button1" ng:click="foo()">Click This</button>
|
||||
|
||||
In the example above, `name` is the standard HTML attribute, and `ng:click` is the angular
|
||||
directive. The `ng:click` directive lets you implement custom behavior in an associated controller
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Comparing Directives and Attribute Widgets
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Extending the Angular Compiler
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Markup
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Testing a New DOM Element
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Widgets: Creating Custom Widgets
|
||||
@description
|
||||
@@ -61,7 +60,7 @@ angular.widget('@my:watch', function(expression, compileElement) {
|
||||
angular.widget('my:time', function(compileElement){
|
||||
compileElement.css('display', 'block');
|
||||
return function(linkElement){
|
||||
function update(){
|
||||
function update() {
|
||||
linkElement.text('Current time is: ' + new Date());
|
||||
setTimeout(update, 1000);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular HTML Compiler: Understanding Angular Widgets
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About Dependency Injection (DI)
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: DI: Understanding DI in Angular
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: DI: Using DI in Controllers
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Understanding Angular Expressions
|
||||
@description
|
||||
@@ -41,7 +40,7 @@ the `Scope:$eval()` method.
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
it('should calculate expression in binding', function() {
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</doc:scenario>
|
||||
@@ -51,9 +50,15 @@ You can try evaluating different expressions here:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div ng:init="exprs=[]" class="expressions">
|
||||
<script>
|
||||
function Cntl2() {
|
||||
this.exprs = [];
|
||||
this.expr = '3*10|currency';
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="Cntl2" class="expressions">
|
||||
Expression:
|
||||
<input type='text' name="expr" value="3*10|currency" size="80"/>
|
||||
<input type='text' ng:model="expr" size="80"/>
|
||||
<button ng:click="exprs.$add(expr)">Evaluate</button>
|
||||
<ul>
|
||||
<li ng:repeat="expr in exprs">
|
||||
@@ -64,7 +69,7 @@ You can try evaluating different expressions here:
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should allow user expression testing', function(){
|
||||
it('should allow user expression testing', function() {
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
@@ -84,13 +89,22 @@ the global state (a common source of subtle bugs).
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<div class="example2" ng:init="$window = $service('$window')">
|
||||
Name: <input name="name" type="text" value="World"/>
|
||||
<button ng:click="($window.mockWindow || $window).alert('Hello ' + name)">Greet</button>
|
||||
<script>
|
||||
function Cntl1($window){
|
||||
this.name = 'World';
|
||||
|
||||
this.greet = function() {
|
||||
($window.mockWindow || $window).alert('Hello ' + this.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="example2" ng:controller="Cntl1">
|
||||
Name: <input ng:model="name" type="text"/>
|
||||
<button ng:click="greet()">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function(){
|
||||
it('should calculate expression in binding', function() {
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
@@ -158,7 +172,7 @@ Extensions: You can further extend the expression vocabulary by adding new metho
|
||||
{name:'Mike', phone:'555-4321'},
|
||||
{name:'Adam', phone:'555-5678'},
|
||||
{name:'Julie', phone:'555-8765'}]"></div>
|
||||
Search: <input name="searchText"/>
|
||||
Search: <input ng:model="searchText"/>
|
||||
<table class="example3">
|
||||
<tr><th>Name</th><th>Phone</th><tr>
|
||||
<tr ng:repeat="friend in friends.$filter(searchText)">
|
||||
@@ -168,7 +182,7 @@ Extensions: You can further extend the expression vocabulary by adding new metho
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should filter the list', function(){
|
||||
it('should filter the list', function() {
|
||||
var tr = using('table.example3').repeater('tr.ng-attr-widget');
|
||||
expect(tr.count()).toBe(5);
|
||||
input('searchText').enter('a');
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Forms
|
||||
@description
|
||||
|
||||
# Overview
|
||||
|
||||
Forms allow users to enter data into your application. Forms represent the bidirectional data
|
||||
bindings in Angular.
|
||||
|
||||
Forms consist of all of the following:
|
||||
|
||||
- the individual widgets with which users interact
|
||||
- the validation rules for widgets
|
||||
- the form, a collection of widgets that contains aggregated validation information
|
||||
|
||||
|
||||
# Form
|
||||
|
||||
A form groups a set of widgets together into a single logical data-set. A form is created using
|
||||
the {@link api/angular.widget.form <form>} element that calls the
|
||||
{@link api/angular.service.$formFactory $formFactory} service. The form is responsible for managing
|
||||
the widgets and for tracking validation information.
|
||||
|
||||
A form is:
|
||||
|
||||
- The collection which contains widgets or other forms.
|
||||
- Responsible for marshaling data from the model into a widget. This is
|
||||
triggered by {@link api/angular.scope.$watch $watch} of the model expression.
|
||||
- Responsible for marshaling data from the widget into the model. This is
|
||||
triggered by the widget emitting the `$viewChange` event.
|
||||
- Responsible for updating the validation state of the widget, when the widget emits
|
||||
`$valid` / `$invalid` event. The validation state is useful for controlling the validation
|
||||
errors shown to the user in it consist of:
|
||||
|
||||
- `$valid` / `$invalid`: Complementary set of booleans which show if a widget is valid / invalid.
|
||||
- `$error`: an object which has a property for each validation key emited by the widget.
|
||||
The value of the key is always true. If widget is valid, then the `$error`
|
||||
object has no properties. For example if the widget emits
|
||||
`$invalid` event with `REQUIRED` key. The internal state of the `$error` would be
|
||||
updated to `$error.REQUIRED == true`.
|
||||
|
||||
- Responsible for aggregating widget validation information into the form.
|
||||
|
||||
- `$valid` / `$invalid`: Complementary set of booleans which show if all the child widgets
|
||||
(or forms) are valid or if any are invalid.
|
||||
- `$error`: an object which has a property for each validation key emited by the
|
||||
child widget. The value of the key is an array of widgets which fired the invalid
|
||||
event. If all child widgets are valid then, then the `$error` object has no
|
||||
properties. For example if a child widget emits
|
||||
`$invalid` event with `REQUIRED` key. The internal state of the `$error` would be
|
||||
updated to `$error.REQUIRED == [ widgetWhichEmitedInvalid ]`.
|
||||
|
||||
|
||||
# Widgets
|
||||
|
||||
In Angular, a widget is the term used for the UI with which the user input. Examples of
|
||||
bult-in Angular widgets are {@link api/angular.widget.input input} and
|
||||
{@link api/angular.widget.select select}. Widgets provide the rendering and the user
|
||||
interaction logic. Widgets should be declared inside a form, if no form is provided an implicit
|
||||
form {@link api/angular.service.$formFactory $formFactory.rootForm} form is used.
|
||||
|
||||
Widgets are implemented as Angular controllers. A widget controller:
|
||||
|
||||
- implements methods:
|
||||
|
||||
- `$render` - Updates the DOM from the internal state as represented by `$viewValue`.
|
||||
- `$parseView` - Translate `$viewValue` to `$modelValue`. (`$modelValue` will be assigned to
|
||||
the model scope by the form)
|
||||
- `$parseModel` - Translate `$modelValue` to `$viewValue`. (`$viewValue` will be assigned to
|
||||
the DOM inside the `$render` method)
|
||||
|
||||
- responds to events:
|
||||
|
||||
- `$validate` - Emitted by the form when the form determines that the widget needs to validate
|
||||
itself. There may be more then one listener on the `$validate` event. The widget responds
|
||||
by emitting `$valid` / `$invalid` event of its own.
|
||||
|
||||
- emits events:
|
||||
|
||||
- `$viewChange` - Emitted when the user interacts with the widget and it is necessary to update
|
||||
the model.
|
||||
- `$valid` - Emitted when the widget determines that it is valid (usually as a response to
|
||||
`$validate` event or inside `$parseView()` or `$parseModel()` method).
|
||||
- `$invalid` - Emitted when the widget determines that it is invalid (usually as a response to
|
||||
`$validate` event or inside `$parseView()` or `$parseModel()` method).
|
||||
- `$destroy` - Emitted when the widget element is removed from the DOM.
|
||||
|
||||
|
||||
# CSS
|
||||
|
||||
Angular-defined widgets and forms set `ng-valid` and `ng-invalid` classes on themselves to allow
|
||||
the web-designer a way to style them. If you write your own widgets, then their `$render()`
|
||||
methods must set the appropriate CSS classes to allow styling.
|
||||
(See {@link dev_guide.templates.css-styling CSS})
|
||||
|
||||
|
||||
# Example
|
||||
|
||||
The following example demonstrates:
|
||||
|
||||
- How an error is displayed when a required field is empty.
|
||||
- Error highlighting.
|
||||
- How form submission is disabled when the form is invalid.
|
||||
- The internal state of the widget and form in the the 'Debug View' area.
|
||||
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<style>
|
||||
.ng-invalid { border: solid 1px red; }
|
||||
.ng-form {display: block;}
|
||||
</style>
|
||||
<script>
|
||||
function UserFormCntl() {
|
||||
this.state = /^\w\w$/;
|
||||
this.zip = /^\d\d\d\d\d$/;
|
||||
this.master = {
|
||||
customer: 'John Smith',
|
||||
address:{
|
||||
line1: '123 Main St.',
|
||||
city:'Anytown',
|
||||
state:'AA',
|
||||
zip:'12345'
|
||||
}
|
||||
};
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
UserFormCntl.prototype = {
|
||||
cancel: function() {
|
||||
this.form = angular.copy(this.master);
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.master = this.form;
|
||||
this.cancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<div ng:controller="UserFormCntl">
|
||||
|
||||
<form name="userForm">
|
||||
|
||||
<label>Name:</label><br/>
|
||||
<input type="text" name="customer" ng:model="form.customer" required/>
|
||||
<span class="error" ng:show="userForm.customer.$error.REQUIRED">
|
||||
Customer name is required!</span>
|
||||
<br/><br/>
|
||||
|
||||
<ng:form name="addressForm">
|
||||
<label>Address:</label> <br/>
|
||||
<input type="text" name="line1" size="33" required
|
||||
ng:model="form.address.line1"/> <br/>
|
||||
<input type="text" name="city" size="12" required
|
||||
ng:model="form.address.city"/>,
|
||||
<input type="text" name="state" ng:pattern="state" size="2" required
|
||||
ng:model="form.address.state"/>
|
||||
<input type="text" name="zip" ng:pattern="zip" size="5" required
|
||||
ng:model="form.address.zip"/><br/><br/>
|
||||
|
||||
<span class="error" ng:show="addressForm.$invalid">
|
||||
Incomplete address:
|
||||
<span class="error" ng:show="addressForm.state.$error.REQUIRED">
|
||||
Missing state!</span>
|
||||
<span class="error" ng:show="addressForm.state.$error.PATTERN">
|
||||
Invalid state!</span>
|
||||
<span class="error" ng:show="addressForm.zip.$error.REQUIRED">
|
||||
Missing zip!</span>
|
||||
<span class="error" ng:show="addressForm.zip.$error.PATTERN">
|
||||
Invalid zip!</span>
|
||||
</span>
|
||||
</ng:form>
|
||||
|
||||
<button ng:click="cancel()"
|
||||
ng:disabled="{{master.$equals(form)}}">Cancel</button>
|
||||
<button ng:click="save()"
|
||||
ng:disabled="{{userForm.$invalid || master.$equals(form)}}">
|
||||
Save</button>
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
Debug View:
|
||||
<pre>form={{form}}</pre>
|
||||
<pre>master={{master}}</pre>
|
||||
<pre>userForm={{userForm}}</pre>
|
||||
<pre>addressForm={{addressForm}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enable save button', function() {
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.customer').enter('');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
input('form.customer').enter('change');
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Save)').click();
|
||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
||||
});
|
||||
it('should enable cancel button', function() {
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
input('form.customer').enter('change');
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
||||
element(':button:contains(Cancel)').click();
|
||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
||||
expect(element(':input[ng\\:model="form.customer"]').val()).toEqual('John Smith');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# Life-cycle
|
||||
|
||||
- The `<form>` element triggers creation of a new form {@link dev_guide.scopes scope} using the
|
||||
{@link api/angular.service.$formFactory $formfactory}. The new form scope is added to the
|
||||
`<form>` element using the jQuery `.data()` method for later retrieval under the key `$form`.
|
||||
The form also sets up these listeners:
|
||||
|
||||
- `$destroy` - This event is emitted by nested widget when it is removed from the view. It gives
|
||||
the form a chance to clean up any validation references to the destroyed widget.
|
||||
- `$valid` / `$invalid` - This event is emitted by the widget on validation state change.
|
||||
|
||||
- `<input>` element triggers the creation of the widget using the
|
||||
{@link api/angular.service.$formFactory $formfactory.$createWidget()} method. The `$createWidget()`
|
||||
creates new widget instance by calling the current scope {@link api/angular.scope.$new .$new()} and
|
||||
registers these listeners:
|
||||
|
||||
- `$watch` on the model scope.
|
||||
- `$viewChange` event on the widget scope.
|
||||
- `$validate` event on the widget scope.
|
||||
- Element `change` event when the user enters data.
|
||||
|
||||
<img class="center" src="img/form_data_flow.png" border="1" />
|
||||
|
||||
|
||||
- When the user interacts with the widget:
|
||||
|
||||
1. The DOM element fires the `change` event which the widget intercepts. Widget then emits
|
||||
a `$viewChange` event which includes the new user-entered value. (Remember that the DOM events
|
||||
are outside of the Angular environment so the widget must emit its event within the
|
||||
{@link api/angular.scope.$apply $apply} method).
|
||||
2. The form's `$viewChange` listener copies the user-entered value to the widget's `$viewValue`
|
||||
property. Since the `$viewValue` is the raw value as entered by user, it may need to be
|
||||
translated to a different format/type (for example, translating a string to a number).
|
||||
If you need your widget to translate between the internal `$viewValue` and the external
|
||||
`$modelValue` state, you must declare a `$parseView()` method. The `$parseView()` method
|
||||
will copy `$viewValue` to `$modelValue` and perform any necessary translations.
|
||||
3. The `$modelValue` is written into the application model.
|
||||
4. The form then emits a `$validate` event, giving the widget's validators chance to validate the
|
||||
input. There can be any number of validators registered. Each validator may in turn
|
||||
emit a `$valid` / `$invalid` event with the validator's validation key. For example `REQUIRED`.
|
||||
5. Form listens to `$valid`/`$invalid` events and updates both the form as well as the widget
|
||||
scope with the validation state. The validation updates the `$valid` and `$invalid`, property
|
||||
as well as `$error` object. The widget's `$error` object is updated with the validation key
|
||||
such that `$error.REQUIRED == true` when the validation emits `$invalid` with `REQUIRED`
|
||||
validation key. Similarly the form's `$error` object gets updated, but instead of boolean
|
||||
`true` it contains an array of invalid widgets (widgets which fired `$invalid` event with
|
||||
`REQUIRED` validation key).
|
||||
|
||||
- When the model is updated:
|
||||
|
||||
1. The model `$watch` listener assigns the model value to `$modelValue` on the widget.
|
||||
2. The form then calls `$parseModel` method on widget if present. The method converts the
|
||||
value to renderable format and assigns it to `$viewValue` (for example converting number to a
|
||||
string.)
|
||||
3. The form then emits a `$validate` which behaves as described above.
|
||||
4. The form then calls `$render` method on the widget to update the DOM structure from the
|
||||
`$viewValue`.
|
||||
|
||||
|
||||
|
||||
# Writing Your Own Widget
|
||||
|
||||
This example shows how to implement a custom HTML editor widget in Angular.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function EditorCntl() {
|
||||
this.htmlContent = '<b>Hello</b> <i>World</i>!';
|
||||
}
|
||||
|
||||
function HTMLEditorWidget(element) {
|
||||
var self = this;
|
||||
var htmlFilter = angular.filter('html');
|
||||
|
||||
this.$parseModel = function() {
|
||||
// need to protect for script injection
|
||||
try {
|
||||
this.$viewValue = htmlFilter(
|
||||
this.$modelValue || '').get();
|
||||
if (this.$error.HTML) {
|
||||
// we were invalid, but now we are OK.
|
||||
this.$emit('$valid', 'HTML');
|
||||
}
|
||||
} catch (e) {
|
||||
// if HTML not parsable invalidate form.
|
||||
this.$emit('$invalid', 'HTML');
|
||||
}
|
||||
}
|
||||
|
||||
this.$render = function() {
|
||||
element.html(this.$viewValue);
|
||||
}
|
||||
|
||||
element.bind('keyup', function() {
|
||||
self.$apply(function() {
|
||||
self.$emit('$viewChange', element.html());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
angular.directive('ng:html-editor-model', function() {
|
||||
function linkFn($formFactory, element) {
|
||||
var exp = element.attr('ng:html-editor-model'),
|
||||
form = $formFactory.forElement(element),
|
||||
widget;
|
||||
element.attr('contentEditable', true);
|
||||
widget = form.$createWidget({
|
||||
scope: this,
|
||||
model: exp,
|
||||
controller: HTMLEditorWidget,
|
||||
controllerArgs: [element]});
|
||||
// if the element is destroyed, then we need to
|
||||
// notify the form.
|
||||
element.bind('$destroy', function() {
|
||||
widget.$destroy();
|
||||
});
|
||||
}
|
||||
linkFn.$inject = ['$formFactory'];
|
||||
return linkFn;
|
||||
});
|
||||
</script>
|
||||
<form name='editorForm' ng:controller="EditorCntl">
|
||||
<div ng:html-editor-model="htmlContent"></div>
|
||||
<hr/>
|
||||
HTML: <br/>
|
||||
<textarea ng:model="htmlContent" cols="80"></textarea>
|
||||
<hr/>
|
||||
<pre>editorForm = {{editorForm}}</pre>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should enter invalid HTML', function() {
|
||||
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-valid/);
|
||||
input('htmlContent').enter('<');
|
||||
expect(element('form[name=editorForm]').prop('className')).toMatch(/ng-invalid/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
||||
# HTML Inputs
|
||||
|
||||
The most common widgets you will use will be in the form of the
|
||||
standard HTML set. These widgets are bound using the `name` attribute
|
||||
to an expression. In addition, they can have `required` attribute to further control their
|
||||
validation.
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl() {
|
||||
this.input1 = '';
|
||||
this.input2 = '';
|
||||
this.input3 = 'A';
|
||||
this.input4 = false;
|
||||
this.input5 = 'c';
|
||||
this.input6 = [];
|
||||
}
|
||||
</script>
|
||||
<table style="font-size:.9em;" ng:controller="Ctrl">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>HTML</th>
|
||||
<th>UI</th>
|
||||
<th ng:non-bindable>{{input#}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>text</th>
|
||||
<td>String</td>
|
||||
<td><tt><input type="text" ng:model="input1"></tt></td>
|
||||
<td><input type="text" ng:model="input1" size="4"></td>
|
||||
<td><tt>{{input1|json}}</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>textarea</th>
|
||||
<td>String</td>
|
||||
<td><tt><textarea ng:model="input2"></textarea></tt></td>
|
||||
<td><textarea ng:model="input2" cols='6'></textarea></td>
|
||||
<td><tt>{{input2|json}}</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>radio</th>
|
||||
<td>String</td>
|
||||
<td><tt>
|
||||
<input type="radio" ng:model="input3" value="A"><br>
|
||||
<input type="radio" ng:model="input3" value="B">
|
||||
</tt></td>
|
||||
<td>
|
||||
<input type="radio" ng:model="input3" value="A">
|
||||
<input type="radio" ng:model="input3" value="B">
|
||||
</td>
|
||||
<td><tt>{{input3|json}}</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>checkbox</th>
|
||||
<td>Boolean</td>
|
||||
<td><tt><input type="checkbox" ng:model="input4"></tt></td>
|
||||
<td><input type="checkbox" ng:model="input4"></td>
|
||||
<td><tt>{{input4|json}}</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>pulldown</th>
|
||||
<td>String</td>
|
||||
<td><tt>
|
||||
<select ng:model="input5"><br>
|
||||
<option value="c">C</option><br>
|
||||
<option value="d">D</option><br>
|
||||
</select><br>
|
||||
</tt></td>
|
||||
<td>
|
||||
<select ng:model="input5">
|
||||
<option value="c">C</option>
|
||||
<option value="d">D</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><tt>{{input5|json}}</tt></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>multiselect</th>
|
||||
<td>Array</td>
|
||||
<td><tt>
|
||||
<select ng:model="input6" multiple size="4"><br>
|
||||
<option value="e">E</option><br>
|
||||
<option value="f">F</option><br>
|
||||
</select><br>
|
||||
</tt></td>
|
||||
<td>
|
||||
<select ng:model="input6" multiple size="4">
|
||||
<option value="e">E</option>
|
||||
<option value="f">F</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><tt>{{input6|json}}</tt></td>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
|
||||
it('should exercise text', function() {
|
||||
input('input1').enter('Carlos');
|
||||
expect(binding('input1')).toEqual('"Carlos"');
|
||||
});
|
||||
it('should exercise textarea', function() {
|
||||
input('input2').enter('Carlos');
|
||||
expect(binding('input2')).toEqual('"Carlos"');
|
||||
});
|
||||
it('should exercise radio', function() {
|
||||
expect(binding('input3')).toEqual('"A"');
|
||||
input('input3').select('B');
|
||||
expect(binding('input3')).toEqual('"B"');
|
||||
input('input3').select('A');
|
||||
expect(binding('input3')).toEqual('"A"');
|
||||
});
|
||||
it('should exercise checkbox', function() {
|
||||
expect(binding('input4')).toEqual('false');
|
||||
input('input4').check();
|
||||
expect(binding('input4')).toEqual('true');
|
||||
});
|
||||
it('should exercise pulldown', function() {
|
||||
expect(binding('input5')).toEqual('"c"');
|
||||
select('input5').option('d');
|
||||
expect(binding('input5')).toEqual('"d"');
|
||||
});
|
||||
it('should exercise multiselect', function() {
|
||||
expect(binding('input6')).toEqual('[]');
|
||||
select('input6').options('e');
|
||||
expect(binding('input6')).toEqual('["e"]');
|
||||
select('input6').options('e', 'f');
|
||||
expect(binding('input6')).toEqual('["e","f"]');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
#Testing
|
||||
|
||||
When unit-testing a controller it may be desirable to have a reference to form and to simulate
|
||||
different form validation states.
|
||||
|
||||
This example demonstrates a login form, where the login button is enabled only when the form is
|
||||
properly filled out.
|
||||
<pre>
|
||||
<div ng:controller="LoginController">
|
||||
<form name="loginForm">
|
||||
<input type="text" ng:model="username" required/>
|
||||
<input type="password" ng:model="password" required/>
|
||||
<button ng:disabled="{{!disableLogin()}}" ng:click="login()">Login</login>
|
||||
</form>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
In the unit tests we do not have access to the DOM, and therefore the `loginForm` reference does
|
||||
not get set on the controller. This example shows how it can be unit-tested, by creating a mock
|
||||
form.
|
||||
<pre>
|
||||
function LoginController() {
|
||||
this.disableLogin = function() {
|
||||
return this.loginForm.$invalid;
|
||||
};
|
||||
}
|
||||
|
||||
describe('LoginController', function() {
|
||||
it('should disable login button when form is invalid', function() {
|
||||
var scope = angular.scope();
|
||||
var loginController = scope.$new(LoginController);
|
||||
|
||||
// In production the 'loginForm' form instance gets set from the view,
|
||||
// but in unit-test we have to set it manually.
|
||||
loginController.loginForm = scope.$service('$formFactory')();
|
||||
|
||||
expect(loginController.disableLogin()).toBe(false);
|
||||
|
||||
// Now simulate an invalid form
|
||||
loginController.loginForm.$emit('$invalid', 'MyReason');
|
||||
expect(loginController.disableLogin()).toBe(true);
|
||||
|
||||
// Now simulate a valid form
|
||||
loginController.loginForm.$emit('$valid', 'MyReason');
|
||||
expect(loginController.disableLogin()).toBe(false);
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
## Custom widgets
|
||||
|
||||
This example demonstrates a login form, where the password has custom validation rules.
|
||||
<pre>
|
||||
<div ng:controller="LoginController">
|
||||
<form name="loginForm">
|
||||
<input type="text" ng:model="username" required/>
|
||||
<input type="@StrongPassword" ng:model="password" required/>
|
||||
<button ng:disabled="{{!disableLogin()}}" ng:click="login()">Login</login>
|
||||
</form>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
In the unit tests we do not have access to the DOM, and therefore the `loginForm` and custom
|
||||
input type reference does not get set on the controller. This example shows how it can be
|
||||
unit-tested, by creating a mock form and a mock custom input type.
|
||||
<pre>
|
||||
function LoginController(){
|
||||
this.disableLogin = function() {
|
||||
return this.loginForm.$invalid;
|
||||
};
|
||||
|
||||
this.StrongPassword = function(element) {
|
||||
var widget = this;
|
||||
element.attr('type', 'password'); // act as password.
|
||||
this.$on('$validate', function(){
|
||||
widget.$emit(widget.$viewValue.length > 5 ? '$valid' : '$invalid', 'PASSWORD');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
describe('LoginController', function() {
|
||||
it('should disable login button when form is invalid', function() {
|
||||
var scope = angular.scope();
|
||||
var loginController = scope.$new(LoginController);
|
||||
var input = angular.element('<input>');
|
||||
|
||||
// In production the 'loginForm' form instance gets set from the view,
|
||||
// but in unit-test we have to set it manually.
|
||||
loginController.loginForm = scope.$service('$formFactory')();
|
||||
|
||||
// now instantiate a custom input type
|
||||
loginController.loginForm.$createWidget({
|
||||
scope: loginController,
|
||||
model: 'password',
|
||||
alias: 'password',
|
||||
controller: loginController.StrongPassword,
|
||||
controllerArgs: [input]
|
||||
});
|
||||
|
||||
// Verify that the custom password input type sets the input type to password
|
||||
expect(input.attr('type')).toEqual('password');
|
||||
|
||||
expect(loginController.disableLogin()).toBe(false);
|
||||
|
||||
// Now simulate an invalid form
|
||||
loginController.loginForm.password.$emit('$invalid', 'PASSWORD');
|
||||
expect(loginController.disableLogin()).toBe(true);
|
||||
|
||||
// Now simulate a valid form
|
||||
loginController.loginForm.password.$emit('$valid', 'PASSWORD');
|
||||
expect(loginController.disableLogin()).toBe(false);
|
||||
|
||||
// Changing model state, should also influence the form validity
|
||||
loginController.password = 'abc'; // too short so it should be invalid
|
||||
scope.$digest();
|
||||
expect(loginController.loginForm.password.$invalid).toBe(true);
|
||||
|
||||
// Changeing model state, should also influence the form validity
|
||||
loginController.password = 'abcdef'; // should be valid
|
||||
scope.$digest();
|
||||
expect(loginController.loginForm.password.$valid).toBe(true);
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Introduction
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular
|
||||
@description
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@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
|
||||
In angular, a controller is a JavaScript function(type/class) that is used to augment instances of
|
||||
angular {@link dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new
|
||||
child scope object via the {@link api/angular.scope.$new scope.$new} API , there is an
|
||||
option to pass in a controller as a method argument. This will tell angular to associate the
|
||||
@@ -68,7 +67,7 @@ Putting any presentation logic into controllers significantly affects testabilit
|
||||
logic. Angular offers {@link dev_guide.templates.databinding} for automatic DOM manipulation. If
|
||||
you have to perform your own manual DOM manipulation, encapsulate the presentation logic in {@link
|
||||
dev_guide.compiler.widgets widgets} and {@link dev_guide.compiler.directives directives}.
|
||||
- Input formatting — Use {@link dev_guide.templates.formatters angular formatters} instead.
|
||||
- Input formatting — Use {@link dev_guide.forms angular form widgets} instead.
|
||||
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
|
||||
- Run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
|
||||
services} instead.
|
||||
@@ -128,7 +127,7 @@ starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
controller augments.
|
||||
- Assigning a property to `this` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method) or
|
||||
as prototype methods of the controller constructor function (the `jalapenoSpicy` method)
|
||||
as prototype methods of the controller constructor function(the `jalapenoSpicy` method)
|
||||
- Both controller methods are available in the template (for the `body` element and and its
|
||||
children).
|
||||
|
||||
@@ -139,7 +138,7 @@ previous example.
|
||||
|
||||
<pre>
|
||||
<body ng:controller="SpicyCtrl">
|
||||
<input name="customSpice" value="wasabi">
|
||||
<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>
|
||||
@@ -227,7 +226,7 @@ Controller Test:
|
||||
<pre>
|
||||
describe('myController function', function() {
|
||||
|
||||
describe('myController', function(){
|
||||
describe('myController', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the Model Component
|
||||
@description
|
||||
@@ -41,7 +40,7 @@ when processing the following template constructs:
|
||||
|
||||
* Form input, select, textarea and other form elements:
|
||||
|
||||
<input name="query" value="fluffy cloud">
|
||||
<input ng:model="query" value="fluffy cloud">
|
||||
|
||||
The code above creates a model called "query" on the current scope with the value set to "fluffy
|
||||
cloud".
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: About MVC in Angular: Understanding the View Component
|
||||
@description
|
||||
|
||||
@@ -42,23 +42,31 @@ easier a web developer's life can if they're using angular:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<b>Invoice:</b>
|
||||
<br />
|
||||
<br />
|
||||
<table>
|
||||
<tr><td> </td><td> </td>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input name="qty" value="1" ng:validate="integer:0" ng:required /></td>
|
||||
<td><input name="cost" value="19.95" ng:validate="number" ng:required /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
<script>
|
||||
function InvoiceCntl() {
|
||||
this.qty = 1;
|
||||
this.cost = 19.95;
|
||||
}
|
||||
</script>
|
||||
<div ng:controller="InvoiceCntl">
|
||||
<b>Invoice:</b>
|
||||
<br />
|
||||
<br />
|
||||
<table>
|
||||
<tr><td> </td><td> </td>
|
||||
<tr><td>Quantity</td><td>Cost</td></tr>
|
||||
<tr>
|
||||
<td><input type="integer" min="0" ng:model="qty" required ></td>
|
||||
<td><input type="number" ng:model="cost" required ></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr />
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
</div>
|
||||
</doc:source>
|
||||
<!--
|
||||
<doc:scenario>
|
||||
it('should show of angular binding', function(){
|
||||
it('should show of angular binding', function() {
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
@@ -89,18 +97,18 @@ In the `<script>` tag we do two angular setup tasks:
|
||||
From the `name` attribute of the `<input>` tags, angular automatically sets up two-way data
|
||||
binding, and we also demonstrate some easy input validation:
|
||||
|
||||
Quantity: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
|
||||
Cost: <input name="cost" value="199.95" ng:validate="number" ng:required/>
|
||||
Quantity: <input type="integer" min="0" ng:model="qty" required >
|
||||
Cost: <input type="number" ng:model="cost" required >
|
||||
|
||||
These input widgets look normal enough, but consider these points:
|
||||
|
||||
* When this page loaded, angular bound the names of the input widgets (`qty` and `cost`) to
|
||||
variables of the same name. Think of those variables as the "Model" component of the
|
||||
Model-View-Controller design pattern.
|
||||
* Note the angular directives, {@link api/angular.widget.@ng:validate ng:validate} and {@link
|
||||
api/angular.widget.@ng:required ng:required}. You may have noticed that when you enter invalid data
|
||||
* Note the angular/HTML widget, {@link api/angular.widget.input input}.
|
||||
You may have noticed that when you enter invalid data
|
||||
or leave the the input fields blank, the borders turn red color, and the display value disappears.
|
||||
These `ng:` directives make it easier to implement field validators than coding them in JavaScript,
|
||||
These widgets make it easier to implement field validation than coding them in JavaScript,
|
||||
no? Yes.
|
||||
|
||||
And finally, the mysterious `{{ double curly braces }}`:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Using $location
|
||||
@description
|
||||
@@ -55,7 +54,7 @@ changes to $location are reflected into the browser address bar.
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="head">seamless integration with html5 API</td>
|
||||
<td class="head">seamless integration with HTML5 API</td>
|
||||
<td>no</td>
|
||||
<td>yes (with a fallback for legacy browsers)</td>
|
||||
</tr>
|
||||
@@ -89,11 +88,11 @@ setter methods that allow you to get or change the current URL in the browser.
|
||||
|
||||
## $location service configuration
|
||||
|
||||
To configure the `$location` service, you define the `$config` service which is an object with
|
||||
configuration properties:
|
||||
To configure the `$location` service, you define the `$locationConfig` service which is an object
|
||||
with configuration properties:
|
||||
|
||||
- **html5Mode**: {boolean}<br />
|
||||
`true` - see Html5 mode<br />
|
||||
`true` - see HTML5 mode<br />
|
||||
`false` - see Hashbang mode<br />
|
||||
default: `false`
|
||||
|
||||
@@ -103,7 +102,7 @@ configuration properties:
|
||||
|
||||
### Example configuration
|
||||
<pre>
|
||||
angular.service('$config', function() {
|
||||
angular.service('$locationConfig', function() {
|
||||
return {
|
||||
html5mode: true,
|
||||
hashPrefix: '!'
|
||||
@@ -127,16 +126,25 @@ 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>
|
||||
|
||||
All setter methods take an optional boolean flag parameter, which signifies whether current history
|
||||
record should be replaced or if a new record should be created (default). To change the current URL
|
||||
without creating a new browser history record you can call:
|
||||
<pre>$location.path('/newVal', true);</pre>
|
||||
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
|
||||
otherwise break the back button (navigating back would retrigger the redirection). To change the
|
||||
current URL without creating a new browser history record you can call:
|
||||
<pre>
|
||||
$location.path('/someNewPath');
|
||||
$location.replace();
|
||||
// or you can chain these as: $location.path('/someNewPath').replace();
|
||||
</pre>
|
||||
|
||||
Note that the setters don't update `window.location` immediately. Instead, `$location` service is
|
||||
aware of the {@link api/angular.scope scope} life-cycle and coalesces multiple `$location`
|
||||
mutations into one "commit" to the `window.location` object during the scope `$flush` phase. Since
|
||||
any of the setters can take the replace flag, it's enough for one setter to use this flag in order
|
||||
to make the entire "commit" a replace operation rather than addition to the browser history.
|
||||
mutations into one "commit" to the `window.location` object during the scope `$digest` phase. Since
|
||||
multiple changes to the $location's state will be pushed to the browser as a single change, it's
|
||||
enough to call the `replace()` method just once to make the entire "commit" a replace operation
|
||||
rather than addition to the browser history. Once the browser is updated, the $location service
|
||||
resets the flag set by `replace()` method and future mutations will create new history records,
|
||||
unless `replace()` is called again.
|
||||
|
||||
### Setters and character encoding
|
||||
You can pass special characters to `$location` service and it will encode them according to rules
|
||||
@@ -151,11 +159,11 @@ encoded.
|
||||
`/path?search=a&b=c#hash`. The segments are encoded as well.
|
||||
|
||||
|
||||
# Hashbang and Html5 Modes
|
||||
# Hashbang and HTML5 Modes
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **Html5 mode** which is based on using the
|
||||
Html5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
HTML5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
@@ -167,7 +175,7 @@ facilitate the browser URL change and history management.
|
||||
<tr>
|
||||
<td class="empty-corner-lt"></td>
|
||||
<td>Hashbang mode</td>
|
||||
<td>Html5 mode</td>
|
||||
<td>HTML5 mode</td>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
@@ -206,7 +214,7 @@ In this mode, `$location` uses Hashbang URLs in all browsers.
|
||||
### Example
|
||||
|
||||
<pre>
|
||||
angular.service('$config', function() {
|
||||
angular.service('$locationConfig', function() {
|
||||
return {
|
||||
html5Mode: false,
|
||||
hashPrefix: '!'
|
||||
@@ -254,14 +262,14 @@ having to worry about whether the browser displaying your app supports the histo
|
||||
### Example
|
||||
|
||||
<pre>
|
||||
angular.service('$config', function() {
|
||||
angular.service('$locationConfig', function() {
|
||||
return {
|
||||
html5Mode: true,
|
||||
hashPrefix: '!'
|
||||
};
|
||||
});
|
||||
|
||||
// in browser with html5 history support:
|
||||
// in browser with HTML5 history support:
|
||||
// open http://host.com/#!/a -> rewrite to http://host.com/a
|
||||
// (replacing the http://host.com/#!/a history record)
|
||||
$location.path() == '/a'
|
||||
@@ -343,7 +351,7 @@ takes care of all relative link issues. **Otherwise you have to specify <base
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
Because of rewriting capability in Html5 mode, your users will be able to open regular url links in
|
||||
Because of rewriting capability in HTML5 mode, your users will be able to open regular url links in
|
||||
legacy browsers and hashbang links in modern browser:
|
||||
|
||||
- Modern browser will rewrite hashbang URLs to regular URLs.
|
||||
@@ -457,7 +465,7 @@ ng:ext-link>external</a>
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
var scope = angular.scope(null, {
|
||||
$config: {html5Mode: true, hashPrefix: '!'},
|
||||
$locationConfig: {html5Mode: true, hashPrefix: '!'},
|
||||
$browser: browsers[name],
|
||||
$document: root,
|
||||
$sniffer: {history: name == 'html5'}
|
||||
@@ -612,7 +620,7 @@ https://github.com/angular/angular.js/issues/404 issue}). If you should require
|
||||
you will need to specify an extra property that has two watchers. For example:
|
||||
<pre>
|
||||
<!-- html -->
|
||||
<input type="text" name="locationPath" />
|
||||
<input type="text" ng:model="locationPath" />
|
||||
</pre>
|
||||
<pre>
|
||||
// js - controller
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Creating Angular Services
|
||||
@description
|
||||
@@ -10,7 +9,7 @@ that angular's DI will use to create the service object when it is needed.
|
||||
The `angular.service` method accepts three parameters:
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{function()} factory` - Factory function(called just once by DI).
|
||||
- `{Object} config` - Configuration object with the following properties:
|
||||
- `$inject` - {Array.<string>} - Array of service ids this service depends on. These services
|
||||
will be passed as arguments into the factory function in the same order specified in the `$inject`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Injecting Services Into Controllers
|
||||
@description
|
||||
@@ -54,13 +53,13 @@ myController.$inject = ['notify'];
|
||||
|
||||
<div ng:controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng:init="message='test'" type="text" name="message" />
|
||||
<input ng:init="message='test'" type="text" ng:model="message" />
|
||||
<button ng:click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should test service', function(){
|
||||
expect(element(':input[name=message]').val()).toEqual('test');
|
||||
it('should test service', function() {
|
||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Managing Service Dependencies
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Registering Angular Services
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Testing Angular Services
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Angular Services: Understanding Angular Services
|
||||
@description
|
||||
|
||||
@@ -1,51 +1,34 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Working With CSS in Angular
|
||||
@description
|
||||
|
||||
|
||||
Angular includes built-in CSS classes, which in turn have predefined CSS styles.
|
||||
Angular sets these CSS classes. It is up to your application to provide useful styling.
|
||||
|
||||
# Built-in CSS classes
|
||||
# CSS classes used by angular
|
||||
|
||||
* `ng-exception`
|
||||
* `ng-invalid`, `ng-valid`
|
||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||
notpass validation. (see {@link api/angular.widget.input input} widget).
|
||||
|
||||
**Usage:** angular applies this class to a DOM element if that element contains an Expression that
|
||||
threw an exception when evaluated.
|
||||
* `ng-pristine`, `ng-dirty`
|
||||
- **Usage:** angular {@link api/angular.widget.input input} widget applies `ng-pristine` class
|
||||
to a new input widget element which did not have user interaction. Once the user interacts with
|
||||
the input widget the class is changed to `ng-dirty`.
|
||||
|
||||
**Styling:** The built-in styling of the ng-exception class displays an error message surrounded
|
||||
by a solid red border, for example:
|
||||
# Marking CSS classes
|
||||
|
||||
<div class="ng-exception">Error message</div>
|
||||
* `ng-widget`, `ng-directive`
|
||||
- **Usage:** angular sets these class on elements where {@link api/angular.widget widget} or
|
||||
{@link api/angular.directive directive} has bound to.
|
||||
|
||||
You can try to evaluate malformed expressions in {@link dev_guide.expressions expressions} to see
|
||||
the `ng-exception` class' styling.
|
||||
|
||||
* `ng-validation-error`
|
||||
|
||||
**Usage:** angular applies this class to an input widget element if that element's input does not
|
||||
pass validation. Note that you set the validation criteria on the input widget element using the
|
||||
Ng:validate or Ng:required directives.
|
||||
|
||||
**Styling:** The built-in styling of the ng-validation-error class turns the border of the input
|
||||
box red and includes a hovering UI element that includes more details of the validation error. You
|
||||
can see an example in {@link api/angular.widget.@ng:validate ng:validate example}.
|
||||
|
||||
## Overriding Styles for Angular CSS Classes
|
||||
|
||||
To override the styles for angular's built-in CSS classes, you can do any of the following:
|
||||
|
||||
* Download the source code, edit angular.css, and host the source on your own server.
|
||||
* Create a local CSS file, overriding any styles that you'd like, and link to it from your HTML file
|
||||
as you normally would:
|
||||
|
||||
<pre>
|
||||
<link href="yourfile.css" rel="stylesheet" type="text/css">
|
||||
</pre>
|
||||
|
||||
* Old browser support
|
||||
- Pre v9, IE browsers could not select `ng:include` elements in CSS, because of the `:`
|
||||
character. For this reason angular also sets `ng-include` class on any element which has `:`
|
||||
character in the name by replacing `:` with `-`.
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
* {@link dev_guide.forms Angular Forms}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Data Binding in Angular
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Creating Angular Filters
|
||||
@description
|
||||
@@ -35,20 +34,26 @@ text upper-case and assigns color.
|
||||
}
|
||||
return out;
|
||||
});
|
||||
|
||||
function Ctrl() {
|
||||
this.greeting = 'hello';
|
||||
}
|
||||
</script>
|
||||
|
||||
<input name="text" type="text" value="hello" /><br>
|
||||
No filter: {{text}}<br>
|
||||
Reverse: {{text|reverse}}<br>
|
||||
Reverse + uppercase: {{text|reverse:true}}<br>
|
||||
Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
|
||||
<div ng:controller="Ctrl">
|
||||
<input ng:model="greeting" type="greeting"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
Reverse + uppercase + blue: {{greeting|reverse:true:"blue"}}
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should reverse text', function(){
|
||||
expect(binding('text|reverse')).toEqual('olleh');
|
||||
input('text').enter('ABC');
|
||||
expect(binding('text|reverse')).toEqual('CBA');
|
||||
});
|
||||
it('should reverse greeting', function() {
|
||||
expect(binding('greeting|reverse')).toEqual('olleh');
|
||||
input('greeting').enter('ABC');
|
||||
expect(binding('greeting|reverse')).toEqual('CBA');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Understanding Angular Filters
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Filters: Using Angular Filters
|
||||
@description
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters: Creating Angular Formatters
|
||||
@description
|
||||
|
||||
To create your own formatter, you can simply register a pair of JavaScript functions with
|
||||
`angular.formatter`. One of your functions is used to parse text from the input widget into the
|
||||
data storage format; the other function is used to format stored data into user-readable text.
|
||||
|
||||
The following example demonstrates a "reverse" formatter. Data is stored in uppercase and in
|
||||
reverse, but it is displayed in lower case and non-reversed. When a user edits the data model via
|
||||
the input widget, the input is automatically parsed into the internal data storage format, and when
|
||||
the data changes in the model, it is automatically formatted to the user-readable form for display
|
||||
in the view.
|
||||
|
||||
<pre>
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script type="text/javascript">
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters
|
||||
@description
|
||||
|
||||
In angular, formatters are responsible for translating user-readable text entered in an {@link
|
||||
api/angular.widget.HTML input widget} to a JavaScript object in the data model that the application
|
||||
can manipulate.
|
||||
|
||||
You can use formatters in a template, and also in JavaScript. Angular provides built-in
|
||||
formatters, and of course you can create your own formatters.
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.formatters.using_formatters Using Angular Formatters}
|
||||
* {@link dev_guide.templates.formatters.creating_formatters Creating Angular Formatters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.formatter Angular Formatter API}
|
||||
@@ -1,9 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Angular Formatters: Using Angular Formatters
|
||||
@description
|
||||
|
||||
The following snippet shows how to use a formatter in a template. The formatter below is
|
||||
`ng:format="reverse"`, added as an attribute to an `<input>` tag.
|
||||
|
||||
<pre>
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Understanding Angular Templates
|
||||
@description
|
||||
@@ -18,9 +17,7 @@ is {@link api/angular.widget.@ng:repeat ng:repeat}.
|
||||
* {@link dev_guide.compiler.markup Markup} — Shorthand for a widget or a directive. The double
|
||||
curly brace notation `{{ }}` to bind expressions to elements is built-in angular markup.
|
||||
* {@link dev_guide.templates.filters Filter} — Formats your data for display to the user.
|
||||
* {@link dev_guide.templates.validators Validator} — Lets you validate user input.
|
||||
* {@link dev_guide.templates.formatters Formatter} — Lets you format the input object into a user
|
||||
readable view.
|
||||
* {@link dev_guide.forms Form widgets} — Lets you validate user input.
|
||||
|
||||
Note: In addition to declaring the elements above in templates, you can also access these elements
|
||||
in JavaScript code.
|
||||
@@ -33,7 +30,7 @@ and {@link dev_guide.expressions expressions}:
|
||||
<html>
|
||||
<!-- Body tag augmented with ng:controller directive -->
|
||||
<body ng:controller="MyController">
|
||||
<input name="foo" value="bar">
|
||||
<input ng:model="foo" value="bar">
|
||||
<!-- Button tag with ng:click directive, and
|
||||
string expression 'buttonText'
|
||||
wrapped in "{{ }}" markup -->
|
||||
@@ -55,8 +52,7 @@ eight.
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Angular Validators}
|
||||
* {@link dev_guide.forms Angular Forms}
|
||||
|
||||
## Related API
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Validators: Creating Angular Validators
|
||||
@description
|
||||
|
||||
|
||||
To create a custom validator, you simply add your validator code as a method onto the
|
||||
`angular.validator` object and provide input(s) for the validator function. Each input provided is
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
|
||||
there was a problem with that input". If you prefer to provide more information when a validator
|
||||
detects a problem with input, you can specify an error message in the validator that angular will
|
||||
display when the user hovers over the input widget.
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
In this sample validator, we specify a regular expression against which to test the user's input.
|
||||
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
|
||||
returns the specified error message ("true").
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
* {@link dev_guide.templates.filters Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Angular Formatters}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.validator API Validator Reference}
|
||||
@@ -1,131 +0,0 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Templates: Understanding Angular Validators
|
||||
@description
|
||||
|
||||
Angular validators are attributes that test the validity of different types of user input. Angular
|
||||
provides a set of built-in input validators:
|
||||
|
||||
* {@link api/angular.validator.phone phone number}
|
||||
* {@link api/angular.validator.number number}
|
||||
* {@link api/angular.validator.integer integer}
|
||||
* {@link api/angular.validator.date date}
|
||||
* {@link api/angular.validator.email email address}
|
||||
* {@link api/angular.validator.json JSON}
|
||||
* {@link api/angular.validator.regexp regular expressions}
|
||||
* {@link api/angular.validator.url URLs}
|
||||
* {@link api/angular.validator.asynchronous asynchronous}
|
||||
|
||||
You can also create your own custom validators.
|
||||
|
||||
# Using Angular Validators
|
||||
|
||||
You can use angular validators in HTML template bindings, and in JavaScript:
|
||||
|
||||
* Validators in HTML Template Bindings
|
||||
|
||||
<pre>
|
||||
<input ng:validator="validator_type:parameters" [...]>
|
||||
</pre>
|
||||
|
||||
* Validators in JavaScript
|
||||
|
||||
<pre>
|
||||
angular.validator.[validator_type](parameters)
|
||||
</pre>
|
||||
|
||||
The following example shows how to use the built-in angular integer validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Change me: <input type="text" name="number" ng:validate="integer" value="123">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate the default number string', function() {
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
it('should not validate "foo"', function() {
|
||||
input('number').enter('foo');
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
# Creating an Angular Validator
|
||||
|
||||
To create a custom validator, you simply add your validator code as a method onto the
|
||||
`angular.validator` object and provide input(s) for the validator function. Each input provided is
|
||||
treated as an argument to the validator function. Any additional inputs should be separated by
|
||||
commas.
|
||||
|
||||
The following bit of pseudo-code shows how to set up a custom validator:
|
||||
|
||||
<pre>
|
||||
angular.validator('your_validator', function(input [,additional params]) {
|
||||
[your validation code];
|
||||
if ( [validation succeeds] ) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // No error message specified
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
Note that this validator returns "true" when the user's input is incorrect, as in "Yes, it's true,
|
||||
there was a problem with that input". If you prefer to provide more information when a validator
|
||||
detects a problem with input, you can specify an error message in the validator that angular will
|
||||
display when the user hovers over the input widget.
|
||||
|
||||
To specify an error message, replace "`return true;`" with an error string, for example:
|
||||
|
||||
return "Must be a value between 1 and 5!";
|
||||
|
||||
Following is a sample UPS Tracking Number validator:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
In this sample validator, we specify a regular expression against which to test the user's input.
|
||||
Note that when the user's input matches `regexp`, the function returns "false" (""); otherwise it
|
||||
returns the specified error message ("true").
|
||||
|
||||
Note: you can also access the current angular scope and DOM element objects in your validator
|
||||
functions as follows:
|
||||
|
||||
* `this` === The current angular scope.
|
||||
* `this.$element` === The DOM element that contains the binding. This allows the filter to
|
||||
manipulate the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
## Related Topics
|
||||
|
||||
* {@link dev_guide.templates Angular Templates}
|
||||
|
||||
## Related API
|
||||
|
||||
* {@link api/angular.validator Validator API}
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Unit Testing
|
||||
@description
|
||||
@@ -43,11 +42,11 @@ on a constructor permanently binds the call site to the type. For example lets s
|
||||
trying to instantiate an `XHR` so that we can get some data from the server.
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = function(){...}
|
||||
xhr.onreadystatechange = function() {...}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
@@ -61,7 +60,7 @@ patching, that is a bad idea for many reasons, which is outside the scope of thi
|
||||
The class above is hard to test since we have to resort to monkey patching:
|
||||
<pre>
|
||||
var oldXHR = XHR;
|
||||
XHR = function MockXHR(){};
|
||||
XHR = function MockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that MockXHR got called with the right arguments
|
||||
@@ -73,8 +72,8 @@ XHR = oldXHR; // if you forget this bad things will happen
|
||||
Another way to approach the problem is look for the service in a well known location.
|
||||
|
||||
<pre>
|
||||
function MyClass(){
|
||||
this.doWork = function(){
|
||||
function MyClass() {
|
||||
this.doWork = function() {
|
||||
global.xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
@@ -94,7 +93,7 @@ State & Singletons}
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldXHR = glabal.xhr;
|
||||
glabal.xhr = function mockXHR(){};
|
||||
glabal.xhr = function mockXHR() {};
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
@@ -110,7 +109,7 @@ having the tests replace the services as needed.
|
||||
<pre>
|
||||
function MyClass() {
|
||||
var serviceRegistry = ????;
|
||||
this.doWork = function(){
|
||||
this.doWork = function() {
|
||||
var xhr = serviceRegistry.get('xhr');
|
||||
xhr({
|
||||
method:'...',
|
||||
@@ -128,7 +127,7 @@ there is only one global variable to be reset).
|
||||
The class above is hard to test since we have to change global state:
|
||||
<pre>
|
||||
var oldServiceLocator = glabal.serviceLocator;
|
||||
glabal.serviceLocator.set('xhr', function mockXHR(){});
|
||||
glabal.serviceLocator.set('xhr', function mockXHR() {});
|
||||
var myClass = new MyClass();
|
||||
myClass.doWork();
|
||||
// assert that mockXHR got called with the right arguments
|
||||
@@ -141,7 +140,7 @@ Lastly the dependency can be passed in.
|
||||
|
||||
<pre>
|
||||
function MyClass(xhr) {
|
||||
this.doWork = function(){
|
||||
this.doWork = function() {
|
||||
xhr({
|
||||
method:'...',
|
||||
url:'...',
|
||||
@@ -174,13 +173,13 @@ for your application is mixed in with DOM manipulation, it will be hard to test
|
||||
below:
|
||||
|
||||
<pre>
|
||||
function PasswordController(){
|
||||
function PasswordController() {
|
||||
// get references to DOM elements
|
||||
var msg = $('.ex1 span');
|
||||
var input = $('.ex1 input');
|
||||
var strength;
|
||||
|
||||
this.grade = function(){
|
||||
this.grade = function() {
|
||||
msg.removeClass(strength);
|
||||
var pwd = input.val();
|
||||
password.text(pwd);
|
||||
@@ -219,9 +218,9 @@ In angular the controllers are strictly separated from the DOM manipulation logi
|
||||
a much easier testability story as can be seen in this example:
|
||||
|
||||
<pre>
|
||||
function PasswordCntrl(){
|
||||
function PasswordCntrl() {
|
||||
this.password = '';
|
||||
this.grade = function(){
|
||||
this.grade = function() {
|
||||
var size = this.password.length;
|
||||
if (size > 8) {
|
||||
this.strength = 'strong';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide
|
||||
@description
|
||||
@@ -42,8 +41,7 @@ of the following documents before returning here to the Developer Guide:
|
||||
## {@link dev_guide.templates Angular Templates}
|
||||
|
||||
* {@link dev_guide.templates.filters Understanding Angular Filters}
|
||||
* {@link dev_guide.templates.formatters Understanding Angular Formatters}
|
||||
* {@link dev_guide.templates.validators Understanding Angular Validators}
|
||||
* {@link dev_guide.forms Understanding Angular Forms}
|
||||
|
||||
## {@link dev_guide.services Angular Services}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Downloading
|
||||
@description
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name FAQ
|
||||
@description
|
||||
|
||||
@@ -67,7 +67,7 @@ This example demonstrates angular's two-way data binding:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Your name: <input type="text" name="yourname" value="World"/>
|
||||
Your name: <input type="text" ng:model="yourname" value="World"/>
|
||||
<hr/>
|
||||
Hello {{yourname}}!
|
||||
</doc:source>
|
||||
|
||||
@@ -69,7 +69,7 @@ view.
|
||||
## Model and Controller
|
||||
|
||||
The data __model__ (a simple array of phones in object literal notation) is instantiated within
|
||||
the __controller__ function (`PhoneListCtrl`):
|
||||
the __controller__ function(`PhoneListCtrl`):
|
||||
|
||||
__`app/js/controllers.js`:__
|
||||
<pre>
|
||||
@@ -91,7 +91,7 @@ providing context for our data model, the controller allows us to establish data
|
||||
the model and the view. We connected the dots between the presentation, data, and logic components
|
||||
as follows:
|
||||
|
||||
* The name of our controller function (in the JavaScript file `controllers.js`) matches the {@link
|
||||
* The name of our controller function(in the JavaScript file `controllers.js`) matches the {@link
|
||||
api/angular.directive.ng:controller ng:controller} directive in the `<body>` tag (`PhoneListCtrl`).
|
||||
* The data is instantiated within the *scope* of our controller function; our template binding
|
||||
points are located within the block bounded by the `<body ng:controller="PhoneListCtrl">` tag.
|
||||
|
||||
@@ -32,7 +32,7 @@ We made no changes to the controller.
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
...
|
||||
Fulltext Search: <input name="query"/>
|
||||
Fulltext Search: <input ng:model="query"/>
|
||||
|
||||
<ul class="phones">
|
||||
<li ng:repeat="phone in phones.$filter(query)">
|
||||
|
||||
@@ -27,11 +27,11 @@ __`app/index.html`:__
|
||||
...
|
||||
<ul class="controls">
|
||||
<li>
|
||||
Search: <input type="text" name="query"/>
|
||||
Search: <input type="text" ng:model="query"/>
|
||||
</li>
|
||||
<li>
|
||||
Sort by:
|
||||
<select name="orderProp">
|
||||
<select ng:model="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
@@ -114,7 +114,7 @@ __`test/unit/controllerSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
describe('PhoneListCtrl', function(){
|
||||
describe('PhoneListCtrl', function() {
|
||||
var scope, $browser, ctrl;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
@@ -122,11 +122,11 @@ __`app/partials/phone-list.html`:__
|
||||
<pre>
|
||||
<ul class="predicates">
|
||||
<li>
|
||||
Search: <input type="text" name="query"/>
|
||||
Search: <input type="text" ng:model="query"/>
|
||||
</li>
|
||||
<li>
|
||||
Sort by:
|
||||
<select name="orderProp">
|
||||
<select ng:model="orderProp">
|
||||
<option value="name">Alphabetical</option>
|
||||
<option value="age">Newest</option>
|
||||
</select>
|
||||
|
||||
@@ -109,7 +109,7 @@ following bindings to `index.html`:
|
||||
* We can also create a model with an input element, and combine it with a filtered binding. Add
|
||||
the following to index.html:
|
||||
|
||||
<input name="userInput"> Uppercased: {{ userInput | uppercase }}
|
||||
<input ng:model="userInput"> Uppercased: {{ userInput | uppercase }}
|
||||
|
||||
|
||||
# Summary
|
||||
|
||||
@@ -129,7 +129,7 @@ __`test/unit/controllersSpec.js`:__
|
||||
<pre>
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
beforeEach(function(){
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toEqualData: function(expected) {
|
||||
return angular.equals(this.actual, expected);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<label>Name:</label>
|
||||
<input type="text" name="form.name" ng:required>
|
||||
<input type="text" ng:model="form.name" required>
|
||||
|
||||
<div ng:repeat="contact in form.contacts">
|
||||
<select name="contact.type">
|
||||
<select ng:model="contact.type">
|
||||
<option>url</option>
|
||||
<option>email</option>
|
||||
<option>phone</option>
|
||||
</select>
|
||||
<input type="text" name="contact.url">
|
||||
<input type="text" ng:model="contact.url">
|
||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
||||
</div>
|
||||
<div>
|
||||
@@ -15,4 +15,4 @@
|
||||
</div>
|
||||
|
||||
<button ng:click="cancel()">Cancel</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
<button ng:click="save()">Save</button>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -1,15 +1,15 @@
|
||||
var DOM = require('dom.js').DOM;
|
||||
var DOM = require('../src/dom.js').DOM;
|
||||
|
||||
describe('dom', function(){
|
||||
describe('dom', function() {
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
beforeEach(function() {
|
||||
dom = new DOM();
|
||||
});
|
||||
|
||||
describe('h', function(){
|
||||
describe('h', function() {
|
||||
|
||||
it('should render using function', function(){
|
||||
it('should render using function', function() {
|
||||
var cbThis;
|
||||
var cdValue;
|
||||
dom.h('heading', 'content', function(value){
|
||||
@@ -20,8 +20,8 @@ describe('dom', function(){
|
||||
expect(cbValue).toEqual('content');
|
||||
});
|
||||
|
||||
it('should update heading numbers', function(){
|
||||
dom.h('heading', function(){
|
||||
it('should update heading numbers', function() {
|
||||
dom.h('heading', function() {
|
||||
this.html('<h1>sub-heading</h1>');
|
||||
});
|
||||
expect(dom.toString()).toContain('<h1>heading</h1>');
|
||||
|
||||
+50
-82
@@ -1,11 +1,11 @@
|
||||
var ngdoc = require('ngdoc.js');
|
||||
var DOM = require('dom.js').DOM;
|
||||
var ngdoc = require('../src/ngdoc.js');
|
||||
var DOM = require('../src/dom.js').DOM;
|
||||
|
||||
describe('ngdoc', function(){
|
||||
describe('ngdoc', function() {
|
||||
var Doc = ngdoc.Doc;
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
beforeEach(function() {
|
||||
dom = new DOM();
|
||||
this.addMatchers({
|
||||
toContain: function(text) {
|
||||
@@ -15,15 +15,15 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('Doc', function(){
|
||||
describe('metadata', function(){
|
||||
describe('Doc', function() {
|
||||
describe('metadata', function() {
|
||||
|
||||
it('should find keywords', function(){
|
||||
it('should find keywords', function() {
|
||||
expect(new Doc('\nHello: World! @ignore. $abc').keywords()).toEqual('$abc hello world');
|
||||
expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the');
|
||||
});
|
||||
|
||||
it('should have shortName', function(){
|
||||
it('should have shortName', function() {
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
@@ -32,7 +32,7 @@ describe('ngdoc', function(){
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
it('should have depth information', function(){
|
||||
it('should have depth information', function() {
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
@@ -43,8 +43,8 @@ describe('ngdoc', function(){
|
||||
|
||||
});
|
||||
|
||||
describe('parse', function(){
|
||||
it('should convert @names into properties', function(){
|
||||
describe('parse', function() {
|
||||
it('should convert @names into properties', function() {
|
||||
var doc = new Doc('\n@name name\n@desc\ndesc\ndesc2\n@dep\n');
|
||||
doc.parse();
|
||||
expect(doc.name).toEqual('name');
|
||||
@@ -52,7 +52,7 @@ describe('ngdoc', function(){
|
||||
expect(doc.dep).toEqual('');
|
||||
});
|
||||
|
||||
it('should parse parameters', function(){
|
||||
it('should parse parameters', function() {
|
||||
var doc = new Doc(
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
@@ -65,7 +65,7 @@ describe('ngdoc', function(){
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse return', function(){
|
||||
it('should parse return', function() {
|
||||
var doc = new Doc('@returns {Type} text *bold*.');
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
@@ -74,14 +74,14 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse filename', function(){
|
||||
it('should parse filename', function() {
|
||||
var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1);
|
||||
doc.parse(0);
|
||||
expect(doc.id).toEqual('a.b');
|
||||
expect(doc.name).toEqual('friendly name');
|
||||
});
|
||||
|
||||
it('should escape <doc:source> element', function(){
|
||||
it('should escape <doc:source> element', function() {
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
@@ -89,7 +89,7 @@ describe('ngdoc', function(){
|
||||
'<pre class="doc-source">\n<>\n</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should preserve the jsfiddle attribute', function(){
|
||||
it('should preserve the jsfiddle attribute', function() {
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:source jsfiddle="foo">lala</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
@@ -97,7 +97,7 @@ describe('ngdoc', function(){
|
||||
'<pre class="doc-source" jsfiddle="foo">lala</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should escape <doc:scenario> element', function(){
|
||||
it('should escape <doc:scenario> element', function() {
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
doc.parse();
|
||||
@@ -132,11 +132,11 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting', function(){
|
||||
describe('sorting', function() {
|
||||
function property(name) {
|
||||
return function(obj) {return obj[name];};
|
||||
}
|
||||
function noop(){}
|
||||
function noop() {}
|
||||
function doc(type, name){
|
||||
return {
|
||||
id: name,
|
||||
@@ -149,7 +149,7 @@ describe('ngdoc', function(){
|
||||
var angular_x = doc('function', 'angular.x');
|
||||
var angular_y = doc('property', 'angular.y');
|
||||
|
||||
it('should put angular.fn() in front of angular.widget, etc', function(){
|
||||
it('should put angular.fn() in front of angular.widget, etc', function() {
|
||||
expect(ngdoc.metadata([angular_widget, angular_y, angular_x]).map(property('id')))
|
||||
.toEqual(['angular.x', 'angular.y', 'angular.widget' ]);
|
||||
});
|
||||
@@ -157,13 +157,13 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('markdown', function(){
|
||||
it('should replace angular in markdown', function(){
|
||||
describe('markdown', function() {
|
||||
it('should replace angular in markdown', function() {
|
||||
expect(new Doc().markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', 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</p>' +
|
||||
@@ -194,12 +194,12 @@ describe('ngdoc', function(){
|
||||
it('should ignore nested doc widgets', function() {
|
||||
expect(new Doc().markdown(
|
||||
'before<doc:tutorial-instructions>\n' +
|
||||
'<doc:tutorial-instruction id="git-mac" name="Git on Mac/Linux">' +
|
||||
'<doc:tutorial-instruction id="git-mac" ng:model="Git on Mac/Linux">' +
|
||||
'\ngit bla bla\n</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>')).toEqual(
|
||||
|
||||
'<p>before</p><doc:tutorial-instructions>\n' +
|
||||
'<doc:tutorial-instruction id="git-mac" name="Git on Mac/Linux">\n' +
|
||||
'<doc:tutorial-instruction id="git-mac" ng:model="Git on Mac/Linux">\n' +
|
||||
'git bla bla\n' +
|
||||
'</doc:tutorial-instruction>\n' +
|
||||
'</doc:tutorial-instructions>');
|
||||
@@ -234,19 +234,19 @@ describe('ngdoc', function(){
|
||||
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
describe('trim', function() {
|
||||
var trim = ngdoc.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
it('should remove leading/trailing space', function() {
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
it('should remove leading space on every line', function() {
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge', function(){
|
||||
it('should merge child with parent', function(){
|
||||
describe('merge', function() {
|
||||
it('should merge child with parent', function() {
|
||||
var parent = new Doc({id: 'angular.service.abc', name: 'angular.service.abc', section: 'api'});
|
||||
var methodA = new Doc({name: 'methodA', methodOf: 'angular.service.abc'});
|
||||
var methodB = new Doc({name: 'methodB', methodOf: 'angular.service.abc'});
|
||||
@@ -294,9 +294,9 @@ describe('ngdoc', function(){
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
describe('TAG', function(){
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
describe('TAG', function() {
|
||||
describe('@param', function() {
|
||||
it('should parse with no default', function() {
|
||||
var doc = new Doc('@param {(number|string)} number Number \n to format.');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
@@ -307,7 +307,7 @@ describe('ngdoc', function(){
|
||||
description : '<p>Number \nto format.</p>' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function(){
|
||||
it('should parse with default and optional', function() {
|
||||
var doc = new Doc('@param {(number|string)=} [fractionSize=2] desc');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
@@ -397,8 +397,8 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('@description', function(){
|
||||
it('should support pre blocks', function(){
|
||||
describe('@description', function() {
|
||||
it('should support pre blocks', function() {
|
||||
var doc = new Doc("@description <pre><b>abc</b></pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
@@ -442,7 +442,7 @@ describe('ngdoc', function(){
|
||||
toContain('<a href="./static.html">./static.html</a>');
|
||||
});
|
||||
|
||||
it('should support line breaks in @link', function(){
|
||||
it('should support line breaks in @link', function() {
|
||||
var doc = new Doc("@description " +
|
||||
'{@link\napi/angular.foo\na\nb}');
|
||||
doc.parse();
|
||||
@@ -452,14 +452,14 @@ describe('ngdoc', function(){
|
||||
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
describe('@example', function() {
|
||||
it('should not remove {{}}', function() {
|
||||
var doc = new Doc('@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('<p>text {{ abc }}</p>');
|
||||
});
|
||||
|
||||
it('should support doc:example', function(){
|
||||
it('should support doc:example', function() {
|
||||
var doc = new Doc('@ngdoc overview\n@example \n' +
|
||||
'<doc:example>\n' +
|
||||
' <doc:source><escapeme></doc:source>\n' +
|
||||
@@ -480,7 +480,7 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('@this', function(){
|
||||
describe('@this', function() {
|
||||
it('should render @this', function() {
|
||||
var doc = new Doc('@this I am self.');
|
||||
doc.ngdoc = 'filter';
|
||||
@@ -495,9 +495,9 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('usage', function(){
|
||||
describe('overview', function(){
|
||||
it('should supress description heading', function(){
|
||||
describe('usage', function() {
|
||||
describe('overview', function() {
|
||||
it('should supress description heading', function() {
|
||||
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('text');
|
||||
@@ -507,8 +507,8 @@ describe('ngdoc', function(){
|
||||
});
|
||||
|
||||
|
||||
describe('function', function(){
|
||||
it('should format', function(){
|
||||
describe('function', function() {
|
||||
it('should format', function() {
|
||||
var doc = new Doc({
|
||||
ngdoc:'function',
|
||||
name:'some.name',
|
||||
@@ -527,8 +527,8 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter', function(){
|
||||
it('should format', function(){
|
||||
describe('filter', function() {
|
||||
it('should format', function() {
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFilter',
|
||||
@@ -543,40 +543,8 @@ describe('ngdoc', function(){
|
||||
});
|
||||
});
|
||||
|
||||
describe('validator', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'validator',
|
||||
shortName:'myValidator',
|
||||
param: [
|
||||
{name:'a'},
|
||||
{name:'b'}
|
||||
]
|
||||
});
|
||||
doc.html_usage_validator(dom);
|
||||
expect(dom).toContain('ng:validate="myValidator:b"');
|
||||
expect(dom).toContain('angular.validator.myValidator(a, b)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFormatter',
|
||||
param: [
|
||||
{name:'a'},
|
||||
]
|
||||
});
|
||||
doc.html_usage_formatter(dom);
|
||||
expect(dom).toContain('ng:format="myFormatter:a"');
|
||||
expect(dom).toContain('var userInputString = angular.formatter.myFormatter.format(modelValue, a);');
|
||||
expect(dom).toContain('var modelValue = angular.formatter.myFormatter.parse(userInputString, a);');
|
||||
});
|
||||
});
|
||||
|
||||
describe('property', function(){
|
||||
it('should format', function(){
|
||||
describe('property', function() {
|
||||
it('should format', function() {
|
||||
var doc = new Doc({
|
||||
ngdoc:'property',
|
||||
name:'myProp',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
var SiteMap = require('SiteMap.js').SiteMap;
|
||||
var Doc = require('ngdoc.js').Doc;
|
||||
var SiteMap = require('../src/SiteMap.js').SiteMap;
|
||||
var Doc = require('../src/ngdoc.js').Doc;
|
||||
|
||||
|
||||
describe('sitemap', function(){
|
||||
it('should render empty sitemap', function(){
|
||||
describe('sitemap', function() {
|
||||
it('should render empty sitemap', function() {
|
||||
var map = new SiteMap([]);
|
||||
expect(map.render()).toEqual([
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
@@ -11,7 +11,7 @@ describe('sitemap', function(){
|
||||
'</urlset>', ''].join('\n'));
|
||||
});
|
||||
|
||||
it('should render ngdoc url', function(){
|
||||
it('should render ngdoc url', function() {
|
||||
var map = new SiteMap([new Doc({section: 'foo', id: 'a.b.c<>\'"&'})]);
|
||||
expect(map.render()).toContain([
|
||||
' <url>',
|
||||
|
||||
+2
-4
@@ -1,8 +1,6 @@
|
||||
if (global.jasmine) return;
|
||||
|
||||
require.paths.push(__dirname + "/../../lib");
|
||||
require.paths.push(__dirname + '/../src');
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var jasmine = require('../../lib/jasmine-1.0.1');
|
||||
var sys = require('util');
|
||||
|
||||
for(var key in jasmine) {
|
||||
@@ -10,7 +8,7 @@ for(var key in jasmine) {
|
||||
}
|
||||
|
||||
//Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
jasmine.Spec.prototype.fail = function(e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
var writer = require('writer.js');
|
||||
describe('writer', function(){
|
||||
describe('toString', function(){
|
||||
var writer = require('../src/writer.js');
|
||||
describe('writer', function() {
|
||||
describe('toString', function() {
|
||||
var toString = writer.toString;
|
||||
|
||||
it('should merge string', function(){
|
||||
it('should merge string', function() {
|
||||
expect(toString('abc')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should merge obj', function(){
|
||||
it('should merge obj', function() {
|
||||
expect(toString({a:1})).toEqual('{"a":1}');
|
||||
});
|
||||
|
||||
it('should merge array', function(){
|
||||
it('should merge array', function() {
|
||||
expect(toString(['abc',{}])).toEqual('abc{}');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ exports.SiteMap = SiteMap;
|
||||
* @returns {SiteMap}
|
||||
*/
|
||||
function SiteMap(docs){
|
||||
this.render = function(){
|
||||
this.render = function() {
|
||||
var map = [];
|
||||
map.push('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
map.push('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ function htmlEscape(text){
|
||||
}
|
||||
|
||||
|
||||
function DOM(){
|
||||
function DOM() {
|
||||
this.out = [];
|
||||
this.headingDepth = 0;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ DOM.prototype = {
|
||||
},
|
||||
|
||||
code: function(text) {
|
||||
this.tag('div', {'ng:non-bindable':''}, function(){
|
||||
this.tag('div', {'ng:non-bindable':''}, function() {
|
||||
this.tag('pre', {'class':"brush: js; html-script: true;"}, text);
|
||||
});
|
||||
},
|
||||
@@ -84,7 +84,7 @@ DOM.prototype = {
|
||||
if (content instanceof Array) {
|
||||
this.ul(content, className, fn);
|
||||
} else if (fn) {
|
||||
this.tag('div', className, function(){
|
||||
this.tag('div', className, function() {
|
||||
fn.call(this, content);
|
||||
});
|
||||
} else {
|
||||
|
||||
+8
-10
@@ -1,13 +1,11 @@
|
||||
require.paths.push(__dirname);
|
||||
require.paths.push('lib');
|
||||
var reader = require('reader.js'),
|
||||
ngdoc = require('ngdoc.js'),
|
||||
writer = require('writer.js'),
|
||||
SiteMap = require('SiteMap.js').SiteMap,
|
||||
appCache = require('appCache.js').appCache,
|
||||
var reader = require('./reader.js'),
|
||||
ngdoc = require('./ngdoc.js'),
|
||||
writer = require('./writer.js'),
|
||||
SiteMap = require('./SiteMap.js').SiteMap,
|
||||
appCache = require('./appCache.js').appCache,
|
||||
Q = require('qq');
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
process.on('uncaughtException', function(err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
@@ -93,6 +91,6 @@ function writeTheRest(writesFuture) {
|
||||
}
|
||||
|
||||
|
||||
function now(){ return new Date().getTime(); }
|
||||
function now() { return new Date().getTime(); }
|
||||
|
||||
function noop(){};
|
||||
function noop() {};
|
||||
|
||||
+71
-111
@@ -2,9 +2,9 @@
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('showdown').Showdown;
|
||||
var DOM = require('dom.js').DOM;
|
||||
var htmlEscape = require('dom.js').htmlEscape;
|
||||
var Showdown = require('../../lib/showdown').Showdown;
|
||||
var DOM = require('./dom.js').DOM;
|
||||
var htmlEscape = require('./dom.js').htmlEscape;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
exports.trim = trim;
|
||||
@@ -13,6 +13,11 @@ exports.scenarios = scenarios;
|
||||
exports.merge = merge;
|
||||
exports.Doc = Doc;
|
||||
|
||||
var BOOLEAN_ATTR = {};
|
||||
['multiple', 'selected', 'checked', 'disabled', 'readOnly', 'required'].forEach(function(value, key) {
|
||||
BOOLEAN_ATTR[value] = true;
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function Doc(text, file, line) {
|
||||
if (typeof text == 'object') {
|
||||
@@ -32,14 +37,14 @@ function Doc(text, file, line) {
|
||||
this.events = this.events || [];
|
||||
this.links = this.links || [];
|
||||
}
|
||||
Doc.METADATA_IGNORE = (function(){
|
||||
Doc.METADATA_IGNORE = (function() {
|
||||
var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8');
|
||||
return words.toString().split(/[,\s\n\r]+/gm);
|
||||
})();
|
||||
|
||||
|
||||
Doc.prototype = {
|
||||
keywords: function keywords(){
|
||||
keywords: function keywords() {
|
||||
var keywords = {};
|
||||
Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; });
|
||||
var words = [];
|
||||
@@ -78,7 +83,7 @@ Doc.prototype = {
|
||||
return this.section + '/' + url;
|
||||
},
|
||||
|
||||
markdown: function (text) {
|
||||
markdown: function(text) {
|
||||
if (!text) return text;
|
||||
|
||||
var self = this,
|
||||
@@ -146,7 +151,7 @@ Doc.prototype = {
|
||||
return parts.join('');
|
||||
},
|
||||
|
||||
parse: function(){
|
||||
parse: function() {
|
||||
var atName;
|
||||
var atText;
|
||||
var match;
|
||||
@@ -175,7 +180,7 @@ Doc.prototype = {
|
||||
this['this'] = this.markdown(this['this']);
|
||||
return this;
|
||||
|
||||
function flush(){
|
||||
function flush() {
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n'));
|
||||
if (atName == 'param') {
|
||||
@@ -229,26 +234,24 @@ Doc.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
html: function(){
|
||||
html: function() {
|
||||
var dom = new DOM(),
|
||||
self = this;
|
||||
|
||||
dom.h(this.name, function(){
|
||||
notice('workInProgress', 'Work in Progress',
|
||||
'This page is currently being revised. It might be incomplete or contain inaccuracies.');
|
||||
dom.h(this.name, function() {
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
if (self.ngdoc != 'overview') {
|
||||
dom.h('Description', self.description, dom.html);
|
||||
}
|
||||
dom.h('Dependencies', self.requires, function(require){
|
||||
dom.tag('code', function(){
|
||||
dom.tag('code', function() {
|
||||
dom.tag('a', {href: 'api/angular.service.' + require.name}, require.name);
|
||||
});
|
||||
dom.html(require.text);
|
||||
});
|
||||
|
||||
(self['html_usage_' + self.ngdoc] || function(){
|
||||
(self['html_usage_' + self.ngdoc] || function() {
|
||||
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
|
||||
}).call(self, dom);
|
||||
|
||||
@@ -271,10 +274,10 @@ Doc.prototype = {
|
||||
|
||||
html_usage_parameters: function(dom) {
|
||||
dom.h('Parameters', this.param, function(param){
|
||||
dom.tag('code', function(){
|
||||
dom.tag('code', function() {
|
||||
dom.text(param.name);
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.tag('i', function() {
|
||||
dom.text('(optional');
|
||||
if(param['default']) {
|
||||
dom.text('=' + param['default']);
|
||||
@@ -293,7 +296,7 @@ Doc.prototype = {
|
||||
html_usage_returns: function(dom) {
|
||||
var self = this;
|
||||
if (self.returns) {
|
||||
dom.h('Returns', function(){
|
||||
dom.h('Returns', function() {
|
||||
dom.tag('code', '{' + self.returns.type + '}');
|
||||
dom.text('– ');
|
||||
dom.html(self.returns.description);
|
||||
@@ -314,8 +317,8 @@ Doc.prototype = {
|
||||
|
||||
html_usage_function: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.code(function() {
|
||||
dom.text(self.name.split('service.').pop());
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
@@ -330,8 +333,8 @@ Doc.prototype = {
|
||||
|
||||
html_usage_property: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.code(function() {
|
||||
dom.text(self.name);
|
||||
});
|
||||
|
||||
@@ -341,8 +344,8 @@ Doc.prototype = {
|
||||
|
||||
html_usage_directive: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function() {
|
||||
dom.text('<' + self.element + ' ');
|
||||
dom.text(self.shortName);
|
||||
if (self.param.length) {
|
||||
@@ -357,9 +360,9 @@ Doc.prototype = {
|
||||
|
||||
html_usage_filter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.h('In HTML Template Binding', function() {
|
||||
dom.tag('code', function() {
|
||||
dom.text('{{ ');
|
||||
dom.text(self.shortName);
|
||||
dom.text('_expression | ');
|
||||
@@ -369,8 +372,8 @@ Doc.prototype = {
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.h('In JavaScript', function() {
|
||||
dom.tag('code', function() {
|
||||
dom.text('angular.filter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
@@ -385,77 +388,29 @@ Doc.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_formatter: function(dom){
|
||||
html_usage_inputType: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.inputType=='select')
|
||||
dom.text('<select name="bindExpression"');
|
||||
else
|
||||
dom.text('<input type="text" name="bindExpression"');
|
||||
dom.text(' ng:format="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', false, true);
|
||||
dom.text('">');
|
||||
dom.h('Usage', function() {
|
||||
dom.code(function() {
|
||||
dom.text('<input type="' + self.shortName + '"');
|
||||
(self.param||[]).forEach(function(param){
|
||||
dom.text('\n ');
|
||||
dom.text(param.optional ? ' [' : ' ');
|
||||
dom.text(param.name);
|
||||
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
|
||||
dom.text(param.optional ? ']' : '');
|
||||
});
|
||||
dom.text('>');
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('var userInputString = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.format(modelValue');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
dom.text('\n');
|
||||
dom.text('var modelValue = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.parse(userInputString');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_validator: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.text('<input type="text" ng:validate="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', true);
|
||||
dom.text('"/>');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('angular.validator.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_widget: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.h('In HTML Template Binding', function() {
|
||||
dom.code(function() {
|
||||
if (self.shortName.match(/^@/)) {
|
||||
dom.text('<');
|
||||
dom.text(self.element);
|
||||
@@ -473,11 +428,11 @@ Doc.prototype = {
|
||||
dom.text('<');
|
||||
dom.text(self.shortName);
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (param.optional) {
|
||||
dom.text(' [' + param.name + '="..."]');
|
||||
} else {
|
||||
dom.text(' ' + param.name + '="..."');
|
||||
}
|
||||
dom.text('\n ');
|
||||
dom.text(param.optional ? ' [' : ' ');
|
||||
dom.text(param.name);
|
||||
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
|
||||
dom.text(param.optional ? ']' : '');
|
||||
});
|
||||
dom.text('></');
|
||||
dom.text(self.shortName);
|
||||
@@ -499,8 +454,8 @@ Doc.prototype = {
|
||||
var self = this;
|
||||
|
||||
if (this.param.length) {
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.h('Usage', function() {
|
||||
dom.code(function() {
|
||||
dom.text(self.name.split('.').pop());
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
@@ -515,7 +470,7 @@ Doc.prototype = {
|
||||
|
||||
dom.h('Methods', this.methods, function(method){
|
||||
var signature = (method.param || []).map(property('name'));
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function() {
|
||||
dom.html(method.description);
|
||||
method.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
@@ -525,20 +480,26 @@ Doc.prototype = {
|
||||
});
|
||||
});
|
||||
dom.h('Properties', this.properties, function(property){
|
||||
dom.h(property.name, function(){
|
||||
dom.h(property.name, function() {
|
||||
dom.html(property.description);
|
||||
dom.h('Example', property.example, dom.html);
|
||||
});
|
||||
});
|
||||
dom.h('Events', this.events, function(event){
|
||||
dom.h(event.shortName, event, function(){
|
||||
dom.h(event.shortName, event, function() {
|
||||
dom.html(event.description);
|
||||
dom.tag('div', {class:'inline'}, function(){
|
||||
dom.h('Type:', event.type);
|
||||
});
|
||||
dom.tag('div', {class:'inline'}, function(){
|
||||
dom.h('Target:', event.target);
|
||||
});
|
||||
if (event.type == 'listen') {
|
||||
dom.tag('div', {class:'inline'}, function() {
|
||||
dom.h('Listen on:', event.target);
|
||||
});
|
||||
} else {
|
||||
dom.tag('div', {class:'inline'}, function() {
|
||||
dom.h('Type:', event.type);
|
||||
});
|
||||
dom.tag('div', {class:'inline'}, function() {
|
||||
dom.h('Target:', event.target);
|
||||
});
|
||||
}
|
||||
event.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
|
||||
@@ -584,8 +545,8 @@ function scenarios(docs){
|
||||
|
||||
function appendSpecs(urlPrefix) {
|
||||
docs.forEach(function(doc){
|
||||
specs.push(' describe("' + doc.section + '/' + doc.id + '", function(){');
|
||||
specs.push(' beforeEach(function(){');
|
||||
specs.push(' describe("' + doc.section + '/' + doc.id + '", function() {');
|
||||
specs.push(' beforeEach(function() {');
|
||||
specs.push(' browser().navigateTo("' + urlPrefix + doc.section + '/' + doc.id + '");');
|
||||
specs.push(' });');
|
||||
specs.push(' ');
|
||||
@@ -632,10 +593,9 @@ var KEYWORD_PRIORITY = {
|
||||
'.angular.Object': 7,
|
||||
'.angular.directive': 7,
|
||||
'.angular.filter': 7,
|
||||
'.angular.formatter': 7,
|
||||
'.angular.scope': 7,
|
||||
'.angular.service': 7,
|
||||
'.angular.validator': 7,
|
||||
'.angular.inputType': 7,
|
||||
'.angular.widget': 7,
|
||||
'.angular.mock': 8,
|
||||
'.dev_guide.overview': 1,
|
||||
@@ -724,7 +684,7 @@ function indent(text, spaceCount) {
|
||||
function merge(docs){
|
||||
var byFullId = {};
|
||||
|
||||
docs.forEach(function (doc) {
|
||||
docs.forEach(function(doc) {
|
||||
byFullId[doc.section + '/' + doc.id] = doc;
|
||||
});
|
||||
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@
|
||||
|
||||
exports.collect = collect;
|
||||
|
||||
require.paths.push(__dirname);
|
||||
var ngdoc = require('ngdoc.js'),
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-fs');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function(){
|
||||
(function() {
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
@@ -82,10 +82,12 @@
|
||||
|
||||
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<textarea name="css">' +
|
||||
'.ng-invalid { border: 1px solid red; } \n' +
|
||||
'body { font-family: Arial,Helvetica,sans-serif; }\n' +
|
||||
'body, td, th { font-size: 14px; margin: 0; }\n' +
|
||||
'table { border-collapse: separate; border-spacing: 2px; display: table; margin-bottom: 0; margin-top: 0; -moz-box-sizing: border-box; text-indent: 0; }\n' +
|
||||
'a:link, a:visited, a:hover { color: #5D6DB6; text-decoration: none; }\n' +
|
||||
'.error { color: red; }\n' +
|
||||
'</textarea>' +
|
||||
'<input type="text" name="title" value="AngularJS Live Example">' +
|
||||
'<textarea name="html">' +
|
||||
|
||||
@@ -49,6 +49,10 @@ li {
|
||||
margin: 0.3em 0 0.3em 0;
|
||||
}
|
||||
|
||||
.ng-invalid {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
|
||||
/*----- Upgrade IE Prompt -----*/
|
||||
|
||||
@@ -70,6 +74,31 @@ li {
|
||||
|
||||
/*----- Global Layout -----*/
|
||||
|
||||
#loading {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
margin: 1.5em -40px;
|
||||
width: 80px;
|
||||
padding: 0.2em 0.2em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
color: #FFFFFF;
|
||||
border: 1px solid black;
|
||||
background-color: #7989D6;
|
||||
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
|
||||
-webkit-box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
||||
|
||||
z-index: 2; /* just below #fader */
|
||||
}
|
||||
|
||||
|
||||
#container {
|
||||
width: 1150px;
|
||||
margin: 0 auto;
|
||||
@@ -426,7 +455,7 @@ li {
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -448,7 +477,7 @@ td.empty-corner-lt {
|
||||
.html5-hashbang-example {
|
||||
height: 255px;
|
||||
margin-left: -40px;
|
||||
padding-left: 30px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.html5-hashbang-example div {
|
||||
@@ -459,3 +488,7 @@ td.empty-corner-lt {
|
||||
.html5-hashbang-example div input {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
+36
-29
@@ -2,53 +2,57 @@ DocsController.$inject = ['$location', '$browser', '$window', '$cookies'];
|
||||
function DocsController($location, $browser, $window, $cookies) {
|
||||
window.$root = this.$root;
|
||||
|
||||
var self = this,
|
||||
var scope = this,
|
||||
OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)/,
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
|
||||
|
||||
this.$location = $location;
|
||||
|
||||
self.versionNumber = angular.version.full;
|
||||
self.version = angular.version.full + " " + angular.version.codeName;
|
||||
self.subpage = false;
|
||||
self.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
scope.$location = $location;
|
||||
scope.versionNumber = angular.version.full;
|
||||
scope.version = angular.version.full + " " + angular.version.codeName;
|
||||
scope.subpage = false;
|
||||
scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
scope.futurePartialTitle = null;
|
||||
scope.loading = 0;
|
||||
|
||||
if (!$location.path() || INDEX_PATH.test($location.path())) {
|
||||
$location.path('/api').replace();
|
||||
}
|
||||
|
||||
this.$watch('$location.path()', function(scope, path) {
|
||||
scope.$watch('$location.path()', function(scope, path) {
|
||||
// ignore non-doc links which are used in examples
|
||||
if (DOCS_PATH.test(path)) {
|
||||
var parts = path.split('/');
|
||||
self.sectionId = parts[1];
|
||||
self.partialId = parts[2] || 'index';
|
||||
self.pages = angular.Array.filter(NG_PAGES, {section:self.sectionId});
|
||||
scope.sectionId = parts[1];
|
||||
scope.partialId = parts[2] || 'index';
|
||||
scope.pages = angular.Array.filter(NG_PAGES, {section: scope.sectionId});
|
||||
scope.loading++;
|
||||
|
||||
var i = self.pages.length;
|
||||
var i = scope.pages.length;
|
||||
while (i--) {
|
||||
if (self.pages[i].id == self.partialId) {
|
||||
self.partialTitle = self.pages[i].name;
|
||||
if (scope.pages[i].id == scope.partialId) {
|
||||
// TODO(i): this is not ideal but better than updating the title before a partial arrives,
|
||||
// which results in the old partial being displayed with the new title
|
||||
scope.futurePartialTitle = scope.pages[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i<0) {
|
||||
self.partialTitle = 'Error: Page Not Found!';
|
||||
delete self.partialId;
|
||||
scope.partialTitle = 'Error: Page Not Found!';
|
||||
delete scope.partialId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.getUrl = function(page){
|
||||
scope.getUrl = function(page) {
|
||||
return page.section + '/' + page.id;
|
||||
};
|
||||
|
||||
this.getCurrentPartial = function(){
|
||||
scope.getCurrentPartial = function() {
|
||||
return this.partialId ? ('./partials/' + this.sectionId + '/' + this.partialId + '.html') : '';
|
||||
};
|
||||
|
||||
this.getClass = function(page) {
|
||||
scope.getClass = function(page) {
|
||||
var depth = page.depth,
|
||||
cssClass = 'level-' + depth + (page.name == this.partialId ? ' selected' : '');
|
||||
|
||||
@@ -58,28 +62,30 @@ function DocsController($location, $browser, $window, $cookies) {
|
||||
return cssClass;
|
||||
};
|
||||
|
||||
this.selectedSection = function(section) {
|
||||
return section == self.sectionId ? 'current' : '';
|
||||
scope.selectedSection = function(section) {
|
||||
return section == scope.sectionId ? 'current' : '';
|
||||
};
|
||||
|
||||
this.selectedPartial = function(partial) {
|
||||
return partial.id == self.partialId ? 'current' : '';
|
||||
scope.selectedPartial = function(partial) {
|
||||
return partial.id == scope.partialId ? 'current' : '';
|
||||
};
|
||||
|
||||
this.afterPartialLoaded = function() {
|
||||
scope.afterPartialLoaded = function() {
|
||||
scope.loading--;
|
||||
scope.partialTitle = scope.futurePartialTitle;
|
||||
SyntaxHighlighter.highlight();
|
||||
$window.scrollTo(0,0);
|
||||
$window._gaq.push(['_trackPageview', $location.path()]);
|
||||
};
|
||||
|
||||
this.getFeedbackUrl = function() {
|
||||
scope.getFeedbackUrl = function() {
|
||||
return "mailto:angular@googlegroups.com?" +
|
||||
"subject=" + escape("Feedback on " + $location.absUrl()) + "&" +
|
||||
"body=" + escape("Hi there,\n\nI read " + $location.absUrl() + " and wanted to ask ....");
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
this.enableOffline = function() {
|
||||
scope.enableOffline = function() {
|
||||
//The cookie will be good for one year!
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(365*24*60*60*1000));
|
||||
@@ -94,14 +100,15 @@ function DocsController($location, $browser, $window, $cookies) {
|
||||
// bind escape to hash reset callback
|
||||
angular.element(window).bind('keydown', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
self.subpage = false;
|
||||
self.$eval();
|
||||
scope.$apply(function() {
|
||||
scope.subpage = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// prevent compilation of code
|
||||
angular.widget('code', function(element){
|
||||
angular.widget('code', function(element) {
|
||||
element.attr('ng:non-bindable', 'true');
|
||||
});
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// we can't add css/js the usual way, because some browsers (FF) eagerly prefetch resources
|
||||
// before the base attribute is added, causing 404 and terribly slow loading of the docs app.
|
||||
(function() {
|
||||
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', 'index.html'])[1],
|
||||
rUrl = /(api|guide|misc|tutorial|cookbook|index[^\.]*\.html).*$/,
|
||||
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', ''])[1],
|
||||
rUrl = /(#!\/|api|guide|misc|tutorial|cookbook|index[^\.]*\.html).*$/,
|
||||
baseUrl = location.href.replace(rUrl, indexFile),
|
||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||
@@ -99,7 +99,7 @@
|
||||
</ul>
|
||||
|
||||
<div id="sidebar">
|
||||
<input type="text" name="search" id="search-box" placeholder="search the docs"
|
||||
<input type="text" ng:model="search" id="search-box" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s">
|
||||
|
||||
<ul id="content-list" ng:class="sectionId" ng:cloak>
|
||||
@@ -111,6 +111,8 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="loading" ng:show="loading">Loading...</div>
|
||||
|
||||
|
||||
<div id="content-panel">
|
||||
<h2 ng:bind="partialTitle"></h2>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+4
-5
@@ -2,7 +2,6 @@
|
||||
* All writing related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var qfs = require('q-fs');
|
||||
var Q = require('qq');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
@@ -18,7 +17,7 @@ exports.output = function(file, content) {
|
||||
};
|
||||
|
||||
//recursively create directory
|
||||
exports.makeDir = function (path) {
|
||||
exports.makeDir = function(path) {
|
||||
var parts = path.split(/\//);
|
||||
var path = ".";
|
||||
//Sequentially create directories
|
||||
@@ -99,7 +98,7 @@ function merge(srcs, to) {
|
||||
var contents = [];
|
||||
//Sequentially read file
|
||||
var done;
|
||||
srcs.forEach(function (src) {
|
||||
srcs.forEach(function(src) {
|
||||
done = Q.when(done, function(content) {
|
||||
if(content) contents.push(content);
|
||||
return qfs.read(src, 'b');
|
||||
@@ -128,7 +127,7 @@ exports.toString = function toString(obj) {
|
||||
return obj;
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
obj.forEach(function (value, key) {
|
||||
obj.forEach(function(value, key) {
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
@@ -140,4 +139,4 @@ exports.toString = function toString(obj) {
|
||||
};
|
||||
|
||||
|
||||
function noop(){};
|
||||
function noop() {};
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
<span><angular/> Buzz</span>
|
||||
<span>
|
||||
filter:
|
||||
<input type="text" name="filterText"/>
|
||||
<input type="text" ng:model="filterText"/>
|
||||
</span>
|
||||
<span>
|
||||
user:
|
||||
<input type="text" name="userId" ng:required/>
|
||||
<input type="text" ng:model="userId" required/>
|
||||
<button ng:click="$location.hashPath = userId">fetch</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -8,11 +8,11 @@ angular.service('myApplication', function($resource){
|
||||
});
|
||||
}, {inject:['$resource']});
|
||||
|
||||
function BuzzController(){
|
||||
function BuzzController() {
|
||||
this.$watch('$location.hashPath', this.userChange);
|
||||
}
|
||||
BuzzController.prototype = {
|
||||
userChange: function(){
|
||||
userChange: function() {
|
||||
this.userId = this.$location.hashPath;
|
||||
this.activities = this.Activity.get({userId:this.userId});
|
||||
},
|
||||
@@ -22,7 +22,7 @@ BuzzController.prototype = {
|
||||
if (activity.replies) {
|
||||
activity.replies.show = !activity.replies.show;
|
||||
} else {
|
||||
activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id}, function(){
|
||||
activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id}, function() {
|
||||
activity.replies.show = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
scope = angular.compile(document);
|
||||
scope.$init();
|
||||
scope.a = 3;
|
||||
scope.$eval();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Quantity: <input type="text" name="a" value="2">
|
||||
*
|
||||
Cost: <input type="text" name="b" value="3.4">
|
||||
= {{a * b | currency}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="../angular-minified.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
scope = angular.compile(document);
|
||||
scope.$init();
|
||||
scope.a = 3;
|
||||
scope.$eval();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Quantity: <input type="text" name="a" value="2">
|
||||
*
|
||||
Cost: <input type="text" name="b" value="3.4">
|
||||
= {{a * b | currency}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,21 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="../angular-debug.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
var scope = angular.compile(document);
|
||||
scope.a = 3;
|
||||
scope.$init();
|
||||
scope.$eval();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Quantity: <input type="text" name="a" value="2">
|
||||
*
|
||||
Cost: <input type="text" name="b" value="3.4">
|
||||
= {{a * b | currency}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,55 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery-1.3.2.js"></script>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
var scope = angular.compile(document);
|
||||
scope.init();
|
||||
scope.set("add", function(){
|
||||
var partial = $("#partial");
|
||||
//id++;
|
||||
partial.html('<div>{{ error() }}<br/></div>');
|
||||
var scope = angular.compile(partial);
|
||||
scope.set("hello", function (){
|
||||
return 'who dat?';
|
||||
});
|
||||
scope.set("error", function (){
|
||||
this.misko.length;
|
||||
});
|
||||
function XXXXXXXXX(){};
|
||||
scope.set('xxx', new XXXXXXXXX());
|
||||
scope.set("names", ["adam", "misko", "shyam"]);
|
||||
scope.init();
|
||||
});
|
||||
scope.set("remove", function(){
|
||||
var partial = $("#partial");
|
||||
/*
|
||||
partial.find('*').andSelf().each(function(){
|
||||
var scope = $(this).data('scope');
|
||||
if (scope) {
|
||||
delete scope.state;
|
||||
delete scope.widgets;
|
||||
}
|
||||
var cntl = $(this).data('controller');
|
||||
if (cntl) {
|
||||
delete cntl.view;
|
||||
}
|
||||
$(this).removeData();
|
||||
});
|
||||
*/
|
||||
partial.removeData('scope');
|
||||
partial.children().remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<link rel="StyleSheet" type="text/css" href="../css/angular.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<input type="button" value="add" ng:click="add()"/>
|
||||
<input type="button" value="remove" ng:click="remove()"/>
|
||||
<div id="partial"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,7 +12,7 @@
|
||||
<body ng:controller="example.personalLog.LogCtrl">
|
||||
|
||||
<form action="" ng:submit="addLog(newMsg)">
|
||||
<input type="text" name="newMsg" />
|
||||
<input type="text" ng:model="newMsg" />
|
||||
<input type="submit" value="add" />
|
||||
<input type="button" value="remove all" ng:click="rmLogs()" />
|
||||
</form>
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('personal log', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should create new logs and order them in reverse chronological order', function(){
|
||||
it('should create new logs and order them in reverse chronological order', function() {
|
||||
//create first msg
|
||||
input('newMsg').enter('my first message');
|
||||
element('form input[type="submit"]').click();
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<body ng:class="status" ng:init="mute={}" ng:watch="$anchor.user: tweets = fetchTweets($anchor.user)">
|
||||
<div class="addressbook box">
|
||||
<h1>Address Book</h1>
|
||||
[ Filter: <input type="text" name="userFilter"/>]
|
||||
[ Filter: <input type="text" ng:model="userFilter"/>]
|
||||
<ul>
|
||||
<li ng:repeat="user in users.$filter(userFilter).$orderBy('screen_name')" ng:class-even="'even'" ng:class-odd="'odd'">
|
||||
<a href="" ng:click="$anchor.user=user.screen_name"><img src="{{user.profile_image_url}}"/></a>
|
||||
@@ -29,13 +29,13 @@
|
||||
<div ng:show="$anchor.edituser" ng:eval="user = users.$find({:$.screen_name == $anchor.edituser})">
|
||||
<div class="editor">
|
||||
<label>Username:</label>
|
||||
<input type="text" name="user.screen_name" disabled="disabled"/>
|
||||
<input type="text" ng:model="user.screen_name" disabled="disabled"/>
|
||||
<label>Name:</label>
|
||||
<input type="text" name="user.name"/>
|
||||
<input type="text" ng:model="user.name"/>
|
||||
<label>Image:</label>
|
||||
<input type="text" name="user.profile_image_url"/>
|
||||
<input type="text" ng:model="user.profile_image_url"/>
|
||||
<label>Notes:</label>
|
||||
<textarea type="text" name="user.notes"></textarea>
|
||||
<textarea type="text" ng:model="user.notes"></textarea>
|
||||
|
||||
<input type="button" ng:click="$anchor.edituser=undefined" value="Close"/>
|
||||
</div>
|
||||
@@ -57,7 +57,7 @@ tweets={{tweets}}
|
||||
</div>
|
||||
<div class="tweeter box">
|
||||
<h1>Tweets: {{$anchor.user}}</h1>
|
||||
[ Filter: <input type="text" name="tweetFilter"/>
|
||||
[ Filter: <input type="text" ng:model="tweetFilter"/>
|
||||
<span ng:show="$anchor.user">| <a href="#user="><< All</a></span>
|
||||
]
|
||||
<div class="loading">Loading...</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
(TODO: I should fetch current tweets)
|
||||
<div class="tweeter box">
|
||||
<h1>Tweets: {{$anchor.user}}</h1>
|
||||
[ Filter: <input type="text" name="tweetFilter"/> (TODO: this should act as search box)
|
||||
[ Filter: <input type="text" ng:model="tweetFilter"/> (TODO: this should act as search box)
|
||||
<span ng:show="$anchor.user">| <a href="#user="><< All</a></span>
|
||||
]
|
||||
<div class="loading">Loading...</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
function noop(){}
|
||||
$(document).ready(function(){
|
||||
function noop() {}
|
||||
$(document).ready(function() {
|
||||
function xhr(method, url, data, callback){
|
||||
jQuery.getJSON(url, function(){
|
||||
jQuery.getJSON(url, function() {
|
||||
callback.apply(this, arguments);
|
||||
scope.updateView();
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ if (flag == '--login') {
|
||||
help();
|
||||
}
|
||||
|
||||
function help(){
|
||||
function help() {
|
||||
console.log('Synopsys');
|
||||
console.log('gdocs.js --login <username>');
|
||||
console.log('gdocs.js --fetch [<docs collection>]');
|
||||
@@ -141,7 +141,7 @@ function login(username, password){
|
||||
);
|
||||
}
|
||||
|
||||
function getAuthToken(){
|
||||
function getAuthToken() {
|
||||
var pwdFile = 'tmp/gdocs.auth';
|
||||
try {
|
||||
fs.statSync(pwdFile);
|
||||
@@ -164,10 +164,10 @@ function request(method, url, options, response) {
|
||||
case 200:
|
||||
var data = [];
|
||||
res.setEncoding('utf8');
|
||||
res.on('end', function (){ response(data.join('')); });
|
||||
res.on('close', function (){ response(data.join('')); }); // https
|
||||
res.on('end', function () { response(data.join('')); });
|
||||
res.on('close', function () { response(data.join('')); }); // https
|
||||
res.on('data', function (chunk) { data.push(chunk); });
|
||||
res.on('error', function (e){ console.log(e); });
|
||||
res.on('error', function (e) { console.log(e); });
|
||||
break;
|
||||
case 401:
|
||||
console.log('Eror: Login credentials expired! Please login.');
|
||||
@@ -190,7 +190,7 @@ function request(method, url, options, response) {
|
||||
}
|
||||
if (options.data)
|
||||
request.write(encodeData(options.data));
|
||||
request.on('end', function(){
|
||||
request.on('end', function() {
|
||||
console.log('end');
|
||||
});
|
||||
request.end();
|
||||
@@ -212,7 +212,7 @@ function askPassword(callback) {
|
||||
|
||||
console.log('Enter your password:');
|
||||
var password = "";
|
||||
stdin.on("data", function (c) {
|
||||
stdin.on("data", function(c) {
|
||||
c = c + "";
|
||||
switch (c) {
|
||||
case "\n": case "\r": case "\u0004":
|
||||
@@ -236,7 +236,7 @@ function reflow(text, margin) {
|
||||
text.split(/\n/).forEach(function(line) {
|
||||
var col = 0;
|
||||
var reflowLine = '';
|
||||
function flush(){
|
||||
function flush() {
|
||||
reflowLine = reflowLine.replace(/\s*$/, '');
|
||||
lines.push(reflowLine);
|
||||
reflowLine = '';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
if [ ! -e gen_docs.disable ]; then
|
||||
/usr/bin/env jasmine-node docs/spec -i docs/src -i lib --noColor && node docs/src/gen-docs.js
|
||||
jasmine-node docs/spec --noColor && node docs/src/gen-docs.js
|
||||
fi
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user