From f54db2ccda399f2677e4ca7588018cb31545a2b4 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Thu, 8 Mar 2012 15:00:38 -0800 Subject: chore(directives,widgets): reorg the code under directive/ dir --- src/widget/input.js | 1171 --------------------------------------------------- 1 file changed, 1171 deletions(-) delete mode 100644 src/widget/input.js (limited to 'src/widget/input.js') diff --git a/src/widget/input.js b/src/widget/input.js deleted file mode 100644 index af446c6b..00000000 --- a/src/widget/input.js +++ /dev/null @@ -1,1171 +0,0 @@ -'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.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 - - - -
- 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'); - }); - -
- */ - 'text': textInputType, - - - /** - * @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', 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'); - }); - -
- */ - '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 - - - -
- 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'); - }); - -
- */ - '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, - 'button': noop, - 'submit': noop, - 'reset': noop -}; - - -function isEmpty(value) { - return isUndefined(value) || value === '' || value === null || value !== value; -} - - -function textInputType(scope, element, attr, ctrl) { - element.bind('blur', function() { - var touched = ctrl.touch(), - value = trim(element.val()); - - if (ctrl.viewValue !== value) { - scope.$apply(function() { - ctrl.read(value); - }); - } else if (touched) { - scope.$apply(); - } - }); - - ctrl.render = function() { - element.val(isEmpty(ctrl.viewValue) ? '' : ctrl.viewValue); - }; - - // pattern validator - var pattern = attr.ngPattern, - patternValidator; - - var emit = function(regexp, value) { - if (isEmpty(value) || regexp.test(value)) { - ctrl.setValidity('PATTERN', true); - return value; - } else { - ctrl.setValidity('PATTERN', false); - return undefined; - } - }; - - if (pattern) { - if (pattern.match(/^\/(.*)\/$/)) { - pattern = new RegExp(pattern.substr(1, pattern.length - 2)); - patternValidator = function(value) { - return emit(pattern, value) - }; - } else { - patternValidator = function(value) { - var patternObj = scope.$eval(pattern); - - if (!patternObj || !patternObj.test) { - throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); - } - return emit(patternObj, value); - }; - } - - ctrl.formatters.push(patternValidator); - ctrl.parsers.push(patternValidator); - } - - // min length validator - if (attr.ngMinlength) { - var minlength = parseInt(attr.ngMinlength, 10); - var minLengthValidator = function(value) { - if (!isEmpty(value) && value.length < minlength) { - ctrl.setValidity('MINLENGTH', false); - return undefined; - } else { - ctrl.setValidity('MINLENGTH', true); - return value; - } - }; - - ctrl.parsers.push(minLengthValidator); - ctrl.formatters.push(minLengthValidator); - } - - // max length validator - if (attr.ngMaxlength) { - var maxlength = parseInt(attr.ngMaxlength, 10); - var maxLengthValidator = function(value) { - if (!isEmpty(value) && value.length > maxlength) { - ctrl.setValidity('MAXLENGTH', false); - return undefined; - } else { - ctrl.setValidity('MAXLENGTH', true); - return value; - } - }; - - ctrl.parsers.push(maxLengthValidator); - ctrl.formatters.push(maxLengthValidator); - } -}; - -function numberInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - ctrl.parsers.push(function(value) { - var empty = isEmpty(value); - if (empty || NUMBER_REGEXP.test(value)) { - ctrl.setValidity('NUMBER', true); - return value === '' ? null : (empty ? value : parseFloat(value)); - } else { - ctrl.setValidity('NUMBER', false); - return undefined; - } - }); - - ctrl.formatters.push(function(value) { - return isEmpty(value) ? '' : '' + value; - }); - - if (attr.min) { - var min = parseFloat(attr.min); - var minValidator = function(value) { - if (!isEmpty(value) && value < min) { - ctrl.setValidity('MIN', false); - return undefined; - } else { - ctrl.setValidity('MIN', true); - return value; - } - }; - - ctrl.parsers.push(minValidator); - ctrl.formatters.push(minValidator); - } - - if (attr.max) { - var max = parseFloat(attr.max); - var maxValidator = function(value) { - if (!isEmpty(value) && value > max) { - ctrl.setValidity('MAX', false); - return undefined; - } else { - ctrl.setValidity('MAX', true); - return value; - } - }; - - ctrl.parsers.push(maxValidator); - ctrl.formatters.push(maxValidator); - } - - ctrl.formatters.push(function(value) { - - if (isEmpty(value) || isNumber(value)) { - ctrl.setValidity('NUMBER', true); - return value; - } else { - ctrl.setValidity('NUMBER', false); - return undefined; - } - }); -} - -function urlInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - var urlValidator = function(value) { - if (isEmpty(value) || URL_REGEXP.test(value)) { - ctrl.setValidity('URL', true); - return value; - } else { - ctrl.setValidity('URL', false); - return undefined; - } - }; - - ctrl.formatters.push(urlValidator); - ctrl.parsers.push(urlValidator); -} - -function emailInputType(scope, element, attr, ctrl) { - textInputType(scope, element, attr, ctrl); - - var emailValidator = function(value) { - if (isEmpty(value) || EMAIL_REGEXP.test(value)) { - ctrl.setValidity('EMAIL', true); - return value; - } else { - ctrl.setValidity('EMAIL', false); - return undefined; - } - }; - - ctrl.formatters.push(emailValidator); - ctrl.parsers.push(emailValidator); -} - -function radioInputType(scope, element, attr, ctrl) { - // correct the name - element.attr('name', attr.id + '@' + attr.name); - - element.bind('click', function() { - if (element[0].checked) { - scope.$apply(function() { - ctrl.touch(); - ctrl.read(attr.value); - }); - }; - }); - - ctrl.render = function() { - var value = attr.value; - element[0].checked = isDefined(value) && (value == ctrl.viewValue); - }; -} - -function checkboxInputType(scope, element, attr, ctrl) { - var trueValue = attr.ngTrueValue, - falseValue = attr.ngFalseValue; - - if (!isString(trueValue)) trueValue = true; - if (!isString(falseValue)) falseValue = false; - - element.bind('click', function() { - scope.$apply(function() { - ctrl.touch(); - ctrl.read(element[0].checked); - }); - }); - - ctrl.render = function() { - element[0].checked = ctrl.viewValue; - }; - - ctrl.formatters.push(function(value) { - return value === trueValue; - }); - - ctrl.parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @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 directive - * @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', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (ctrl) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl); - } - } - }; -}]; - - -/** - * @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; - this.modelValue = Number.NaN; - this.parsers = []; - this.formatters = []; - this.error = {}; - this.pristine = true; - this.dirty = false; - this.valid = true; - 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; - - this.dirty = true; - this.pristine = false; - try { - $scope.$emit('$viewTouch'); - } catch (e) { - $exceptionHandler(e); - } - return true; - }; - - - /** - * @ngdoc function - * @name angular.module.ng.$compileProvider.directive.ng:model.NgModelController#setValidity - * @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.setValidity = function(name, isValid) { - - if (!isValid && this.error[name]) return; - if (isValid && !this.error[name]) return; - - 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); - }; - - - /** - * @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; - - forEach(this.parsers, function(fn) { - value = fn(value); - }); - - if (isDefined(value) && this.model !== value) { - this.modelValue = value; - ngModel(value); - $scope.$emit('$viewChange', value, this); - } - }; - - // model -> value - var ctrl = this; - $scope.$watch(function() { - return ngModel(); - }, function(value) { - - // ignore change from view - if (ctrl.modelValue === value) return; - - var formatters = ctrl.formatters, - idx = formatters.length; - - ctrl.modelValue = value; - while(idx--) { - value = formatters[idx](value); - } - - if (isDefined(value) && ctrl.viewValue !== value) { - ctrl.viewValue = value; - ctrl.render(); - } - }); -}]; - - -/** - * @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` as well. - * - * `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 basic 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: { - ngModel: 'accessor' - }, - require: ['ngModel', '^?form'], - controller: NgModelController, - link: function(scope, element, attr, controllers) { - var modelController = controllers[0], - formController = controllers[1]; - - if (formController) { - formController.registerWidget(modelController, attr.name); - } - - forEach(['valid', 'invalid', 'pristine', 'dirty'], function(name) { - scope.$watch(function() { - return modelController[name]; - }, function(value) { - element[value ? 'addClass' : 'removeClass']('ng-' + name); - }); - }); - - element.bind('$destroy', function() { - scope.$emit('$destroy', modelController); - }); - } - }; -}]; - - -/** - * @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) { - scope.$on('$viewChange', function(event, value, widget) { - if (ctrl === widget) scope.$eval(attr.ngChange); - }); - } -}); - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng:model-instant - * - * @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:model-instant`. - * - * @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:model-instant`. - *
- * - * 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 ngModelInstantDirective = ['$browser', function($browser) { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var handler = function() { - var touched = ctrl.touch(), - value = trim(element.val()); - - if (ctrl.viewValue !== value) { - scope.$apply(function() { - ctrl.read(value); - }); - } else if (touched) { - scope.$apply(); - } - }; - - var timeout; - element.bind('keydown', function(event) { - var key = event.keyCode; - - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - if (!timeout) { - timeout = $browser.defer(function() { - handler(); - timeout = null; - }); - } - }); - - element.bind('change input', handler); - } - }; -}]; - - -var requiredDirective = [function() { - return { - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - - var validator = function(value) { - if (attr.required && isEmpty(value)) { - ctrl.setValidity('REQUIRED', false); - return null; - } else { - ctrl.setValidity('REQUIRED', true); - return value; - } - }; - - ctrl.formatters.push(validator); - ctrl.parsers.unshift(validator); - - attr.$observe('required', function() { - validator(ctrl.viewValue); - }); - } - }; -}]; - - -/** - * @ngdoc directive - * @name angular.module.ng.$compileProvider.directive.ng:list - * - * @description - * Text input that converts between comma-seperated string into an array of strings. - * - * @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