From 36b7fbe929c4f5e207e321263212e069888d55f3 Mon Sep 17 00:00:00 2001 From: danielcrisp Date: Thu, 17 Sep 2015 19:08:33 +0100 Subject: [PATCH 1/3] Use safe method for triggering scope digest --- src/unsavedChanges.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/unsavedChanges.js b/src/unsavedChanges.js index f5d47c0..a124357 100644 --- a/src/unsavedChanges.js +++ b/src/unsavedChanges.js @@ -237,8 +237,8 @@ angular.module('unsavedChanges', ['resettable']) } ]) -.directive('unsavedWarningForm', ['unsavedWarningSharedService', '$rootScope', - function(unsavedWarningSharedService, $rootScope) { +.directive('unsavedWarningForm', ['unsavedWarningSharedService', '$rootScope', '$timeout', + function(unsavedWarningSharedService, $rootScope, $timeout) { return { scope: {}, require: '^form', @@ -278,7 +278,9 @@ angular.module('unsavedChanges', ['resettable']) // trigger resettables within this form or element var resettables = angular.element(formElement[0].querySelector('[resettable]')); if(resettables.length) { - scope.$apply(resettables.triggerHandler('resetResettables')); + $timeout(function () { + resettables.triggerHandler('resetResettables'); + }); } // sets for back to valid and pristine states From a287d8b491098b70a41205f6235c07a451e6d002 Mon Sep 17 00:00:00 2001 From: Daniel Crisp Date: Thu, 17 Sep 2015 19:35:51 +0100 Subject: [PATCH 2/3] Added safer methods than --- src/unsavedChanges.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/unsavedChanges.js b/src/unsavedChanges.js index a124357..f2b1262 100644 --- a/src/unsavedChanges.js +++ b/src/unsavedChanges.js @@ -203,13 +203,16 @@ angular.module('unsavedChanges', ['resettable']) // @todo this could be written a lot cleaner! if (!allFormsClean()) { unsavedWarningsConfig.log("a form is dirty"); - if (!confirm(unsavedWarningsConfig.navigateMessage)) { - unsavedWarningsConfig.log("user wants to cancel leaving"); - event.preventDefault(); // user clicks cancel, wants to stay on page - } else { - unsavedWarningsConfig.log("user doesn't care about loosing stuff"); - $rootScope.$broadcast('resetResettables'); - } + // allow any existing scope digest to complete + setTimeout(function () { + if (!confirm(unsavedWarningsConfig.navigateMessage)) { + unsavedWarningsConfig.log("user wants to cancel leaving"); + event.preventDefault(); // user clicks cancel, wants to stay on page + } else { + unsavedWarningsConfig.log("user doesn't care about loosing stuff"); + $rootScope.$broadcast('resetResettables'); + } + }); } else { unsavedWarningsConfig.log("all forms are clean"); } @@ -278,6 +281,7 @@ angular.module('unsavedChanges', ['resettable']) // trigger resettables within this form or element var resettables = angular.element(formElement[0].querySelector('[resettable]')); if(resettables.length) { + // use safer method than $apply $timeout(function () { resettables.triggerHandler('resetResettables'); }); From 6438113a3d36e14b971228fce7ee9bce2a51e766 Mon Sep 17 00:00:00 2001 From: Daniel Crisp Date: Thu, 17 Sep 2015 19:36:32 +0100 Subject: [PATCH 3/3] Build --- dist/unsavedChanges.js | 26 ++++++++++++++++---------- dist/unsavedChanges.min.js | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/dist/unsavedChanges.js b/dist/unsavedChanges.js index f5d47c0..f2b1262 100644 --- a/dist/unsavedChanges.js +++ b/dist/unsavedChanges.js @@ -203,13 +203,16 @@ angular.module('unsavedChanges', ['resettable']) // @todo this could be written a lot cleaner! if (!allFormsClean()) { unsavedWarningsConfig.log("a form is dirty"); - if (!confirm(unsavedWarningsConfig.navigateMessage)) { - unsavedWarningsConfig.log("user wants to cancel leaving"); - event.preventDefault(); // user clicks cancel, wants to stay on page - } else { - unsavedWarningsConfig.log("user doesn't care about loosing stuff"); - $rootScope.$broadcast('resetResettables'); - } + // allow any existing scope digest to complete + setTimeout(function () { + if (!confirm(unsavedWarningsConfig.navigateMessage)) { + unsavedWarningsConfig.log("user wants to cancel leaving"); + event.preventDefault(); // user clicks cancel, wants to stay on page + } else { + unsavedWarningsConfig.log("user doesn't care about loosing stuff"); + $rootScope.$broadcast('resetResettables'); + } + }); } else { unsavedWarningsConfig.log("all forms are clean"); } @@ -237,8 +240,8 @@ angular.module('unsavedChanges', ['resettable']) } ]) -.directive('unsavedWarningForm', ['unsavedWarningSharedService', '$rootScope', - function(unsavedWarningSharedService, $rootScope) { +.directive('unsavedWarningForm', ['unsavedWarningSharedService', '$rootScope', '$timeout', + function(unsavedWarningSharedService, $rootScope, $timeout) { return { scope: {}, require: '^form', @@ -278,7 +281,10 @@ angular.module('unsavedChanges', ['resettable']) // trigger resettables within this form or element var resettables = angular.element(formElement[0].querySelector('[resettable]')); if(resettables.length) { - scope.$apply(resettables.triggerHandler('resetResettables')); + // use safer method than $apply + $timeout(function () { + resettables.triggerHandler('resetResettables'); + }); } // sets for back to valid and pristine states diff --git a/dist/unsavedChanges.min.js b/dist/unsavedChanges.min.js index ef6a715..d2d1053 100644 --- a/dist/unsavedChanges.min.js +++ b/dist/unsavedChanges.min.js @@ -1 +1 @@ -"use strict";angular.module("unsavedChanges",["resettable"]).provider("unsavedWarningsConfig",function(){var f=this;var e=false;var b=true;var d=["$locationChangeStart","$stateChangeStart"];var c="You will lose unsaved changes if you leave this page";var a="You will lose unsaved changes if you reload this page";Object.defineProperty(f,"navigateMessage",{get:function(){return c},set:function(g){c=g}});Object.defineProperty(f,"reloadMessage",{get:function(){return a},set:function(g){a=g}});Object.defineProperty(f,"useTranslateService",{get:function(){return b},set:function(g){b=!!(g)}});Object.defineProperty(f,"routeEvent",{get:function(){return d},set:function(g){if(typeof g==="string"){g=[g]}d=g}});Object.defineProperty(f,"logEnabled",{get:function(){return e},set:function(g){e=!!(g)}});this.$get=["$injector",function(h){function i(j){if(h.has("$translate")&&b){return h.get("$translate").instant(j)}else{return false}}var g={log:function(){if(console.log&&e&&arguments.length){var j=[].slice.call(arguments);if(typeof console.log==="object"){log.apply.call(console.log,console,j)}else{console.log.apply(console,j)}}}};Object.defineProperty(g,"useTranslateService",{get:function(){return b}});Object.defineProperty(g,"reloadMessage",{get:function(){return i(a)||a}});Object.defineProperty(g,"navigateMessage",{get:function(){return i(c)||c}});Object.defineProperty(g,"routeEvent",{get:function(){return d}});Object.defineProperty(g,"logEnabled",{get:function(){return e}});return g}]}).service("unsavedWarningSharedService",["$rootScope","unsavedWarningsConfig","$injector","$window",function(j,c,k,d){var i=this;var a=[];var g=true;var e=[];this.allForms=function(){return a};function h(){g=true;angular.forEach(a,function(m,l){c.log("Form : "+m.$name+" dirty : "+m.$dirty);if(m.$dirty){g=false}});return g}this.init=function(l){if(a.length===0){f()}c.log("Registering form",l);a.push(l)};this.removeForm=function(m){var l=a.indexOf(m);if(l===-1){return}a.splice(l,1);c.log("Removing form from watch list",m);if(a.length===0){b()}};function b(){c.log("No more forms, tearing down");angular.forEach(e,function(l){l()});e=[];d.onbeforeunload=null}this.confirmExit=function(){if(!h()){return c.reloadMessage}j.$broadcast("resetResettables");b()};function f(){c.log("Setting up");d.onbeforeunload=i.confirmExit;var l=c.routeEvent;angular.forEach(l,function(m){var n=j.$on(m,function(p,o,q){c.log("user is moving with "+m);if(!h()){c.log("a form is dirty");if(!confirm(c.navigateMessage)){c.log("user wants to cancel leaving");p.preventDefault()}else{c.log("user doesn't care about loosing stuff");j.$broadcast("resetResettables")}}else{c.log("all forms are clean")}});e.push(n)})}}]).directive("unsavedWarningClear",["unsavedWarningSharedService",function(a){return{scope:{},require:"^form",priority:10,link:function(d,c,b,e){c.bind("click",function(f){e.$setPristine()})}}}]).directive("unsavedWarningForm",["unsavedWarningSharedService","$rootScope",function(b,a){return{scope:{},require:"^form",link:function(e,d,c,g){var f=0;while(d[0].tagName!=="FORM"&&f<3){f++;d=d.parent()}if(f>=3){throw ("unsavedWarningForm must be inside a form element")}b.init(g);d.bind("submit",function(h){if(g.$valid){g.$setPristine()}});d.bind("reset",function(h){h.preventDefault();var i=angular.element(d[0].querySelector("[resettable]"));if(i.length){e.$apply(i.triggerHandler("resetResettables"))}g.$setPristine()});e.$on("$destroy",function(){b.removeForm(g)})}}}]);angular.module("resettable",[]).directive("resettable",["$parse","$compile","$rootScope",function(d,c,b){return{restrict:"A",link:function a(m,g,j,e){var h,k,l;j.$observe("ngModel",function(n){k=d(j.ngModel);h=k.assign;l=k(m)});var i=function(){h(m,l)};g.on("resetResettables",i);var f=m.$on("resetResettables",i);m.$on("$destroy",f)}}}]); \ No newline at end of file +"use strict";angular.module("unsavedChanges",["resettable"]).provider("unsavedWarningsConfig",function(){var f=this;var e=false;var b=true;var d=["$locationChangeStart","$stateChangeStart"];var c="You will lose unsaved changes if you leave this page";var a="You will lose unsaved changes if you reload this page";Object.defineProperty(f,"navigateMessage",{get:function(){return c},set:function(g){c=g}});Object.defineProperty(f,"reloadMessage",{get:function(){return a},set:function(g){a=g}});Object.defineProperty(f,"useTranslateService",{get:function(){return b},set:function(g){b=!!(g)}});Object.defineProperty(f,"routeEvent",{get:function(){return d},set:function(g){if(typeof g==="string"){g=[g]}d=g}});Object.defineProperty(f,"logEnabled",{get:function(){return e},set:function(g){e=!!(g)}});this.$get=["$injector",function(h){function i(j){if(h.has("$translate")&&b){return h.get("$translate").instant(j)}else{return false}}var g={log:function(){if(console.log&&e&&arguments.length){var j=[].slice.call(arguments);if(typeof console.log==="object"){log.apply.call(console.log,console,j)}else{console.log.apply(console,j)}}}};Object.defineProperty(g,"useTranslateService",{get:function(){return b}});Object.defineProperty(g,"reloadMessage",{get:function(){return i(a)||a}});Object.defineProperty(g,"navigateMessage",{get:function(){return i(c)||c}});Object.defineProperty(g,"routeEvent",{get:function(){return d}});Object.defineProperty(g,"logEnabled",{get:function(){return e}});return g}]}).service("unsavedWarningSharedService",["$rootScope","unsavedWarningsConfig","$injector","$window",function(j,c,k,d){var i=this;var a=[];var g=true;var e=[];this.allForms=function(){return a};function h(){g=true;angular.forEach(a,function(m,l){c.log("Form : "+m.$name+" dirty : "+m.$dirty);if(m.$dirty){g=false}});return g}this.init=function(l){if(a.length===0){f()}c.log("Registering form",l);a.push(l)};this.removeForm=function(m){var l=a.indexOf(m);if(l===-1){return}a.splice(l,1);c.log("Removing form from watch list",m);if(a.length===0){b()}};function b(){c.log("No more forms, tearing down");angular.forEach(e,function(l){l()});e=[];d.onbeforeunload=null}this.confirmExit=function(){if(!h()){return c.reloadMessage}j.$broadcast("resetResettables");b()};function f(){c.log("Setting up");d.onbeforeunload=i.confirmExit;var l=c.routeEvent;angular.forEach(l,function(m){var n=j.$on(m,function(p,o,q){c.log("user is moving with "+m);if(!h()){c.log("a form is dirty");setTimeout(function(){if(!confirm(c.navigateMessage)){c.log("user wants to cancel leaving");p.preventDefault()}else{c.log("user doesn't care about loosing stuff");j.$broadcast("resetResettables")}})}else{c.log("all forms are clean")}});e.push(n)})}}]).directive("unsavedWarningClear",["unsavedWarningSharedService",function(a){return{scope:{},require:"^form",priority:10,link:function(d,c,b,e){c.bind("click",function(f){e.$setPristine()})}}}]).directive("unsavedWarningForm",["unsavedWarningSharedService","$rootScope","$timeout",function(c,a,b){return{scope:{},require:"^form",link:function(f,e,d,h){var g=0;while(e[0].tagName!=="FORM"&&g<3){g++;e=e.parent()}if(g>=3){throw ("unsavedWarningForm must be inside a form element")}c.init(h);e.bind("submit",function(i){if(h.$valid){h.$setPristine()}});e.bind("reset",function(i){i.preventDefault();var j=angular.element(e[0].querySelector("[resettable]"));if(j.length){b(function(){j.triggerHandler("resetResettables")})}h.$setPristine()});f.$on("$destroy",function(){c.removeForm(h)})}}}]);angular.module("resettable",[]).directive("resettable",["$parse","$compile","$rootScope",function(d,c,b){return{restrict:"A",link:function a(m,g,j,e){var h,k,l;j.$observe("ngModel",function(n){k=d(j.ngModel);h=k.assign;l=k(m)});var i=function(){h(m,l)};g.on("resetResettables",i);var f=m.$on("resetResettables",i);m.$on("$destroy",f)}}}]); \ No newline at end of file