From 2fa87c1fc07ec62f89ba64869eadca676607cb8b 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 | 38 ++++++++++++++++++++++---------- test/ng/directive/ngModelSpec.js | 26 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index de1a5c491efa..7e4a9d30c2ba 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -799,6 +799,28 @@ 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; + } + + this.$runFormatters = function() { + var modelValue = this.$modelValue, + viewValue = formatValue(this.$modelValue); + + if (this.$viewValue !== viewValue) { + this.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; + this.$render(); + } + }; + // model -> value // Note: we cannot use a normal scope.$watch as we want to detect the following: // 1. scope value is 'a' @@ -814,19 +836,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // TODO(perf): why not move this to the action fn? if (modelValue !== ctrl.$modelValue) { ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; + var oldViewValue = ctrl.$viewValue; - 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.$runFormatters(); + if (ctrl.$viewValue !== oldViewValue) { + ctrl.$$runValidators(undefined, modelValue, ctrl.$viewValue, noop); } } diff --git a/test/ng/directive/ngModelSpec.js b/test/ng/directive/ngModelSpec.js index 583414deaa9c..7fd6b4018a4a 100644 --- a/test/ng/directive/ngModelSpec.js +++ b/test/ng/directive/ngModelSpec.js @@ -578,6 +578,32 @@ describe('ngModel', function() { dealoc(form); })); + + describe('$runFormatters', function() { + it('should reformat the value', function() { + spyOn(ctrl, '$render'); + ctrl.$validators.spyValidator = jasmine.createSpy('spyValidator'); + scope.$apply('value = "first"'); + ctrl.$formatters.push(function(value) { + return 'change'; + }); + ctrl.$runFormatters(); + expect(ctrl.$viewValue).toBe('change'); + }); + + it('should not rerender nor validate in case view value is not changed', function() { + ctrl.$formatters.push(function(value) { + return 'nochange'; + }); + + spyOn(ctrl, '$render'); + ctrl.$validators.spyValidator = jasmine.createSpy('spyValidator'); + scope.$apply('value = "first"'); + ctrl.$runFormatters(); + expect(ctrl.$validators.spyValidator).toHaveBeenCalledOnce(); + expect(ctrl.$render).toHaveBeenCalledOnce(); + }); + }); });