@@ -1440,6 +1440,12 @@ var VALID_CLASS = 'ng-valid',
14401440 * ngModel.$formatters.push(formatter);
14411441 * ```
14421442 *
1443+ * @property {Object.<string, function> } $validators A collection of validators that are applied
1444+ * whenever the model value changes. The key value within the object refers to the name of the
1445+ * validator while the function refers to the validation operation. The validation operation is
1446+ * provided with the model value as an argument and must return a true or false value depending
1447+ * on the response of that validation.
1448+ *
14431449 * @property {Array.<Function> } $viewChangeListeners Array of functions to execute whenever the
14441450 * view value has changed. It is called with no arguments, and its return value is ignored.
14451451 * This can be used in place of additional $watches against the model value.
@@ -1558,6 +1564,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
15581564 function ( $scope , $exceptionHandler , $attr , $element , $parse , $animate , $timeout ) {
15591565 this . $viewValue = Number . NaN ;
15601566 this . $modelValue = Number . NaN ;
1567+ this . $validators = { } ;
15611568 this . $parsers = [ ] ;
15621569 this . $formatters = [ ] ;
15631570 this . $viewChangeListeners = [ ] ;
@@ -1637,7 +1644,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
16371644 * Change the validity state, and notifies the form when the control changes validity. (i.e. it
16381645 * does not notify form if given validator is already marked as invalid).
16391646 *
1640- * This method should be called by validators - i.e. the parser or formatter functions.
1647+ * This method can be called within $parsers/$formatters. However, if possible, please use the
1648+ * `ngModel.$validators` pipeline which is designed to handle validations with true/false values.
16411649 *
16421650 * @param {string } validationErrorKey Name of the validator. the `validationErrorKey` will assign
16431651 * to `$error[validationErrorKey]=isValid` so that it is available for data-binding.
@@ -1786,6 +1794,23 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17861794 ctrl . $render ( ) ;
17871795 } ;
17881796
1797+ /**
1798+ * @ngdoc method
1799+ * @name ngModel.NgModelController#$validate
1800+ *
1801+ * @description
1802+ * Runs each of the registered validations set on the $validators object.
1803+ */
1804+ this . $validate = function ( ) {
1805+ this . $$runValidators ( ctrl . $modelValue , ctrl . $viewValue ) ;
1806+ } ;
1807+
1808+ this . $$runValidators = function ( modelValue , viewValue ) {
1809+ forEach ( ctrl . $validators , function ( fn , name ) {
1810+ ctrl . $setValidity ( name , fn ( modelValue , viewValue ) ) ;
1811+ } ) ;
1812+ } ;
1813+
17891814 /**
17901815 * @ngdoc method
17911816 * @name ngModel.NgModelController#$commitViewValue
@@ -1798,12 +1823,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17981823 * usually handles calling this in response to input events.
17991824 */
18001825 this . $commitViewValue = function ( ) {
1801- var value = ctrl . $viewValue ;
1826+ var viewValue = ctrl . $viewValue ;
18021827 $timeout . cancel ( pendingDebounce ) ;
1803- if ( ctrl . $$lastCommittedViewValue === value ) {
1828+ if ( ctrl . $$lastCommittedViewValue === viewValue ) {
18041829 return ;
18051830 }
1806- ctrl . $$lastCommittedViewValue = value ;
1831+ ctrl . $$lastCommittedViewValue = viewValue ;
18071832
18081833 // change to dirty
18091834 if ( ctrl . $pristine ) {
@@ -1814,13 +1839,19 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18141839 parentForm . $setDirty ( ) ;
18151840 }
18161841
1842+ var modelValue = viewValue ;
18171843 forEach ( ctrl . $parsers , function ( fn ) {
1818- value = fn ( value ) ;
1844+ modelValue = fn ( modelValue ) ;
18191845 } ) ;
18201846
1821- if ( ctrl . $modelValue !== value ) {
1822- ctrl . $modelValue = value ;
1823- ngModelSet ( $scope , value ) ;
1847+ if ( ctrl . $modelValue !== modelValue &&
1848+ ( isUndefined ( ctrl . $$invalidModelValue ) || ctrl . $$invalidModelValue != modelValue ) ) {
1849+
1850+ ctrl . $$runValidators ( modelValue , viewValue ) ;
1851+ ctrl . $modelValue = ctrl . $valid ? modelValue : undefined ;
1852+ ctrl . $$invalidModelValue = ctrl . $valid ? undefined : modelValue ;
1853+
1854+ ngModelSet ( $scope , ctrl . $modelValue ) ;
18241855 forEach ( ctrl . $viewChangeListeners , function ( listener ) {
18251856 try {
18261857 listener ( ) ;
@@ -1894,26 +1925,31 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18941925
18951926 // model -> value
18961927 $scope . $watch ( function ngModelWatch ( ) {
1897- var value = ngModelGet ( $scope ) ;
1928+ var modelValue = ngModelGet ( $scope ) ;
18981929
18991930 // if scope model value and ngModel value are out of sync
1900- if ( ctrl . $modelValue !== value ) {
1931+ if ( ctrl . $modelValue !== modelValue &&
1932+ ( isUndefined ( ctrl . $$invalidModelValue ) || ctrl . $$invalidModelValue != modelValue ) ) {
19011933
19021934 var formatters = ctrl . $formatters ,
19031935 idx = formatters . length ;
19041936
1905- ctrl . $modelValue = value ;
1937+ var viewValue = modelValue ;
19061938 while ( idx -- ) {
1907- value = formatters [ idx ] ( value ) ;
1939+ viewValue = formatters [ idx ] ( viewValue ) ;
19081940 }
19091941
1910- if ( ctrl . $viewValue !== value ) {
1911- ctrl . $viewValue = ctrl . $$lastCommittedViewValue = value ;
1942+ ctrl . $$runValidators ( modelValue , viewValue ) ;
1943+ ctrl . $modelValue = ctrl . $valid ? modelValue : undefined ;
1944+ ctrl . $$invalidModelValue = ctrl . $valid ? undefined : modelValue ;
1945+
1946+ if ( ctrl . $viewValue !== viewValue ) {
1947+ ctrl . $viewValue = ctrl . $$lastCommittedViewValue = viewValue ;
19121948 ctrl . $render ( ) ;
19131949 }
19141950 }
19151951
1916- return value ;
1952+ return modelValue ;
19171953 } ) ;
19181954} ] ;
19191955
0 commit comments