diff options
| author | Vojta Jina | 2012-02-23 19:47:58 -0800 | 
|---|---|---|
| committer | Vojta Jina | 2012-02-28 18:22:35 -0800 | 
| commit | 139e1b09a94f60b351cc961d8bfd882c0b8ba594 (patch) | |
| tree | e4403c52a9be8db33a991420056c865855bdd6f7 /src/widget/input.js | |
| parent | 60743fc52aea9eabee58258a31f4ba465013cb4e (diff) | |
| download | angular.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
Diffstat (limited to 'src/widget/input.js')
| -rw-r--r-- | src/widget/input.js | 1242 | 
1 files changed, 702 insertions, 540 deletions
| 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; +      }); +    } +  }; +}; | 
