aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/content/api/angular.inputType.ngdoc92
-rw-r--r--docs/content/api/angular.service.ngdoc2
-rw-r--r--docs/content/api/index.ngdoc2
-rw-r--r--docs/content/cookbook/advancedform.ngdoc55
-rw-r--r--docs/content/cookbook/buzz.ngdoc3
-rw-r--r--docs/content/cookbook/form.ngdoc37
-rw-r--r--docs/content/cookbook/helloworld.ngdoc13
-rw-r--r--docs/content/guide/dev_guide.compiler.directives.ngdoc2
-rw-r--r--docs/content/guide/dev_guide.expressions.ngdoc27
-rw-r--r--docs/content/guide/dev_guide.forms.ngdoc610
-rw-r--r--docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc4
-rw-r--r--docs/content/guide/dev_guide.mvc.understanding_model.ngdoc2
-rw-r--r--docs/content/guide/dev_guide.overview.ngdoc44
-rw-r--r--docs/content/guide/dev_guide.services.$location.ngdoc2
-rw-r--r--docs/content/guide/dev_guide.services.injecting_controllers.ngdoc4
-rw-r--r--docs/content/guide/dev_guide.templates.css-styling.ngdoc52
-rw-r--r--docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc26
-rw-r--r--docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc55
-rw-r--r--docs/content/guide/dev_guide.templates.formatters.ngdoc20
-rw-r--r--docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc9
-rw-r--r--docs/content/guide/dev_guide.templates.ngdoc9
-rw-r--r--docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc82
-rw-r--r--docs/content/guide/dev_guide.templates.validators.ngdoc131
-rw-r--r--docs/content/guide/index.ngdoc3
-rw-r--r--docs/content/misc/started.ngdoc2
-rw-r--r--docs/content/tutorial/step_03.ngdoc2
-rw-r--r--docs/content/tutorial/step_04.ngdoc4
-rw-r--r--docs/content/tutorial/step_07.ngdoc4
-rw-r--r--docs/content/tutorial/step_09.ngdoc2
-rw-r--r--docs/examples/settings.html8
-rw-r--r--docs/img/form_data_flow.pngbin0 -> 55400 bytes
-rw-r--r--docs/spec/ngdocSpec.js36
-rw-r--r--docs/src/ngdoc.js104
-rw-r--r--docs/src/templates/doc_widgets.js8
-rw-r--r--docs/src/templates/docs.css12
-rw-r--r--docs/src/templates/index.html2
36 files changed, 916 insertions, 554 deletions
diff --git a/docs/content/api/angular.inputType.ngdoc b/docs/content/api/angular.inputType.ngdoc
new file mode 100644
index 00000000..434fe6c2
--- /dev/null
+++ b/docs/content/api/angular.inputType.ngdoc
@@ -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>
diff --git a/docs/content/api/angular.service.ngdoc b/docs/content/api/angular.service.ngdoc
index 874fe4bb..50fe1560 100644
--- a/docs/content/api/angular.service.ngdoc
+++ b/docs/content/api/angular.service.ngdoc
@@ -14,8 +14,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
diff --git a/docs/content/api/index.ngdoc b/docs/content/api/index.ngdoc
index 05928ab4..2ec86346 100644
--- a/docs/content/api/index.ngdoc
+++ b/docs/content/api/index.ngdoc
@@ -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
diff --git a/docs/content/cookbook/advancedform.ngdoc b/docs/content/cookbook/advancedform.ngdoc
index 585c66a6..d38008f2 100644
--- a/docs/content/cookbook/advancedform.ngdoc
+++ b/docs/content/cookbook/advancedform.ngdoc
@@ -9,9 +9,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 = {
@@ -42,31 +40,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:
@@ -90,7 +91,7 @@ master.$equals(form)}}">Save</button>
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>
diff --git a/docs/content/cookbook/buzz.ngdoc b/docs/content/cookbook/buzz.ngdoc
index a1e4a8b2..fad4c1ff 100644
--- a/docs/content/cookbook/buzz.ngdoc
+++ b/docs/content/cookbook/buzz.ngdoc
@@ -15,6 +15,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 +33,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">
diff --git a/docs/content/cookbook/form.ngdoc b/docs/content/cookbook/form.ngdoc
index 2aeafc4d..c74b203b 100644
--- a/docs/content/cookbook/form.ngdoc
+++ b/docs/content/cookbook/form.ngdoc
@@ -24,25 +24,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/>
@@ -68,19 +69,21 @@ ng:validate="regexp:zip"/><br/><br/>
});
it('should validate zip', function(){
- expect(using('.example').element(':input[name="user.address.zip"]').prop('className'))
- .not().toMatch(/ng-validation-error/);
+ 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/);
+ 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>
@@ -94,7 +97,7 @@ available in
* 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 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
diff --git a/docs/content/cookbook/helloworld.ngdoc b/docs/content/cookbook/helloworld.ngdoc
index 8018a399..9562aaff 100644
--- a/docs/content/cookbook/helloworld.ngdoc
+++ b/docs/content/cookbook/helloworld.ngdoc
@@ -5,9 +5,16 @@
<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(){
diff --git a/docs/content/guide/dev_guide.compiler.directives.ngdoc b/docs/content/guide/dev_guide.compiler.directives.ngdoc
index 0f99e46b..3b233551 100644
--- a/docs/content/guide/dev_guide.compiler.directives.ngdoc
+++ b/docs/content/guide/dev_guide.compiler.directives.ngdoc
@@ -16,7 +16,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
diff --git a/docs/content/guide/dev_guide.expressions.ngdoc b/docs/content/guide/dev_guide.expressions.ngdoc
index 177a5e87..ab5a897b 100644
--- a/docs/content/guide/dev_guide.expressions.ngdoc
+++ b/docs/content/guide/dev_guide.expressions.ngdoc
@@ -51,9 +51,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">
@@ -84,9 +90,18 @@ 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>
@@ -158,7 +173,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)">
diff --git a/docs/content/guide/dev_guide.forms.ngdoc b/docs/content/guide/dev_guide.forms.ngdoc
new file mode 100644
index 00000000..6849ff4e
--- /dev/null
+++ b/docs/content/guide/dev_guide.forms.ngdoc
@@ -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 &lt;form&gt;} 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:
+ <div class="error" ng:show="addressForm.state.$error.REQUIRED">
+ Missing state!</span>
+ <div class="error" ng:show="addressForm.state.$error.PATTERN">
+ Invalid state!</span>
+ <div class="error" ng:show="addressForm.zip.$error.REQUIRED">
+ Missing zip!</span>
+ <div 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>&lt;input type="text" ng:model="input1"&gt;</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>&lt;textarea ng:model="input2"&gt;&lt;/textarea&gt;</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>
+ &lt;input type="radio" ng:model="input3" value="A"&gt;<br>
+ &lt;input type="radio" ng:model="input3" value="B"&gt;
+ </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>&lt;input type="checkbox" ng:model="input4"&gt;</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>
+ &lt;select ng:model="input5"&gt;<br>
+ &nbsp;&nbsp;&lt;option value="c"&gt;C&lt;/option&gt;<br>
+ &nbsp;&nbsp;&lt;option value="d"&gt;D&lt;/option&gt;<br>
+ &lt;/select&gt;<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>
+ &lt;select ng:model="input6" multiple size="4"&gt;<br>
+ &nbsp;&nbsp;&lt;option value="e"&gt;E&lt;/option&gt;<br>
+ &nbsp;&nbsp;&lt;option value="f"&gt;F&lt;/option&gt;<br>
+ &lt;/select&gt;<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>
+
+
diff --git a/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
index 15ae3b34..7a6653e9 100644
--- a/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
+++ b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
@@ -68,7 +68,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.
@@ -139,7 +139,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>
diff --git a/docs/content/guide/dev_guide.mvc.understanding_model.ngdoc b/docs/content/guide/dev_guide.mvc.understanding_model.ngdoc
index a35541d0..b4659b0c 100644
--- a/docs/content/guide/dev_guide.mvc.understanding_model.ngdoc
+++ b/docs/content/guide/dev_guide.mvc.understanding_model.ngdoc
@@ -41,7 +41,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".
diff --git a/docs/content/guide/dev_guide.overview.ngdoc b/docs/content/guide/dev_guide.overview.ngdoc
index f5db7f94..fcf15044 100644
--- a/docs/content/guide/dev_guide.overview.ngdoc
+++ b/docs/content/guide/dev_guide.overview.ngdoc
@@ -42,19 +42,27 @@ 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>
@@ -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 }}`:
diff --git a/docs/content/guide/dev_guide.services.$location.ngdoc b/docs/content/guide/dev_guide.services.$location.ngdoc
index 4e0e8548..c0f35c96 100644
--- a/docs/content/guide/dev_guide.services.$location.ngdoc
+++ b/docs/content/guide/dev_guide.services.$location.ngdoc
@@ -612,7 +612,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
diff --git a/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc b/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc
index 0046dd7f..44206f7c 100644
--- a/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc
+++ b/docs/content/guide/dev_guide.services.injecting_controllers.ngdoc
@@ -54,13 +54,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');
+ expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
});
</doc:scenario>
</doc:example>
diff --git a/docs/content/guide/dev_guide.templates.css-styling.ngdoc b/docs/content/guide/dev_guide.templates.css-styling.ngdoc
index 4a4b2d65..4bd3f1b2 100644
--- a/docs/content/guide/dev_guide.templates.css-styling.ngdoc
+++ b/docs/content/guide/dev_guide.templates.css-styling.ngdoc
@@ -4,48 +4,32 @@
@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}
diff --git a/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc b/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc
index ebb7d923..27daec9f 100644
--- a/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc
+++ b/docs/content/guide/dev_guide.templates.filters.creating_filters.ngdoc
@@ -35,20 +35,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>
diff --git a/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc
deleted file mode 100644
index 2ecd8f19..00000000
--- a/docs/content/guide/dev_guide.templates.formatters.creating_formatters.ngdoc
+++ /dev/null
@@ -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>
-
diff --git a/docs/content/guide/dev_guide.templates.formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.ngdoc
deleted file mode 100644
index 82a14fb4..00000000
--- a/docs/content/guide/dev_guide.templates.formatters.ngdoc
+++ /dev/null
@@ -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}
diff --git a/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc b/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc
deleted file mode 100644
index bf983cd5..00000000
--- a/docs/content/guide/dev_guide.templates.formatters.using_formatters.ngdoc
+++ /dev/null
@@ -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>
diff --git a/docs/content/guide/dev_guide.templates.ngdoc b/docs/content/guide/dev_guide.templates.ngdoc
index ca0ca99a..32514eb9 100644
--- a/docs/content/guide/dev_guide.templates.ngdoc
+++ b/docs/content/guide/dev_guide.templates.ngdoc
@@ -18,9 +18,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 +31,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 +53,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
diff --git a/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc b/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc
deleted file mode 100644
index 835b0b51..00000000
--- a/docs/content/guide/dev_guide.templates.validators.creating_validators.ngdoc
+++ /dev/null
@@ -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}
diff --git a/docs/content/guide/dev_guide.templates.validators.ngdoc b/docs/content/guide/dev_guide.templates.validators.ngdoc
deleted file mode 100644
index 76df92b5..00000000
--- a/docs/content/guide/dev_guide.templates.validators.ngdoc
+++ /dev/null
@@ -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}
diff --git a/docs/content/guide/index.ngdoc b/docs/content/guide/index.ngdoc
index b2aab161..8d609afa 100644
--- a/docs/content/guide/index.ngdoc
+++ b/docs/content/guide/index.ngdoc
@@ -42,8 +42,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}
diff --git a/docs/content/misc/started.ngdoc b/docs/content/misc/started.ngdoc
index 3bf71cf1..591fb859 100644
--- a/docs/content/misc/started.ngdoc
+++ b/docs/content/misc/started.ngdoc
@@ -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>
diff --git a/docs/content/tutorial/step_03.ngdoc b/docs/content/tutorial/step_03.ngdoc
index ec546956..89a1b0cb 100644
--- a/docs/content/tutorial/step_03.ngdoc
+++ b/docs/content/tutorial/step_03.ngdoc
@@ -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)">
diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc
index 72aa26c9..d05a8e7c 100644
--- a/docs/content/tutorial/step_04.ngdoc
+++ b/docs/content/tutorial/step_04.ngdoc
@@ -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>
diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc
index fa0c1e1f..eaf7f4ab 100644
--- a/docs/content/tutorial/step_07.ngdoc
+++ b/docs/content/tutorial/step_07.ngdoc
@@ -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>
diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc
index 80b10f65..7d8e3430 100644
--- a/docs/content/tutorial/step_09.ngdoc
+++ b/docs/content/tutorial/step_09.ngdoc
@@ -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
diff --git a/docs/examples/settings.html b/docs/examples/settings.html
index 2fa5dca8..74500b35 100644
--- a/docs/examples/settings.html
+++ b/docs/examples/settings.html
@@ -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> \ No newline at end of file
+<button ng:click="save()">Save</button>
diff --git a/docs/img/form_data_flow.png b/docs/img/form_data_flow.png
new file mode 100644
index 00000000..60e947a5
--- /dev/null
+++ b/docs/img/form_data_flow.png
Binary files differ
diff --git a/docs/spec/ngdocSpec.js b/docs/spec/ngdocSpec.js
index 106fd22b..2afcc3d4 100644
--- a/docs/spec/ngdocSpec.js
+++ b/docs/spec/ngdocSpec.js
@@ -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>');
@@ -543,38 +543,6 @@ 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(){
var doc = new Doc({
diff --git a/docs/src/ngdoc.js b/docs/src/ngdoc.js
index 8a20e64a..1a4f5d25 100644
--- a/docs/src/ngdoc.js
+++ b/docs/src/ngdoc.js
@@ -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') {
@@ -385,69 +390,21 @@ Doc.prototype = {
});
},
- html_usage_formatter: 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('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){
+ html_usage_inputType: 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(')');
+ 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('>');
});
-
self.html_usage_parameters(dom);
- self.html_usage_this(dom);
- self.html_usage_returns(dom);
});
},
@@ -473,11 +430,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);
@@ -533,12 +490,18 @@ Doc.prototype = {
dom.h('Events', this.events, function(event){
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);
@@ -632,10 +595,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,
diff --git a/docs/src/templates/doc_widgets.js b/docs/src/templates/doc_widgets.js
index 17284a1d..72f59f74 100644
--- a/docs/src/templates/doc_widgets.js
+++ b/docs/src/templates/doc_widgets.js
@@ -81,14 +81,16 @@
fiddleSrc = fiddleSrc.replace(new RegExp('^\\s{' + stripIndent + '}', 'gm'), '');
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
- '<textarea name="css">' +
+ '<textarea ng:model="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">' +
+ '<input type="text" ng:model="title" value="AngularJS Live Example">' +
+ '<textarea ng:model="html">' +
'<script src="' + angularJsUrl + '" ng:autobind></script>\n\n' +
'<!-- AngularJS Example Code: -->\n\n' +
fiddleSrc +
diff --git a/docs/src/templates/docs.css b/docs/src/templates/docs.css
index 99ea7454..c38252ff 100644
--- a/docs/src/templates/docs.css
+++ b/docs/src/templates/docs.css
@@ -49,6 +49,10 @@ li {
margin: 0.3em 0 0.3em 0;
}
+.ng-invalid {
+ border: 1px solid red;
+}
+
/*----- Upgrade IE Prompt -----*/
@@ -426,7 +430,7 @@ li {
}
table {
- border-collapse: collapse;
+ border-collapse: collapse;
}
td {
@@ -448,7 +452,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 +463,7 @@ td.empty-corner-lt {
.html5-hashbang-example div input {
width: 360px;
}
+
+.error {
+ color: red;
+}
diff --git a/docs/src/templates/index.html b/docs/src/templates/index.html
index a2def7a6..87c27ac0 100644
--- a/docs/src/templates/index.html
+++ b/docs/src/templates/index.html
@@ -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>