@@ -1651,15 +1651,33 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
16511651 this . $name = $attr . name ;
16521652
16531653
1654- var ngModelGet = $parse ( $attr . ngModel ) ,
1655- ngModelSet = ngModelGet . assign ,
1654+ var parsedNgModel = $parse ( $attr . ngModel ) ,
16561655 pendingDebounce = null ,
16571656 ctrl = this ;
16581657
1658+ var ngModelGet = function ngModelGet ( ) {
1659+ var modelValue = parsedNgModel ( $scope ) ;
1660+ if ( ctrl . $options && ctrl . $options . getterSetter && isFunction ( modelValue ) ) {
1661+ modelValue = modelValue ( ) ;
1662+ }
1663+ return modelValue ;
1664+ } ;
1665+
1666+ var ngModelSet = function ngModelSet ( newValue ) {
1667+ var getterSetter ;
1668+ if ( ctrl . $options && ctrl . $options . getterSetter &&
1669+ isFunction ( getterSetter = parsedNgModel ( $scope ) ) ) {
1670+
1671+ getterSetter ( ctrl . $modelValue ) ;
1672+ } else {
1673+ parsedNgModel . assign ( $scope , ctrl . $modelValue ) ;
1674+ }
1675+ } ;
1676+
16591677 this . $$setOptions = function ( options ) {
16601678 ctrl . $options = options ;
16611679
1662- if ( ! ngModelSet && ( ! options || ! options . getterSetter ) ) {
1680+ if ( ! parsedNgModel . assign && ( ! options || ! options . getterSetter ) ) {
16631681 throw $ngModelMinErr ( 'nonassign' , "Expression '{0}' is non-assignable. Element: {1}" ,
16641682 $attr . ngModel , startingTag ( $element ) ) ;
16651683 }
@@ -1875,26 +1893,17 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
18751893 * Runs each of the registered validators (first synchronous validators and then asynchronous validators).
18761894 */
18771895 this . $validate = function ( ) {
1878- // ignore $validate before model initialized
1879- if ( ctrl . $modelValue !== ctrl . $modelValue ) {
1896+ // ignore $validate before model is initialized
1897+ if ( isNumber ( ctrl . $modelValue ) && isNaN ( ctrl . $modelValue ) ) {
18801898 return ;
18811899 }
1882-
1883- var prev = ctrl . $modelValue ;
1884- ctrl . $$runValidators ( undefined , ctrl . $$invalidModelValue || ctrl . $modelValue , ctrl . $viewValue , function ( ) {
1885- if ( prev !== ctrl . $modelValue ) {
1886- ctrl . $$writeModelToScope ( ) ;
1887- }
1888- } ) ;
1900+ this . $$parseAndValidate ( ) ;
18891901 } ;
18901902
18911903 this . $$runValidators = function ( parseValid , modelValue , viewValue , doneCallback ) {
18921904 currentValidationRunId ++ ;
18931905 var localValidationRunId = currentValidationRunId ;
18941906
1895- // We can update the $$invalidModelValue immediately as we don't have to wait for validators!
1896- ctrl . $$invalidModelValue = modelValue ;
1897-
18981907 // check parser error
18991908 if ( ! processParseErrors ( parseValid ) ) {
19001909 return ;
@@ -1971,8 +1980,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
19711980
19721981 function validationDone ( ) {
19731982 if ( localValidationRunId === currentValidationRunId ) {
1974- // set the validated model value
1975- ctrl . $modelValue = ctrl . $valid ? modelValue : undefined ;
19761983
19771984 doneCallback ( ) ;
19781985 }
@@ -2011,31 +2018,31 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
20112018 $animate . addClass ( $element , DIRTY_CLASS ) ;
20122019 parentForm . $setDirty ( ) ;
20132020 }
2021+ this . $$parseAndValidate ( ) ;
2022+ } ;
20142023
2015- var parserValid = true , modelValue = viewValue ;
2024+ this . $$parseAndValidate = function ( ) {
2025+ var parserValid = true ,
2026+ viewValue = ctrl . $$lastCommittedViewValue ,
2027+ modelValue = viewValue ;
20162028 for ( var i = 0 ; i < ctrl . $parsers . length ; i ++ ) {
20172029 modelValue = ctrl . $parsers [ i ] ( modelValue ) ;
20182030 if ( isUndefined ( modelValue ) ) {
20192031 parserValid = false ;
20202032 break ;
20212033 }
20222034 }
2023-
2035+ var prevModelValue = ctrl . $modelValue ;
20242036 ctrl . $$runValidators ( parserValid , modelValue , viewValue , function ( ) {
2025- ctrl . $$writeModelToScope ( ) ;
2037+ ctrl . $modelValue = ctrl . $valid ? modelValue : undefined ;
2038+ if ( ctrl . $modelValue !== prevModelValue ) {
2039+ ctrl . $$writeModelToScope ( ) ;
2040+ }
20262041 } ) ;
20272042 } ;
20282043
20292044 this . $$writeModelToScope = function ( ) {
2030- var getterSetter ;
2031-
2032- if ( ctrl . $options && ctrl . $options . getterSetter &&
2033- isFunction ( getterSetter = ngModelGet ( $scope ) ) ) {
2034-
2035- getterSetter ( ctrl . $modelValue ) ;
2036- } else {
2037- ngModelSet ( $scope , ctrl . $modelValue ) ;
2038- }
2045+ ngModelSet ( ctrl . $modelValue ) ;
20392046 forEach ( ctrl . $viewChangeListeners , function ( listener ) {
20402047 try {
20412048 listener ( ) ;
@@ -2123,17 +2130,20 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
21232130 } ;
21242131
21252132 // model -> value
2133+ // Note: we cannot use a normal scope.$watch as we want to detect the following:
2134+ // 1. scope value is 'a'
2135+ // 2. user enters 'b'
2136+ // 3. ng-change kicks in and reverts scope value to 'a'
2137+ // -> scope value did not change since the last digest as
2138+ // ng-change executes in apply phase
2139+ // 4. view should be changed back to 'a'
21262140 $scope . $watch ( function ngModelWatch ( ) {
2127- var modelValue = ngModelGet ( $scope ) ;
2128-
2129- if ( ctrl . $options && ctrl . $options . getterSetter && isFunction ( modelValue ) ) {
2130- modelValue = modelValue ( ) ;
2131- }
2141+ var modelValue = ngModelGet ( ) ;
21322142
21332143 // if scope model value and ngModel value are out of sync
21342144 // TODO(perf): why not move this to the action fn?
2135- if ( ctrl . $ modelValue !== modelValue &&
2136- ( isUndefined ( ctrl . $$invalidModelValue ) || ctrl . $$invalidModelValue ! = modelValue ) ) {
2145+ if ( modelValue !== ctrl . $ modelValue) {
2146+ ctrl . $modelValue = modelValue ;
21372147
21382148 var formatters = ctrl . $formatters ,
21392149 idx = formatters . length ;
0 commit comments