From ae8f562a4aa67c12ebe2d605e48fb7b69e04b291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rouven=20We=C3=9Fling?= Date: Thu, 15 Jan 2015 20:51:34 +0100 Subject: [PATCH] feat(ngModel) Allow running the formatters without a change to the modelValue Fixes #3407 --- src/ng/directive/ngModel.js | 46 +++++++++++++++++++++----------- test/ng/directive/ngModelSpec.js | 34 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index da93dfd32c6a..77a43f910553 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -799,6 +799,36 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ } }; + function formatValue(modelValue) { + var formatters = ctrl.$formatters, + idx = formatters.length; + + var viewValue = modelValue; + while (idx--) { + viewValue = formatters[idx](viewValue); + } + + return viewValue; + } + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setModelValue + * + */ + this.$setModelValue = function(modelValue) { + ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; + + var viewValue = formatValue(this.$modelValue); + + if (this.$viewValue !== viewValue) { + this.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; + this.$render(); + + ctrl.$$runValidators(undefined, modelValue, ctrl.$viewValue, noop); + } + }; + // model -> value // Note: we cannot use a normal scope.$watch as we want to detect the following: // 1. scope value is 'a' @@ -813,21 +843,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // if scope model value and ngModel value are out of sync // TODO(perf): why not move this to the action fn? if (modelValue !== ctrl.$modelValue) { - ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; - - var formatters = ctrl.$formatters, - idx = formatters.length; - - var viewValue = modelValue; - while (idx--) { - viewValue = formatters[idx](viewValue); - } - if (ctrl.$viewValue !== viewValue) { - ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; - ctrl.$render(); - - ctrl.$$runValidators(undefined, modelValue, viewValue, noop); - } + ctrl.$setModelValue(modelValue); } return modelValue; diff --git a/test/ng/directive/ngModelSpec.js b/test/ng/directive/ngModelSpec.js index d23cb0298afc..b43cb964479e 100644 --- a/test/ng/directive/ngModelSpec.js +++ b/test/ng/directive/ngModelSpec.js @@ -455,6 +455,40 @@ describe('ngModel', function() { }); + describe('$setModelValue', function() { + + it('should set the value to $modelValue', function() { + ctrl.$setModelValue(10); + expect(ctrl.$modelValue).toBe(10); + }); + + it('should update the value on the scope', inject(function($compile) { + var element = $compile('
')(scope); + + var input = element.children().eq(0); + ctrl = input.controller('ngModel'); + + scope.val = 11; + scope.$digest(); + ctrl.$setModelValue(22); + scope.$digest(); + expect(scope.val).toBe(22); + })); + + it('should $render only if value changed', function() { + spyOn(ctrl, '$render'); + + ctrl.$setModelValue(3); + expect(ctrl.$render).toHaveBeenCalledOnce(); + ctrl.$render.reset(); + + ctrl.$formatters.push(function() {return 3;}); + ctrl.$setModelValue(5); + expect(ctrl.$render).not.toHaveBeenCalled(); + }); + }); + + describe('model -> view', function() { it('should set the value to $modelValue', function() {