From 1147f21999edf9a434cd8d24865a6455e744d858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82e=CC=A8biowski?= Date: Thu, 5 Dec 2013 14:59:13 +0100 Subject: [PATCH] fix(input): prevent double $digest when using jQuery trigger. If an event was performed natively, jQuery sets the isTrigger property. When triggering event manually, the field is not present. Manually triggered events are performed synchronously which causes the "$digest already in progress" error. Closes #5293 --- src/ng/directive/input.js | 14 +++++++++++--- src/ngScenario/Scenario.js | 3 ++- test/ng/directive/inputSpec.js | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 6a974d9dae02..c280b7c25f47 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -407,7 +407,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { }); } - var listener = function() { + var listener = function(ev) { if (composing) return; var value = element.val(); @@ -419,9 +419,17 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { } if (ctrl.$viewValue !== value) { - scope.$apply(function() { + // If an event was performed natively, jQuery sets the isTrigger property. + // When triggering event manually, the field is not present. Manually + // triggered events are performed synchronously which causes the "$digest + // already in progress" error. + if (ev && ev.isTrigger) { ctrl.$setViewValue(value); - }); + } else { + scope.$apply(function() { + ctrl.$setViewValue(value); + }); + } } }; diff --git a/src/ngScenario/Scenario.js b/src/ngScenario/Scenario.js index 9a30d8a9ace9..f323e01d0f5a 100644 --- a/src/ngScenario/Scenario.js +++ b/src/ngScenario/Scenario.js @@ -239,7 +239,8 @@ function callerFile(offset) { * To work around this we instead use our own handler that fires a real event. */ (function(fn){ - var parentTrigger = fn.trigger; + // We need a handle to the original trigger function for input tests. + var parentTrigger = fn._originalTrigger = fn.trigger; fn.trigger = function(type) { if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) { var processDefaults = []; diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 48319cfaf2ff..c9a345cdd38f 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -533,6 +533,23 @@ describe('input', function() { 'event so that form auto complete works',function() { assertBrowserSupportsChangeEvent(true); }); + + if (!_jqLiteMode) { + it('should not cause the double $digest when triggering an event using jQuery', function() { + $sniffer.hasEvent = function(eventName) { + return eventName !== 'input'; + }; + + compileInput(''); + + scope.field = 'fake field'; + scope.$watch('field', function() { + // We need to use _originalTrigger since trigger is modified by Angular Scenario. + inputElm._originalTrigger('change'); + }); + scope.$apply(); + }); + } }); describe('"paste" and "cut" events', function() {