From 139e1b09a94f60b351cc961d8bfd882c0b8ba594 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Thu, 23 Feb 2012 19:47:58 -0800 Subject: docs(forms): Update API docs for forms - API forms (ng:model + controller, form + controller) - fix some broken links - ng:change, ng:model-instant --- src/widget/input.js | 1250 +++++++++++++++++++++++++++++---------------------- 1 file changed, 706 insertions(+), 544 deletions(-) (limited to 'src/widget/input.js') 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 - - - -
- Single word: - - Required! - - Single word only! - - text = {{text}}
- myForm.input.valid = {{myForm.input.valid}}
- myForm.input.error = {{myForm.input.error}}
- myForm.valid = {{myForm.valid}}
- myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
-
-
- - 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'); - }); - -
- */ - - -/** - * @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 - - - + /** + * @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 + + +
- Email: + Single word: Required! - - Not valid email! + + Single word only! + text = {{text}}
myForm.input.valid = {{myForm.input.valid}}
myForm.input.error = {{myForm.input.error}}
myForm.valid = {{myForm.valid}}
myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
- myForm.error.EMAIL = {{!!myForm.error.EMAIL}}
-
-
- - 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'); - }); - -
- */ - - -/** - * @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 - - - -
- URL: - - Required! - - Not valid url! - text = {{text}}
- myForm.input.valid = {{myForm.input.valid}}
- myForm.input.error = {{myForm.input.error}}
- myForm.valid = {{myForm.valid}}
- myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
- myForm.error.url = {{!!myForm.error.url}}
-
-
- - 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'); - }); - -
- */ - - -/** - * @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 - - - -
- List: - - Required! - names = {{names}}
- myForm.input.valid = {{myForm.input.valid}}
- myForm.input.error = {{myForm.input.error}}
- myForm.valid = {{myForm.valid}}
- myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
-
-
- - 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'); - }); - -
- */ -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); + +
+ + 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; - }); - } - }; -}; - -/** - * @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 - - - -
- Number: - - Required! - - Not valid number! - value = {{value}}
- myForm.input.valid = {{myForm.input.valid}}
- myForm.input.error = {{myForm.input.error}}
- myForm.valid = {{myForm.valid}}
- myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
-
-
- - 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'); - }); - - it('should be invalid if over max', function() { - input('value').enter('123'); - expect(binding('value')).toEqual('12'); - 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 - - - -
- Value1:
- Value2:
- value1 = {{value1}}
- value2 = {{value2}}
-
-
- - 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'); - }); - -
- */ - - - -/** - * @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 - - - -
- Red
- Green
- Blue
- color = {{color}}
-
-
- - 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'); - }); - -
- */ + it('should be invalid if multi word', function() { + input('text').enter('hello world'); + expect(binding('myForm.input.valid')).toEqual('false'); + }); +
+
+ */ + 'text': textInputType, -/** - * @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 - - - -
-
- User name: - - Required!
- Last name: - - Too short! - - Too long!
-
-
- user = {{user}}
- myForm.userName.valid = {{myForm.userName.valid}}
- myForm.userName.error = {{myForm.userName.error}}
- myForm.lastName.valid = {{myForm.lastName.valid}}
- myForm.userName.error = {{myForm.lastName.error}}
- myForm.valid = {{myForm.valid}}
- myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
- myForm.error.MINLENGTH = {{!!myForm.error.MINLENGTH}}
- myForm.error.MAXLENGTH = {{!!myForm.error.MAXLENGTH}}
-
-
- - 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.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 + + + +
+ Number: + + Required! + + Not valid number! + value = {{value}}
+ myForm.input.valid = {{myForm.input.valid}}
+ myForm.input.error = {{myForm.input.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+
+
+ + it('should initialize to model', function() { + expect(binding('value')).toEqual('12'); + 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('value').enter(''); + expect(binding('value')).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 over max', function() { + input('value').enter('123'); + expect(binding('value')).toEqual('12'); + expect(binding('myForm.input.valid')).toEqual('false'); + }); + +
+ */ + 'number': numberInputType, - 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'); - }); -
-
- */ + /** + * @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 + + + +
+ URL: + + Required! + + Not valid url! + text = {{text}}
+ myForm.input.valid = {{myForm.input.valid}}
+ myForm.input.error = {{myForm.input.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+ myForm.error.url = {{!!myForm.error.url}}
+
+
+ + 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'); + }); -/** - * @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, + it('should be invalid if not url', function() { + input('text').enter('xxx'); + expect(binding('myForm.input.valid')).toEqual('false'); + }); + +
+ */ 'url': urlInputType, + + + /** + * @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 + + + +
+ Email: + + Required! + + Not valid email! + text = {{text}}
+ myForm.input.valid = {{myForm.input.valid}}
+ myForm.input.error = {{myForm.input.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+ myForm.error.EMAIL = {{!!myForm.error.EMAIL}}
+
+
+ + 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'); + }); + +
+ */ 'email': emailInputType, + + /** + * @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 + + + +
+ Red
+ Green
+ Blue
+ color = {{color}}
+
+
+ + it('should change state', function() { + expect(binding('color')).toEqual('blue'); + + input('color').select('red'); + expect(binding('color')).toEqual('red'); + }); + +
+ */ '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 + + + +
+ Value1:
+ Value2:
+ value1 = {{value1}}
+ value2 = {{value2}}
+
+
+ + 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'); + }); + +
+ */ '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 + + + +
+
+ User name: + + Required!
+ Last name: + + Too short! + + Too long!
+
+
+ user = {{user}}
+ myForm.userName.valid = {{myForm.userName.valid}}
+ myForm.userName.error = {{myForm.userName.error}}
+ myForm.lastName.valid = {{myForm.lastName.valid}}
+ myForm.userName.error = {{myForm.lastName.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+ myForm.error.MINLENGTH = {{!!myForm.error.MINLENGTH}}
+ myForm.error.MAXLENGTH = {{!!myForm.error.MAXLENGTH}}
+
+
+ + 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'); + }); + +
+ */ 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.} 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.} 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 + * + * + * + *
+ * + * + *
+ * debug = {{confirmed}}
+ * counter = {{counter}} + *
+ *
+ * + * 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'); + * }); + * + *
+ */ 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 + * + * + * First name:
+ * Last name:
+ * + * First name ({{firstName}}) is only updated on `blur` event, but the last name ({{lastName}}) + * is updated immediately, because of using `ng:bind-immediate`. + *
+ * + * 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'); + * }); + * + *
+ */ 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 + + + +
+ List: + + Required! + names = {{names}}
+ myForm.input.valid = {{myForm.input.valid}}
+ myForm.input.error = {{myForm.input.error}}
+ myForm.valid = {{myForm.valid}}
+ myForm.error.REQUIRED = {{!!myForm.error.REQUIRED}}
+
+
+ + 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'); + }); + +
+ */ +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; + }); + } + }; +}; -- cgit v1.2.3