Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 47382a4

Browse files
committed
fix(ngModelOptions): preserve context of getter/setters
Closes #9394 BREAKING CHANGE: previously, ngModel invoked getter/setters in the global context. For example: ```js <input ng-model="model.value" ng-model-options="{ getterSetter: true }"> ``` would previously invoke `model.value()` in the global context. Now, ngModel invokes `value` with `model` as the context. It's unlikely that real apps relied on this behavior. If they did they can use `.bind` to explicilty bind a getter/getter to the global context, or just reference globals normally without `this`.
1 parent e3764e3 commit 47382a4

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

src/ng/directive/input.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17461746
var ngModelGet = function ngModelGet() {
17471747
var modelValue = parsedNgModel($scope);
17481748
if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) {
1749-
modelValue = modelValue();
1749+
modelValue = $scope.$eval($attr.ngModel + '()');
17501750
}
17511751
return modelValue;
17521752
};
@@ -1755,16 +1755,14 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
17551755
var getterSetter;
17561756
if (ctrl.$options && ctrl.$options.getterSetter &&
17571757
isFunction(getterSetter = parsedNgModel($scope))) {
1758-
1759-
getterSetter(ctrl.$modelValue);
1758+
$scope.$eval($attr.ngModel + '($$$p)', {$$$p: ctrl.$modelValue});
17601759
} else {
17611760
parsedNgModel.assign($scope, ctrl.$modelValue);
17621761
}
17631762
};
17641763

17651764
this.$$setOptions = function(options) {
17661765
ctrl.$options = options;
1767-
17681766
if (!parsedNgModel.assign && (!options || !options.getterSetter)) {
17691767
throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
17701768
$attr.ngModel, startingTag($element));

test/ng/directive/inputSpec.js

+29
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,35 @@ describe('input', function() {
20932093
'ng-model-options="{ getterSetter: true }" />');
20942094
});
20952095

2096+
it('should invoke a model in the correct context if getterSetter is true', function() {
2097+
compileInput(
2098+
'<input type="text" ng-model="someService.getterSetter" '+
2099+
'ng-model-options="{ getterSetter: true }" />');
2100+
2101+
scope.someService = {
2102+
value: 'a',
2103+
getterSetter: function(newValue) {
2104+
this.value = newValue || this.value;
2105+
return this.value;
2106+
}
2107+
};
2108+
spyOn(scope.someService, 'getterSetter').andCallThrough();
2109+
scope.$apply();
2110+
2111+
expect(inputElm.val()).toBe('a');
2112+
expect(scope.someService.getterSetter).toHaveBeenCalledWith();
2113+
expect(scope.someService.value).toBe('a');
2114+
2115+
changeInputValueTo('b');
2116+
expect(scope.someService.getterSetter).toHaveBeenCalledWith('b');
2117+
expect(scope.someService.value).toBe('b');
2118+
2119+
scope.someService.value = 'c';
2120+
scope.$apply();
2121+
expect(inputElm.val()).toBe('c');
2122+
expect(scope.someService.getterSetter).toHaveBeenCalledWith();
2123+
});
2124+
20962125
it('should assign invalid values to the scope if allowInvalid is true', function() {
20972126
compileInput('<input type="text" name="input" ng-model="value" maxlength="1" ' +
20982127
'ng-model-options="{allowInvalid: true}" />');

0 commit comments

Comments
 (0)