From e510efabd39548c7b0b1ab4991c456f20fe9c55b Mon Sep 17 00:00:00 2001 From: Bret Little Date: Mon, 19 Sep 2016 11:51:44 -0600 Subject: [PATCH] Make `$apply` calls safe --- src/mask.js | 32 +++++++++++++++++++------------- test/maskSpec.js | 6 ++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/mask.js b/src/mask.js index 1f738ab..073af83 100644 --- a/src/mask.js +++ b/src/mask.js @@ -49,7 +49,7 @@ angular.module('ui.mask', []) return tempOptions; }]; }) - .directive('uiMask', ['uiMask.Config', function(maskConfig) { + .directive('uiMask', ['uiMask.Config', '$timeout', function(maskConfig, $timeout) { function isFocused (elem) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); } @@ -444,12 +444,14 @@ angular.module('ui.mask', []) if (!isValid || value.length === 0) { valueMasked = ''; iElement.val(''); - scope.$apply(function() { - //only $setViewValue when not $pristine to avoid changing $pristine state. - if (!controller.$pristine) { - controller.$setViewValue(''); - } - }); + $timeout(function() { + scope.$apply(function() { + //only $setViewValue when not $pristine to avoid changing $pristine state. + if (!controller.$pristine) { + controller.$setViewValue(''); + } + }); + }, null, false); } } //Check for different value and trigger change. @@ -597,9 +599,11 @@ angular.module('ui.mask', []) iElement.val(maskPlaceholder); // This shouldn't be needed but for some reason after aggressive backspacing the controller $viewValue is incorrect. // This keeps the $viewValue updated and correct. - scope.$apply(function () { - controller.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. - }); + $timeout(function() { + scope.$apply(function () { + controller.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. + }); + }, null, false); setCaretPosition(this, caretPosOld); return; } @@ -640,9 +644,11 @@ angular.module('ui.mask', []) //we need this check. What could happen if you don't have it is that you'll set the model value without the user //actually doing anything. Meaning, things like pristine and touched will be set. if (valAltered) { - scope.$apply(function () { - controller.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. - }); + $timeout(function() { + scope.$apply(function () { + controller.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. + }); + }, null, false); } // Caret Repositioning diff --git a/test/maskSpec.js b/test/maskSpec.js index efa7123..9f7876a 100644 --- a/test/maskSpec.js +++ b/test/maskSpec.js @@ -233,6 +233,7 @@ describe("uiMask", function () { input.triggerHandler("input"); expect(input.data("$ngModelController").$error.required).toBe(true); input.val("(abc123_) _ _").triggerHandler("input"); + timeout.flush(); expect(scope.x).toBe("ab1"); expect(input.data("$ngModelController").$error.required).toBeUndefined(); }); @@ -373,14 +374,17 @@ describe("uiMask", function () { input.val("aa___").triggerHandler("input"); input.triggerHandler("blur"); + timeout.flush(); expect(input.val()).toBe("aa_"); input.val("99a___").triggerHandler("input"); input.triggerHandler("blur"); + timeout.flush(); expect(input.val()).toBe("99_"); input.val("992___").triggerHandler("input"); input.triggerHandler("blur"); + timeout.flush(); expect(input.val()).toBe("992"); }); @@ -642,6 +646,7 @@ describe("uiMask", function () { scope.$apply("mask = '@193'"); input.val("f123____").triggerHandler("input"); input.triggerHandler("blur"); + timeout.flush(); expect(input.val()).toBe("f123"); }); @@ -659,6 +664,7 @@ describe("uiMask", function () { scope.$apply("mask = '@193Ab'"); input.val("f123cCCc").triggerHandler("input"); input.triggerHandler("blur"); + timeout.flush(); expect(input.val()).toBe("f123Cc"); });