aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVojta Jina2012-02-23 19:47:58 -0800
committerVojta Jina2012-02-28 18:22:35 -0800
commit139e1b09a94f60b351cc961d8bfd882c0b8ba594 (patch)
treee4403c52a9be8db33a991420056c865855bdd6f7
parent60743fc52aea9eabee58258a31f4ba465013cb4e (diff)
downloadangular.js-139e1b09a94f60b351cc961d8bfd882c0b8ba594.tar.bz2
docs(forms): Update API docs for forms
- API forms (ng:model + controller, form + controller) - fix some broken links - ng:change, ng:model-instant
-rw-r--r--docs/content/api/angular.inputType.ngdoc34
-rw-r--r--docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc18
-rw-r--r--docs/content/guide/dev_guide.bootstrap.manual_bootstrap.ngdoc2
-rw-r--r--docs/content/guide/dev_guide.compiler.understanding_compiler.ngdoc8
-rw-r--r--docs/content/guide/dev_guide.scopes.internals.ngdoc8
-rw-r--r--docs/src/templates/docs.css5
-rw-r--r--src/loader.js2
-rw-r--r--src/scenario/dsl.js4
-rw-r--r--src/widget/form.js56
-rw-r--r--src/widget/input.js1242
10 files changed, 776 insertions, 603 deletions
diff --git a/docs/content/api/angular.inputType.ngdoc b/docs/content/api/angular.inputType.ngdoc
deleted file mode 100644
index a5d1f74a..00000000
--- a/docs/content/api/angular.inputType.ngdoc
+++ /dev/null
@@ -1,34 +0,0 @@
-@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 by 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.
diff --git a/docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc b/docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc
index 391a4b47..56dbcbd8 100644
--- a/docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc
+++ b/docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc
@@ -58,8 +58,8 @@ the following example.
During the compilation process the {@link angular.module.ng.$compile compiler} matches text and
attributes using the {@link angular.module.ng.$interpolate $interpolate} service to see if they
contain embedded expressions. These expressions are registered as {@link
-angular.module.ng.$rootScope.Scope#.watch watches} and will update as part of normal {@link
-angular.module.ng.$rootScope.Scope#.digest digest} cycle. An example of interpolation is shown
+angular.module.ng.$rootScope.Scope#$watch watches} and will update as part of normal {@link
+angular.module.ng.$rootScope.Scope#$digest digest} cycle. An example of interpolation is shown
here:
<pre>
@@ -87,7 +87,7 @@ Compilation of HTML happens in three phases:
3. Link the template with scope by calling the liking function returned from the previous step.
This in turn will call the linking function of the individual directives allowing them to
register any listeners on the elements and set up any {@link
- angular.module.ng.$rootScope.Scope#.watch watches} with the {@link
+ angular.module.ng.$rootScope.Scope#$watch watches} with the {@link
angular.module.ng.$rootScope.Scope scope}. The result of this is a live binding between the
scope and the DOM. A change in the scope is reflected in the DOM.
@@ -417,8 +417,8 @@ compiler}. The attributes are:
append the template to the element.
* `transclude` - compile the content of the element and make it available to the directive.
- Typically used with {@link api/angular.module.ng.$compileProvider.directive.ng-transclude
- ng-transclude}. The advantage of transclusion is that the linking function receives a
+ Typically used with {@link api/angular.module.ng.$compileProvider.directive.ng:transclude
+ ng:transclude}. The advantage of transclusion is that the linking function receives a
transclusion function which is pre-bound to the correct scope. In a typical setup the widget
creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
scope. This makes it possible for the widget to have private state, and the transclusion to
@@ -451,7 +451,7 @@ compile functions takes the following arguments.
* `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
between all directive compile functions. See {@link
- angular.module.ng.$compileProvider.directive.Attributes Attributes}
+ #Attributes Attributes}
* `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`.
@@ -470,15 +470,14 @@ executed after the template has been cloned. This is where most of the directive
put.
* `scope` - {@link angular.module.ng.$rootScope.Scope Scope} - The scope to be used by the
- directive for registering {@link angular.module.ng.$rootScope.Scope#.watch watches}.
+ directive for registering {@link angular.module.ng.$rootScope.Scope#$watch watches}.
* `iElement` - instance element - The element where the directive is to be used. It is safe to
manipulate the children of the element only in `postLink` function since the children have
already been linked.
* `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
- between all directive linking functions. See {@link
- angular.module.ng.$compileProvider.directive.Attributes Attributes}
+ between all directive linking functions. See {@link #Attributes Attributes}
* `controller` - a controller instance - A controller instance if at least one directive on the
element defines a controller. The controller is shared among all the directives, which allows
@@ -495,6 +494,7 @@ compiler linking function will fail to locate the correct elements for linking.
Executed after the child elements are linked. Safe to do DOM transformation in here.
+<a name="Attributes"></a>
## Attributes
The attributes object - passed as a parameter in the link() or compile() functions - is a way of
diff --git a/docs/content/guide/dev_guide.bootstrap.manual_bootstrap.ngdoc b/docs/content/guide/dev_guide.bootstrap.manual_bootstrap.ngdoc
index 1c934745..09c3a07b 100644
--- a/docs/content/guide/dev_guide.bootstrap.manual_bootstrap.ngdoc
+++ b/docs/content/guide/dev_guide.bootstrap.manual_bootstrap.ngdoc
@@ -7,7 +7,7 @@ angular, but advanced users who want more control over the initialization proces
the manual bootstrapping method instead.
The best way to get started with manual bootstrapping is to look at the what happens when you use
-{@link api/angular.directive.ng:app ng:app}, by showing each step of the process
+{@link api/angular.module.ng.$compileProvider.directive.ng:app ng:app}, by showing each step of the process
explicitly.
<pre>
diff --git a/docs/content/guide/dev_guide.compiler.understanding_compiler.ngdoc b/docs/content/guide/dev_guide.compiler.understanding_compiler.ngdoc
index e802aee6..8d31b44a 100644
--- a/docs/content/guide/dev_guide.compiler.understanding_compiler.ngdoc
+++ b/docs/content/guide/dev_guide.compiler.understanding_compiler.ngdoc
@@ -2,8 +2,8 @@
@name Developer Guide: Angular HTML Compiler: Understanding How the Compiler Works
@description
-The {@link angular.module.ng.$compile compiler} is responsible for applying
-{@link angular.module.ng.$compileProvider.directive directives} to the HTML. The directives
+The {@link api/angular.module.ng.$compile compiler} is responsible for applying
+{@link api/angular.module.ng.$compileProvider.directive directives} to the HTML. The directives
extend the behavior of HTML elements and can effect the DOM structure, presentation, and behavior.
This allows Angular to teach the browser new tricks.
@@ -13,7 +13,7 @@ function. The result of the compilation process is a linking function. The linki
can be used on the template clones to quickly bind the directives with the scope.
The result of the compilation process is a live view. We say 'live' since any changes to the
-model attached to the {@link angular.module.ng.$rootScope.Scope scope} are reflected in the view,
+model attached to the {@link api/angular.module.ng.$rootScope.Scope scope} are reflected in the view,
and any changes in the view are reflected in the scope. This makes the scope the 'single source of
truth'.
@@ -21,7 +21,7 @@ Since directives allow attachment of behavior to the HTML, the angular philosoph
HTML as Domain Specific Language (DSL) when building an application. For example it may be useful
to declare `TabPanel` directive, or `KeyboardShortcut` directive when for an application.
-For details on how directives are created see {@link angular.module.ng.$compileProvider.directive
+For details on how directives are created see {@link api/angular.module.ng.$compileProvider.directive
directives}
## Related Topics
diff --git a/docs/content/guide/dev_guide.scopes.internals.ngdoc b/docs/content/guide/dev_guide.scopes.internals.ngdoc
index 7cfac09a..a57146a0 100644
--- a/docs/content/guide/dev_guide.scopes.internals.ngdoc
+++ b/docs/content/guide/dev_guide.scopes.internals.ngdoc
@@ -46,7 +46,7 @@ reside on a child scope, if a property read does not find the property on a scop
recursively check the parent scope, grandparent scope, etc. all the way to the root scope before
defaulting to undefined.
-{@link angular.module.ng.$compileProvider.directive directives} associated with elements
+{@link api/angular.module.ng.$compileProvider.directive directives} associated with elements
(ng:controller, ng:repeat, ng:include, etc.) create new child scopes that inherit properties from
the current parent scope. Any code in Angular is free to create a new scope. Whether or not your
code does so is an implementation detail of the directive, that is, you can decide when or if this
@@ -117,9 +117,9 @@ scopes come into play throughout and get a sense of their interactions.
1. At application compile time, a root scope is created and is attached to the root `<HTML>` DOM
element.
2. During the compilation phase, the {@link dev_guide.compiler compiler} matches {@link
-angular.module.ng.$compileProvider.directive directives} against the DOM template. The directives
+api/angular.module.ng.$compileProvider.directive directives} against the DOM template. The directives
usually fall into one of two categories:
- - Observing {@link angular.module.ng.$compileProvider.directive directives}, such as double-curly
+ - Observing {@link api/angular.module.ng.$compileProvider.directive directives}, such as double-curly
expressions `{{expression}}`, register listeners using the {@link
api/angular.module.ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs to
be notified whenever the expression changes so that it can update the view.
@@ -133,7 +133,7 @@ api/angular.module.ng.$rootScope.Scope#$apply $apply()} method so that all liste
### Directives that create scopes
-In most cases, {@link angular.module.ng.$compileProvider.directive directives} and scopes interact but do not create new
+In most cases, {@link api/angular.module.ng.$compileProvider.directive directives} and scopes interact but do not create new
instances of scope. However, some directives, such as {@link api/angular.module.ng.$compileProvider.directive.ng:controller
ng:controller} and {@link api/angular.module.ng.$compileProvider.directive.@ng:repeat ng:repeat}, create new child scopes using
the {@link api/angular.module.ng.$rootScope.Scope#$new $new()} method and then attach the child scope to the
diff --git a/docs/src/templates/docs.css b/docs/src/templates/docs.css
index b3f9aad9..37656b3b 100644
--- a/docs/src/templates/docs.css
+++ b/docs/src/templates/docs.css
@@ -295,6 +295,9 @@ li {
margin-left: 4em;
}
+#content-list .level-6 {
+ margin-left: 5em;
+}
#content-list a.current {
font-weight: bold;
@@ -509,7 +512,7 @@ td.empty-corner-lt {
width: 360px;
}
-.error {
+.doc-example-live .error {
color: red;
}
diff --git a/src/loader.js b/src/loader.js
index 604beaef..315c5c42 100644
--- a/src/loader.js
+++ b/src/loader.js
@@ -171,7 +171,7 @@ function setupModuleLoader(window) {
* @param {Function} directiveFactory Factory function for creating new instance of
* directives.
* @description
- * See {@link angular.module.ng.$compileProvider#directive $compileProvider.directive()}.
+ * See {@link angular.module.ng.$compileProvider.directive $compileProvider.directive()}.
*/
directive: invokeLater('$compileProvider', 'directive'),
diff --git a/src/scenario/dsl.js b/src/scenario/dsl.js
index f6cc8086..8a1bccb1 100644
--- a/src/scenario/dsl.js
+++ b/src/scenario/dsl.js
@@ -199,11 +199,11 @@ angular.scenario.dsl('binding', function() {
angular.scenario.dsl('input', function() {
var chain = {};
- chain.enter = function(value) {
+ chain.enter = function(value, event) {
return this.addFutureAction("input '" + this.name + "' enter '" + value + "'", function($window, $document, done) {
var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
input.val(value);
- input.trigger('blur');
+ input.trigger(event || 'blur');
done();
});
};
diff --git a/src/widget/form.js b/src/widget/form.js
index 23b07107..306581e9 100644
--- a/src/widget/form.js
+++ b/src/widget/form.js
@@ -1,5 +1,27 @@
'use strict';
+
+/**
+ * @ngdoc object
+ * @name angular.module.ng.$compileProvider.directive.form.FormController
+ *
+ * @property {boolean} pristine True if user has not interacted with the form yet.
+ * @property {boolean} dirty True if user has already interacted with the form.
+ * @property {boolean} valid True if all of the containg widgets are valid.
+ * @property {boolean} invalid True if at least one containing widget is invalid.
+ *
+ * @property {Object} error Is an object hash, containing references to all invalid widgets, where
+ *
+ * - keys are error ids (such as `REQUIRED`, `URL` or `EMAIL`),
+ * - values are arrays of widgets that are invalid with given error.
+ *
+ * @description
+ * Form is a controller that keeps track of all registered widgets.
+ *
+ * Each {@link angular.module.ng.$compileProvider.directive.form form} directive creates an instance
+ * of `FormController`.
+ *
+ */
FormController.$inject = ['$scope', 'name'];
function FormController($scope, name) {
var form = this,
@@ -73,6 +95,19 @@ function FormController($scope, name) {
}
}
+/**
+ * @ngdoc function
+ * @name angular.module.ng.$compileProvider.directive.form.FormController#registerWidget
+ * @methodOf angular.module.ng.$compileProvider.directive.form.FormController
+ * @function
+ *
+ * @param {Object} widget Widget to register (controller of a widget)
+ * @param {string=} alias Name alias of the widget.
+ * (If specified, widget will be accesible as a form property)
+ *
+ * @description
+ *
+ */
FormController.prototype.registerWidget = function(widget, alias) {
if (alias && !this.hasOwnProperty(alias)) {
widget.widgetId = alias;
@@ -82,16 +117,15 @@ FormController.prototype.registerWidget = function(widget, alias) {
/**
- * @ngdoc directive
+ * @ngdoc widget
* @name angular.module.ng.$compileProvider.directive.form
*
* @scope
* @description
- * Angular widget that creates a form scope using the
- * {@link angular.module.ng.$formFactory $formFactory} API. The resulting form scope instance is
- * attached to the DOM element using the jQuery `.data()` method under the `$form` key.
- * See {@link guide/dev_guide.forms forms} on detailed discussion of forms and widgets.
+ * Directive that instantiates
+ * {@link angular.module.ng.$compileProvider.directive.form.FormController Form} controller.
*
+ * If `name` attribute is specified, the controller is published to the scope as well.
*
* # Alias: `ng:form`
*
@@ -101,9 +135,16 @@ FormController.prototype.registerWidget = function(widget, alias) {
* element nesting.
*
*
+ * # CSS classes
+ * - `ng-valid` Is set if the form is valid.
+ * - `ng-invalid` Is set if the form is invalid.
+ * - `ng-pristine` Is set if the form is pristine.
+ * - `ng-dirty` Is set if the form is dirty.
+ *
+ *
* # Submitting a form and preventing default action
*
- * Since the role of forms in client-side Angular applications is different than in old-school
+ * Since the role of forms in client-side Angular applications is different than in classical
* roundtrip apps, it is desirable for the browser not to translate the form submission into a full
* page reload that sends the data to the server. Instead some javascript logic should be triggered
* to handle the form submission in application specific way.
@@ -128,7 +169,8 @@ FormController.prototype.registerWidget = function(widget, alias) {
* hitting enter in any of the input fields will trigger the click handler on the *first* button or
* input[type=submit] (`ng:click`) *and* a submit handler on the enclosing form (`ng:submit`)
*
- * @param {string=} name Name of the form.
+ * @param {string=} name Name of the form. If specified, the form controller will be published into
+ * related scope, under this name.
*
* @example
<doc:example>
diff --git a/src/widget/input.js b/src/widget/input.js
index 6c95327c..f3590e7d 100644
--- a/src/widget/input.js
+++ b/src/widget/input.js
@@ -1,568 +1,358 @@
'use strict';
-
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
+var inputType = {
-/**
- * @ngdoc inputType
- * @name angular.inputType.text
- *
- * @description
- * Standard HTML text input with angular data binding.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.text = 'guest';
- $scope.word = /^\w*$/;
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- Single word: <input type="text" name="input" ng:model="text"
- ng:pattern="word" required>
- <span class="error" ng:show="myForm.input.error.REQUIRED">
- Required!</span>
- <span class="error" ng:show="myForm.input.error.PATTERN">
- Single word only!</span>
-
- <tt>text = {{text}}</tt><br/>
- <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
- <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
- <tt>myForm.valid = {{myForm.valid}}</tt><br/>
- <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('text')).toEqual('guest');
- expect(binding('myForm.input.valid')).toEqual('true');
- });
-
- it('should be invalid if empty', function() {
- input('text').enter('');
- expect(binding('text')).toEqual('');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
-
- it('should be invalid if multi word', function() {
- input('text').enter('hello world');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
-
-
-/**
- * @ngdoc inputType
- * @name angular.inputType.email
- *
- * @description
- * Text input with email validation. Sets the `EMAIL` validation error key if not a valid email
- * address.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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>
- function Ctrl($scope) {
- $scope.text = 'me@example.com';
- }
- </script>
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.text
+ *
+ * @description
+ * Standard HTML text input with angular data binding.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.text = 'guest';
+ $scope.word = /^\w*$/;
+ }
+ </script>
<form name="myForm" ng:controller="Ctrl">
- Email: <input type="email" name="input" ng:model="text" required>
+ Single word: <input type="text" name="input" ng:model="text"
+ ng:pattern="word" required>
<span class="error" ng:show="myForm.input.error.REQUIRED">
Required!</span>
- <span class="error" ng:show="myForm.input.error.EMAIL">
- Not valid email!</span>
+ <span class="error" ng:show="myForm.input.error.PATTERN">
+ Single word only!</span>
+
<tt>text = {{text}}</tt><br/>
<tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
<tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
<tt>myForm.valid = {{myForm.valid}}</tt><br/>
<tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
- <tt>myForm.error.EMAIL = {{!!myForm.error.EMAIL}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('text')).toEqual('me@example.com');
- expect(binding('myForm.input.valid')).toEqual('true');
- });
-
- it('should be invalid if empty', function() {
- input('text').enter('');
- expect(binding('text')).toEqual('');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
-
- it('should be invalid if not email', function() {
- input('text').enter('xxx');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
-
-
-/**
- * @ngdoc inputType
- * @name angular.inputType.url
- *
- * @description
- * Text input with URL validation. Sets the `URL` validation error key if the content is not a
- * valid URL.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.text = 'http://google.com';
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- URL: <input type="url" name="input" ng:model="text" required>
- <span class="error" ng:show="myForm.input.error.REQUIRED">
- Required!</span>
- <span class="error" ng:show="myForm.input.error.url">
- Not valid url!</span>
- <tt>text = {{text}}</tt><br/>
- <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
- <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
- <tt>myForm.valid = {{myForm.valid}}</tt><br/>
- <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
- <tt>myForm.error.url = {{!!myForm.error.url}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('text')).toEqual('http://google.com');
- expect(binding('myForm.input.valid')).toEqual('true');
- });
-
- it('should be invalid if empty', function() {
- input('text').enter('');
- expect(binding('text')).toEqual('');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
-
- it('should be invalid if not url', function() {
- input('text').enter('xxx');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
-
-
-/**
- * @ngdoc inputType
- * @name angular.inputType.list
- *
- * @description
- * Text input that converts between comma-seperated string into an array of strings.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.names = ['igor', 'misko', 'vojta'];
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- List: <input type="list" name="input" ng:model="names" required>
- <span class="error" ng:show="myForm.list.error.REQUIRED">
- Required!</span>
- <tt>names = {{names}}</tt><br/>
- <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
- <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
- <tt>myForm.valid = {{myForm.valid}}</tt><br/>
- <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('names')).toEqual('["igor","misko","vojta"]');
- expect(binding('myForm.input.valid')).toEqual('true');
- });
-
- it('should be invalid if empty', function() {
- input('names').enter('');
- expect(binding('names')).toEqual('');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
-var ngListDirective = function() {
- return {
- require: 'ngModel',
- link: function(scope, element, attr, ctrl) {
- var parse = function(viewValue) {
- var list = [];
-
- if (viewValue) {
- forEach(viewValue.split(/\s*,\s*/), function(value) {
- if (value) list.push(value);
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('text')).toEqual('guest');
+ expect(binding('myForm.input.valid')).toEqual('true');
});
- }
-
- return list;
- };
- ctrl.parsers.push(parse);
- ctrl.formatters.push(function(value) {
- if (isArray(value) && !equals(parse(ctrl.viewValue), value)) {
- return value.join(', ');
- }
-
- return undefined;
- });
- }
- };
-};
+ it('should be invalid if empty', function() {
+ input('text').enter('');
+ expect(binding('text')).toEqual('');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
-/**
- * @ngdoc inputType
- * @name angular.inputType.number
- *
- * @description
- * Text input with number validation and transformation. Sets the `NUMBER` validation
- * error if not a valid number.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} min Sets the `MIN` validation error key if the value entered is less then `min`.
- * @param {string=} max Sets the `MAX` validation error key if the value entered is greater then `min`.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.value = 12;
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- Number: <input type="number" name="input" ng:model="value"
- min="0" max="99" required>
- <span class="error" ng:show="myForm.list.error.REQUIRED">
- Required!</span>
- <span class="error" ng:show="myForm.list.error.NUMBER">
- Not valid number!</span>
- <tt>value = {{value}}</tt><br/>
- <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
- <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
- <tt>myForm.valid = {{myForm.valid}}</tt><br/>
- <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('value')).toEqual('12');
- expect(binding('myForm.input.valid')).toEqual('true');
- });
+ it('should be invalid if multi word', function() {
+ input('text').enter('hello world');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+ 'text': textInputType,
- it('should be invalid if empty', function() {
- input('value').enter('');
- expect(binding('value')).toEqual('');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- it('should be invalid if over max', function() {
- input('value').enter('123');
- expect(binding('value')).toEqual('12');
- expect(binding('myForm.input.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.number
+ *
+ * @description
+ * Text input with number validation and transformation. Sets the `NUMBER` validation
+ * error if not a valid number.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} min Sets the `MIN` validation error key if the value entered is less then `min`.
+ * @param {string=} max Sets the `MAX` validation error key if the value entered is greater then `min`.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.value = 12;
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ Number: <input type="number" name="input" ng:model="value"
+ min="0" max="99" required>
+ <span class="error" ng:show="myForm.list.error.REQUIRED">
+ Required!</span>
+ <span class="error" ng:show="myForm.list.error.NUMBER">
+ Not valid number!</span>
+ <tt>value = {{value}}</tt><br/>
+ <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
+ <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
+ <tt>myForm.valid = {{myForm.valid}}</tt><br/>
+ <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('value')).toEqual('12');
+ expect(binding('myForm.input.valid')).toEqual('true');
+ });
+ it('should be invalid if empty', function() {
+ input('value').enter('');
+ expect(binding('value')).toEqual('');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
-/**
- * @ngdoc inputType
- * @name angular.inputType.checkbox
- *
- * @description
- * HTML checkbox.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} ng:true-value The value to which the expression should be set when selected.
- * @param {string=} ng:false-value The value to which the expression should be set when not selected.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.value1 = true;
- $scope.value2 = 'YES'
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- Value1: <input type="checkbox" ng:model="value1"> <br/>
- Value2: <input type="checkbox" ng:model="value2"
- ng:true-value="YES" ng:false-value="NO"> <br/>
- <tt>value1 = {{value1}}</tt><br/>
- <tt>value2 = {{value2}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should change state', function() {
- expect(binding('value1')).toEqual('true');
- expect(binding('value2')).toEqual('YES');
-
- input('value1').check();
- input('value2').check();
- expect(binding('value1')).toEqual('false');
- expect(binding('value2')).toEqual('NO');
- });
- </doc:scenario>
- </doc:example>
- */
+ it('should be invalid if over max', function() {
+ input('value').enter('123');
+ expect(binding('value')).toEqual('12');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+ 'number': numberInputType,
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.url
+ *
+ * @description
+ * Text input with URL validation. Sets the `URL` validation error key if the content is not a
+ * valid URL.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.text = 'http://google.com';
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ URL: <input type="url" name="input" ng:model="text" required>
+ <span class="error" ng:show="myForm.input.error.REQUIRED">
+ Required!</span>
+ <span class="error" ng:show="myForm.input.error.url">
+ Not valid url!</span>
+ <tt>text = {{text}}</tt><br/>
+ <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
+ <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
+ <tt>myForm.valid = {{myForm.valid}}</tt><br/>
+ <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
+ <tt>myForm.error.url = {{!!myForm.error.url}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('text')).toEqual('http://google.com');
+ expect(binding('myForm.input.valid')).toEqual('true');
+ });
-/**
- * @ngdoc inputType
- * @name angular.inputType.radio
- *
- * @description
- * HTML radio button.
- *
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string} value The value to which the expression should be set when selected.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.color = 'blue';
- }
- </script>
- <form name="myForm" ng:controller="Ctrl">
- <input type="radio" ng:model="color" value="red"> Red <br/>
- <input type="radio" ng:model="color" value="green"> Green <br/>
- <input type="radio" ng:model="color" value="blue"> Blue <br/>
- <tt>color = {{color}}</tt><br/>
- </form>
- </doc:source>
- <doc:scenario>
- it('should change state', function() {
- expect(binding('color')).toEqual('blue');
+ it('should be invalid if empty', function() {
+ input('text').enter('');
+ expect(binding('text')).toEqual('');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
- input('color').select('red');
- expect(binding('color')).toEqual('red');
- });
- </doc:scenario>
- </doc:example>
- */
+ it('should be invalid if not url', function() {
+ input('text').enter('xxx');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+ 'url': urlInputType,
-/**
- * @ngdoc widget
- * @name angular.module.ng.$compileProvider.directive.input
- *
- * @description
- * HTML input element widget with angular data-binding. Input widget follows HTML5 input types
- * and polyfills the HTML5 validation behavior for older browsers.
- *
- * The {@link angular.inputType custom angular.inputType}s provide a shorthand for declaring new
- * inputs. This is a sharthand for text-box based inputs, and there is no need to go through the
- * full {@link angular.module.ng.$formFactory $formFactory} widget lifecycle.
- *
- *
- * @param {string} type Widget types as defined by {@link angular.inputType}. If the
- * type is in the format of `@ScopeType` then `ScopeType` is loaded from the
- * current scope, allowing quick definition of type.
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- *
- * @example
- <doc:example>
- <doc:source>
- <script>
- function Ctrl($scope) {
- $scope.user = {name: 'guest', last: 'visitor'};
- }
- </script>
- <div ng:controller="Ctrl">
- <form name="myForm">
- User name: <input type="text" name="userName" ng:model="user.name" required>
- <span class="error" ng:show="myForm.userName.error.REQUIRED">
- Required!</span><br>
- Last name: <input type="text" name="lastName" ng:model="user.last"
- ng:minlength="3" ng:maxlength="10">
- <span class="error" ng:show="myForm.lastName.error.MINLENGTH">
- Too short!</span>
- <span class="error" ng:show="myForm.lastName.error.MAXLENGTH">
- Too long!</span><br>
- </form>
- <hr>
- <tt>user = {{user}}</tt><br/>
- <tt>myForm.userName.valid = {{myForm.userName.valid}}</tt><br>
- <tt>myForm.userName.error = {{myForm.userName.error}}</tt><br>
- <tt>myForm.lastName.valid = {{myForm.lastName.valid}}</tt><br>
- <tt>myForm.userName.error = {{myForm.lastName.error}}</tt><br>
- <tt>myForm.valid = {{myForm.valid}}</tt><br>
- <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br>
- <tt>myForm.error.MINLENGTH = {{!!myForm.error.MINLENGTH}}</tt><br>
- <tt>myForm.error.MAXLENGTH = {{!!myForm.error.MAXLENGTH}}</tt><br>
- </div>
- </doc:source>
- <doc:scenario>
- it('should initialize to model', function() {
- expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
- expect(binding('myForm.userName.valid')).toEqual('true');
- expect(binding('myForm.valid')).toEqual('true');
- });
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.email
+ *
+ * @description
+ * Text input with email validation. Sets the `EMAIL` validation error key if not a valid email
+ * address.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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>
+ function Ctrl($scope) {
+ $scope.text = 'me@example.com';
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ Email: <input type="email" name="input" ng:model="text" required>
+ <span class="error" ng:show="myForm.input.error.REQUIRED">
+ Required!</span>
+ <span class="error" ng:show="myForm.input.error.EMAIL">
+ Not valid email!</span>
+ <tt>text = {{text}}</tt><br/>
+ <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
+ <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
+ <tt>myForm.valid = {{myForm.valid}}</tt><br/>
+ <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
+ <tt>myForm.error.EMAIL = {{!!myForm.error.EMAIL}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('text')).toEqual('me@example.com');
+ expect(binding('myForm.input.valid')).toEqual('true');
+ });
- it('should be invalid if empty when required', function() {
- input('user.name').enter('');
- expect(binding('user')).toEqual('{"last":"visitor","name":null}');
- expect(binding('myForm.userName.valid')).toEqual('false');
- expect(binding('myForm.valid')).toEqual('false');
- });
+ it('should be invalid if empty', function() {
+ input('text').enter('');
+ expect(binding('text')).toEqual('');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
- it('should be valid if empty when min length is set', function() {
- input('user.last').enter('');
- expect(binding('user')).toEqual('{"last":"","name":"guest"}');
- expect(binding('myForm.lastName.valid')).toEqual('true');
- expect(binding('myForm.valid')).toEqual('true');
- });
+ it('should be invalid if not email', function() {
+ input('text').enter('xxx');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+ 'email': emailInputType,
- it('should be invalid if less than required min length', function() {
- input('user.last').enter('xx');
- expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
- expect(binding('myForm.lastName.valid')).toEqual('false');
- expect(binding('myForm.lastName.error')).toMatch(/MINLENGTH/);
- expect(binding('myForm.valid')).toEqual('false');
- });
- it('should be valid if longer than max length', function() {
- input('user.last').enter('some ridiculously long name');
- expect(binding('user'))
- .toEqual('{"last":"visitor","name":"guest"}');
- expect(binding('myForm.lastName.valid')).toEqual('false');
- expect(binding('myForm.lastName.error')).toMatch(/MAXLENGTH/);
- expect(binding('myForm.valid')).toEqual('false');
- });
- </doc:scenario>
- </doc:example>
- */
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.radio
+ *
+ * @description
+ * HTML radio button.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string} value The value to which the expression should be set when selected.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.color = 'blue';
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ <input type="radio" ng:model="color" value="red"> Red <br/>
+ <input type="radio" ng:model="color" value="green"> Green <br/>
+ <input type="radio" ng:model="color" value="blue"> Blue <br/>
+ <tt>color = {{color}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should change state', function() {
+ expect(binding('color')).toEqual('blue');
+
+ input('color').select('red');
+ expect(binding('color')).toEqual('red');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+ 'radio': radioInputType,
-/**
- * @ngdoc widget
- * @name angular.module.ng.$compileProvider.directive.textarea
- *
- * @description
- * HTML textarea element widget with angular data-binding. The data-binding and validation
- * properties of this element are exactly the same as those of the
- * {@link angular.module.ng.$compileProvider.directive.input input element}.
- *
- * @param {string} type Widget types as defined by {@link angular.inputType}. If the
- * type is in the format of `@ScopeType` then `ScopeType` is loaded from the
- * current scope, allowing quick definition of type.
- * @param {string} ng:model Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the widgets is published.
- * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
- * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
- * minlength.
- * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
- * maxlength.
- * @param {string=} 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.
- * @param {string=} ng:change Angular expression to be executed when input changes due to user
- * interaction with the input element.
- */
-var inputType = {
- 'text': textInputType,
- 'number': numberInputType,
- 'url': urlInputType,
- 'email': emailInputType,
-
- 'radio': radioInputType,
+ /**
+ * @ngdoc inputType
+ * @name angular.module.ng.$compileProvider.directive.input.checkbox
+ *
+ * @description
+ * HTML checkbox.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} ng:true-value The value to which the expression should be set when selected.
+ * @param {string=} ng:false-value The value to which the expression should be set when not selected.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.value1 = true;
+ $scope.value2 = 'YES'
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ Value1: <input type="checkbox" ng:model="value1"> <br/>
+ Value2: <input type="checkbox" ng:model="value2"
+ ng:true-value="YES" ng:false-value="NO"> <br/>
+ <tt>value1 = {{value1}}</tt><br/>
+ <tt>value2 = {{value2}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should change state', function() {
+ expect(binding('value1')).toEqual('true');
+ expect(binding('value2')).toEqual('YES');
+
+ input('value1').check();
+ input('value2').check();
+ expect(binding('value1')).toEqual('false');
+ expect(binding('value2')).toEqual('NO');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
'checkbox': checkboxInputType,
'hidden': noop,
@@ -808,6 +598,123 @@ function checkboxInputType(scope, element, attr, ctrl) {
}
+/**
+ * @ngdoc widget
+ * @name angular.module.ng.$compileProvider.directive.textarea
+ *
+ * @description
+ * HTML textarea element widget with angular data-binding. The data-binding and validation
+ * properties of this element are exactly the same as those of the
+ * {@link angular.module.ng.$compileProvider.directive.input input element}.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ */
+
+
+/**
+ * @ngdoc widget
+ * @name angular.module.ng.$compileProvider.directive.input
+ *
+ * @description
+ * HTML input element widget with angular data-binding. Input widget follows HTML5 input types
+ * and polyfills the HTML5 validation behavior for older browsers.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {number=} ng:minlength Sets `MINLENGTH` validation error key if the value is shorter than
+ * minlength.
+ * @param {number=} ng:maxlength Sets `MAXLENGTH` validation error key if the value is longer than
+ * maxlength.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.user = {name: 'guest', last: 'visitor'};
+ }
+ </script>
+ <div ng:controller="Ctrl">
+ <form name="myForm">
+ User name: <input type="text" name="userName" ng:model="user.name" required>
+ <span class="error" ng:show="myForm.userName.error.REQUIRED">
+ Required!</span><br>
+ Last name: <input type="text" name="lastName" ng:model="user.last"
+ ng:minlength="3" ng:maxlength="10">
+ <span class="error" ng:show="myForm.lastName.error.MINLENGTH">
+ Too short!</span>
+ <span class="error" ng:show="myForm.lastName.error.MAXLENGTH">
+ Too long!</span><br>
+ </form>
+ <hr>
+ <tt>user = {{user}}</tt><br/>
+ <tt>myForm.userName.valid = {{myForm.userName.valid}}</tt><br>
+ <tt>myForm.userName.error = {{myForm.userName.error}}</tt><br>
+ <tt>myForm.lastName.valid = {{myForm.lastName.valid}}</tt><br>
+ <tt>myForm.userName.error = {{myForm.lastName.error}}</tt><br>
+ <tt>myForm.valid = {{myForm.valid}}</tt><br>
+ <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br>
+ <tt>myForm.error.MINLENGTH = {{!!myForm.error.MINLENGTH}}</tt><br>
+ <tt>myForm.error.MAXLENGTH = {{!!myForm.error.MAXLENGTH}}</tt><br>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
+ expect(binding('myForm.userName.valid')).toEqual('true');
+ expect(binding('myForm.valid')).toEqual('true');
+ });
+
+ it('should be invalid if empty when required', function() {
+ input('user.name').enter('');
+ expect(binding('user')).toEqual('{"last":"visitor","name":null}');
+ expect(binding('myForm.userName.valid')).toEqual('false');
+ expect(binding('myForm.valid')).toEqual('false');
+ });
+
+ it('should be valid if empty when min length is set', function() {
+ input('user.last').enter('');
+ expect(binding('user')).toEqual('{"last":"","name":"guest"}');
+ expect(binding('myForm.lastName.valid')).toEqual('true');
+ expect(binding('myForm.valid')).toEqual('true');
+ });
+
+ it('should be invalid if less than required min length', function() {
+ input('user.last').enter('xx');
+ expect(binding('user')).toEqual('{"last":"visitor","name":"guest"}');
+ expect(binding('myForm.lastName.valid')).toEqual('false');
+ expect(binding('myForm.lastName.error')).toMatch(/MINLENGTH/);
+ expect(binding('myForm.valid')).toEqual('false');
+ });
+
+ it('should be valid if longer than max length', function() {
+ input('user.last').enter('some ridiculously long name');
+ expect(binding('user'))
+ .toEqual('{"last":"visitor","name":"guest"}');
+ expect(binding('myForm.lastName.valid')).toEqual('false');
+ expect(binding('myForm.lastName.error')).toMatch(/MAXLENGTH/);
+ expect(binding('myForm.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
var inputDirective = [function() {
return {
restrict: 'E',
@@ -821,6 +728,28 @@ var inputDirective = [function() {
}];
+/**
+ * @ngdoc object
+ * @name angular.module.ng.$compileProvider.directive.ng:model.NgModelController
+ *
+ * @property {string} viewValue Actual string value in the view.
+ * @property {*} modelValue The value in the model, that the widget is bound to.
+ * @property {Array.<Function>} parsers Whenever the widget reads value from the DOM, it executes
+ * all of these functions to sanitize / convert the value as well as validate.
+ *
+ * @property {Array.<Function>} formatters Wheneveer the model value changes, it executes all of
+ * these functions to convert the value as well as validate.
+ *
+ * @property {Object} error An bject hash with all errors as keys.
+ *
+ * @property {boolean} pristine True if user has not interacted with the widget yet.
+ * @property {boolean} dirty True if user has already interacted with the widget.
+ * @property {boolean} valid True if there is no error.
+ * @property {boolean} invalid True if at least one error on the widget.
+ *
+ * @description
+ *
+ */
var NgModelController = ['$scope', '$exceptionHandler', 'ngModel',
function($scope, $exceptionHandler, ngModel) {
this.viewValue = Number.NaN;
@@ -834,6 +763,21 @@ var NgModelController = ['$scope', '$exceptionHandler', 'ngModel',
this.invalid = false;
this.render = noop;
+
+ /**
+ * @ngdoc function
+ * @name angular.module.ng.$compileProvider.directive.ng:model.NgModelController#touch
+ * @methodOf angular.module.ng.$compileProvider.directive.ng:model.NgModelController
+ *
+ * @return {boolean} Whether it did change state.
+ *
+ * @description
+ * This method should be called from within a DOM event handler.
+ * For example {@link angular.module.ng.$compileProvider.directive.input input} or
+ * {@link angular.module.ng.$compileProvider.directive.select select} directives call it.
+ *
+ * It changes state to `dirty` and emits `$viewTouch` event if the state was `pristine` before.
+ */
this.touch = function() {
if (this.dirty) return false;
@@ -847,31 +791,59 @@ var NgModelController = ['$scope', '$exceptionHandler', 'ngModel',
return true;
};
- // don't $emit valid if already valid, the same for $invalid
- // not sure about this method name, should the argument be reversed ? emitError ?
+
+ /**
+ * @ngdoc function
+ * @name angular.module.ng.$compileProvider.directive.ng:model.NgModelController#emitValidity
+ * @methodOf angular.module.ng.$compileProvider.directive.ng:model.NgModelController
+ *
+ * @description
+ * Change the validity state, and notifies the form when the widget changes validity. (i.e. does
+ * not emit `$invalid` if given validator is already marked as invalid).
+ *
+ * This method should be called by validators - ie the parser or formatter method.
+ *
+ * @param {string} name Name of the validator.
+ * @param {boolean} isValid Whether it should $emit `$valid` (true) or `$invalid` (false) event.
+ */
this.emitValidity = function(name, isValid) {
if (!isValid && this.error[name]) return;
if (isValid && !this.error[name]) return;
- if (!isValid) {
- this.error[name] = true;
- this.invalid = true;
- this.valid = false;
- }
-
if (isValid) {
delete this.error[name];
if (equals(this.error, {})) {
this.valid = true;
this.invalid = false;
}
+ } else {
+ this.error[name] = true;
+ this.invalid = true;
+ this.valid = false;
}
return $scope.$emit(isValid ? '$valid' : '$invalid', name, this);
};
- // view -> model
+
+ /**
+ * @ngdoc function
+ * @name angular.module.ng.$compileProvider.directive.ng:model.NgModelController#read
+ * @methodOf angular.module.ng.$compileProvider.directive.ng:model.NgModelController
+ *
+ * @description
+ * Read a value from view.
+ *
+ * This method should be called from within a DOM event handler.
+ * For example {@link angular.module.ng.$compileProvider.directive.input input} or
+ * {@link angular.module.ng.$compileProvider.directive.select select} directives call it.
+ *
+ * It internally calls all `formatters` and if resulted value is valid, update the model and emits
+ * `$viewChange` event afterwards.
+ *
+ * @param {string} value Value from the view
+ */
this.read = function(value) {
this.viewValue = value;
@@ -911,6 +883,37 @@ var NgModelController = ['$scope', '$exceptionHandler', 'ngModel',
}];
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:model
+ *
+ * @element input
+ *
+ * @description
+ * Is directive that tells Angular to do two-way data binding. It works together with `input`, `select`, `textarea`. You can easily write your own directives to use `ng:model` pretty easily.
+ *
+ * `ng:model` is responsible for:
+ *
+ * - binding the view into the model, which other directives such as `input`, `textarea` or `select`
+ * require,
+ * - providing validation behavior (i.e. required, number, email, url),
+ * - keeping state of the widget (valid/invalid, dirty/pristine, validation errors),
+ * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`),
+ * - register the widget with parent {@link angular.module.ng.$compileProvider.directive.form form}.
+ *
+ * For examples, how to use `ng:model`, see:
+ *
+ * - {@link angular.module.ng.$compileProvider.directive.input input}
+ * - {@link angular.module.ng.$compileProvider.directive.input.text text}
+ * - {@link angular.module.ng.$compileProvider.directive.input.checkbox checkbox}
+ * - {@link angular.module.ng.$compileProvider.directive.input.radio radio}
+ * - {@link angular.module.ng.$compileProvider.directive.input.number number}
+ * - {@link angular.module.ng.$compileProvider.directive.input.email email}
+ * - {@link angular.module.ng.$compileProvider.directive.input.url url}
+ * - {@link angular.module.ng.$compileProvider.directive.select select}
+ * - {@link angular.module.ng.$compileProvider.directive.textarea textarea}
+ *
+ */
var ngModelDirective = [function() {
return {
inject: {
@@ -942,6 +945,53 @@ var ngModelDirective = [function() {
}];
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:change
+ *
+ * @description
+ * Evaluate given expression when user changes the input.
+ * The expression is not evaluated when the value change is coming from the model.
+ *
+ * Note, this directive requires `ng:model` to be present.
+ *
+ * @element input
+ *
+ * @example
+ * <doc:example>
+ * <doc:source>
+ * <script>
+ * function Controller($scope) {
+ * $scope.counter = 0;
+ * $scope.change = function() {
+ * $scope.counter++;
+ * };
+ * }
+ * </script>
+ * <div ng:controller="Controller">
+ * <input type="checkbox" ng:model="confirmed" ng:change="change()" id="ng-change-example1" />
+ * <input type="checkbox" ng:model="confirmed" id="ng-change-example2" />
+ * <label for="ng-change-example2">Confirmed</label><br />
+ * debug = {{confirmed}}<br />
+ * counter = {{counter}}
+ * </div>
+ * </doc:source>
+ * <doc:scenario>
+ * it('should evaluate the expression if changing from view', function() {
+ * expect(binding('counter')).toEqual('0');
+ * element('#ng-change-example1').click();
+ * expect(binding('counter')).toEqual('1');
+ * expect(binding('confirmed')).toEqual('true');
+ * });
+ *
+ * it('should not evaluate the expression if changing from model', function() {
+ * element('#ng-change-example2').click();
+ * expect(binding('counter')).toEqual('0');
+ * expect(binding('confirmed')).toEqual('true');
+ * });
+ * </doc:scenario>
+ * </doc:example>
+ */
var ngChangeDirective = valueFn({
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
@@ -952,6 +1002,38 @@ var ngChangeDirective = valueFn({
});
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:bind-immediate
+ *
+ * @element input
+ *
+ * @description
+ * By default, Angular udpates the model only on `blur` event - when the input looses focus.
+ * If you want to update after every key stroke, use `ng:bind-immediate`.
+ *
+ * @example
+ * <doc:example>
+ * <doc:source>
+ * First name: <input type="text" ng:model="firstName" /><br />
+ * Last name: <input type="text" ng:model="lastName" ng:bind-immediate /><br />
+ *
+ * First name ({{firstName}}) is only updated on `blur` event, but the last name ({{lastName}})
+ * is updated immediately, because of using `ng:bind-immediate`.
+ * </doc:source>
+ * <doc:scenario>
+ * it('should update first name on blur', function() {
+ * input('firstName').enter('santa', 'blur');
+ * expect(binding('firstName')).toEqual('santa');
+ * });
+ *
+ * it('should update last name immediately', function() {
+ * input('lastName').enter('santa', 'keydown');
+ * expect(binding('lastName')).toEqual('santa');
+ * });
+ * </doc:scenario>
+ * </doc:example>
+ */
var ngBindImmediateDirective = ['$browser', function($browser) {
return {
require: 'ngModel',
@@ -1005,3 +1087,83 @@ var requiredDirective = [function() {
}
};
}];
+
+
+/**
+ * @ngdoc directive
+ * @name angular.module.ng.$compileProvider.directive.ng:list
+ *
+ * @description
+ * Text input that converts between comma-seperated string into an array of strings.
+ *
+ * @param {string} ng:model Assignable angular expression to data-bind to.
+ * @param {string=} name Property name of the form under which the widgets is published.
+ * @param {string=} required Sets `REQUIRED` validation error key if the value is not entered.
+ * @param {string=} 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.
+ * @param {string=} ng:change Angular expression to be executed when input changes due to user
+ * interaction with the input element.
+ *
+ * @element input
+ *
+ * @example
+ <doc:example>
+ <doc:source>
+ <script>
+ function Ctrl($scope) {
+ $scope.names = ['igor', 'misko', 'vojta'];
+ }
+ </script>
+ <form name="myForm" ng:controller="Ctrl">
+ List: <input type="list" name="input" ng:model="names" required>
+ <span class="error" ng:show="myForm.list.error.REQUIRED">
+ Required!</span>
+ <tt>names = {{names}}</tt><br/>
+ <tt>myForm.input.valid = {{myForm.input.valid}}</tt><br/>
+ <tt>myForm.input.error = {{myForm.input.error}}</tt><br/>
+ <tt>myForm.valid = {{myForm.valid}}</tt><br/>
+ <tt>myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}</tt><br/>
+ </form>
+ </doc:source>
+ <doc:scenario>
+ it('should initialize to model', function() {
+ expect(binding('names')).toEqual('["igor","misko","vojta"]');
+ expect(binding('myForm.input.valid')).toEqual('true');
+ });
+
+ it('should be invalid if empty', function() {
+ input('names').enter('');
+ expect(binding('names')).toEqual('');
+ expect(binding('myForm.input.valid')).toEqual('false');
+ });
+ </doc:scenario>
+ </doc:example>
+ */
+var ngListDirective = function() {
+ return {
+ require: 'ngModel',
+ link: function(scope, element, attr, ctrl) {
+ var parse = function(viewValue) {
+ var list = [];
+
+ if (viewValue) {
+ forEach(viewValue.split(/\s*,\s*/), function(value) {
+ if (value) list.push(value);
+ });
+ }
+
+ return list;
+ };
+
+ ctrl.parsers.push(parse);
+ ctrl.formatters.push(function(value) {
+ if (isArray(value) && !equals(parse(ctrl.viewValue), value)) {
+ return value.join(', ');
+ }
+
+ return undefined;
+ });
+ }
+ };
+};