diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js
index 1b393676cd79..2a6adadaf6a9 100644
--- a/src/ng/directive/input.js
+++ b/src/ng/directive/input.js
@@ -1716,13 +1716,14 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
var parsedNgModel = $parse($attr.ngModel),
+ parsedNgModelContext = null,
pendingDebounce = null,
ctrl = this;
var ngModelGet = function ngModelGet() {
var modelValue = parsedNgModel($scope);
if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) {
- modelValue = modelValue();
+ modelValue = modelValue.call(parsedNgModelContext ? parsedNgModelContext($scope) : $scope);
}
return modelValue;
};
@@ -1732,7 +1733,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
if (ctrl.$options && ctrl.$options.getterSetter &&
isFunction(getterSetter = parsedNgModel($scope))) {
- getterSetter(ctrl.$modelValue);
+ getterSetter.call(parsedNgModelContext ? parsedNgModelContext($scope) : $scope, ctrl.$modelValue);
} else {
parsedNgModel.assign($scope, ctrl.$modelValue);
}
@@ -1741,6 +1742,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
this.$$setOptions = function(options) {
ctrl.$options = options;
+ if (ctrl.$options && ctrl.$options.getterSetter && ctrl.$options.getterSetterContext) {
+ // Use the provided context expression to specify the context used when invoking the
+ // getter/setter function
+ parsedNgModelContext = $parse(ctrl.$options.getterSetterContext);
+ }
if (!parsedNgModel.assign && (!options || !options.getterSetter)) {
throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
$attr.ngModel, startingTag($element));
diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js
index 8d4e47763609..565073e05379 100644
--- a/test/ng/directive/inputSpec.js
+++ b/test/ng/directive/inputSpec.js
@@ -1975,6 +1975,83 @@ describe('input', function() {
'ng-model-options="{ getterSetter: true }" />');
});
+ it('should try to invoke a model with default context if getterSetter is true and getterSetterContext is not provided', function() {
+ scope.value = 'scopeContext';
+ compileInput(
+ '');
+
+ scope.someService = {
+ value: 'b',
+ getterSetter: function(newValue) {
+ this.value = newValue || this.value;
+ return this.value;
+ }
+ };
+ spyOn(scope.someService, 'getterSetter').andCallThrough();
+ scope.$apply();
+ expect(inputElm.val()).toBe('scopeContext');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith();
+ expect(scope.someService.value).toBe('b'); // 'this' is not bound to the service w/o ngModelContext
+ expect(scope.value).toBe('scopeContext');
+
+ changeInputValueTo('a');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith('a');
+ expect(scope.someService.value).toBe('b');
+ expect(scope.value).toBe('a');
+
+ scope.someService.value = 'c';
+ scope.$apply();
+ expect(inputElm.val()).toBe('a');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith();
+ expect(scope.someService.value).toBe('c');
+ expect(scope.value).toBe('a');
+
+ scope.value = 'd';
+ scope.$apply();
+ expect(inputElm.val()).toBe('d');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith();
+ expect(scope.someService.value).toBe('c');
+ expect(scope.value).toBe('d');
+ });
+
+ it('should try to invoke a model with the provided context if getterSetter is true and getterSetterContext is an expression', function() {
+ compileInput(
+ '');
+
+ scope.someService = {
+ value: 'b',
+ getterSetter: function(newValue) {
+ this.value = newValue || this.value;
+ return this.value;
+ }
+ };
+ spyOn(scope.someService, 'getterSetter').andCallThrough();
+ scope.$apply();
+ expect(inputElm.val()).toBe('b');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith();
+ expect(scope.someService.value).toBe('b');
+
+ changeInputValueTo('a');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith('a');
+ expect(scope.someService.value).toBe('a');
+
+ scope.someService.value = 'c';
+ scope.$apply();
+ expect(inputElm.val()).toBe('c');
+ expect(scope.someService.getterSetter).toHaveBeenCalledWith();
+ expect(scope.someService.value).toBe('c');
+ });
+
+ it('should fail to parse if getterSetterContext is an invalid expression', function() {
+ expect(function() {
+ compileInput(
+ '');
+ }).toThrowMinErr("$parse", "syntax", "Syntax Error: Token 'error' is an unexpected token at column 7 of the expression [throw error] starting at [error].");
+ });
+
it('should assign invalid values to the scope if allowInvalid is true', function() {
compileInput('');