AngularJS – Warn on unsaved form changes

Happy Tuesday everybody! It’s actually raining here in San Diego, wow.

 

Anyway – I needed a way to check if an Angular form had been modified and warn my user. I found this question that offered some help. The caveat: I couldn’t use any specific form names, since the forms already had names and I didn’t want to force users to add IDs to their forms, so I had to modify it a bit.

To use this, we add confirm-on-exit as an attribute to the form element.


<form name="anything" confirm-on-exit>......</form>

Then attach this directive to your application, and you’re all done!


(function() {
'use strict';
 
 angular.module("app").directive('confirmOnExit', function() {
 return {
 link: function($scope, elem, attrs) {
 if (elem[0].nodeName === 'FORM') { //the user wants to check a form
 window.onbeforeunload = function(){
 if (angular.element(elem[0]).hasClass('ng-dirty') && !angular.element(elem[0]).hasClass('ng-submitted')) {
 return "You have unsaved changes - are you sure you want to leave the page?";
 }
 }
 $scope.$on('$locationChangeStart', function(event, next, current) {
 if (angular.element(elem[0]).hasClass('ng-dirty') && !angular.element(elem[0]).hasClass('ng-submitted')) {
 if(!confirm("You have unsaved changes - are you sure you want to leave the page?")) {
 event.preventDefault();
 }
 }
 });
 }
 else { //the user wants to check a specific collection
 if (attrs.confirmOnExit) {
 var loaded = false;
 var hasUnsavedChanges = false;
 $scope.$watchCollection(
 attrs.confirmOnExit,
 function( newValue, oldValue ) {
 if ( !loaded && newValue !== undefined ) { // the collection will change once on initialization; allow for this
 loaded = true;
 }
 else if( newValue !== undefined ){ // the collection has already been initialized; mark it as changed
 hasUnsavedChanges = true;
 }
 }
 );
 
 $scope.$on('$locationChangeStart', function(event, next, current) {
 if ( hasUnsavedChanges ) {
 if(!confirm("Make sure you save your changes before you go!")) {
 event.preventDefault();
 }
 }
 });
 }
 else {
 console.log('Please pass in a collection for confirm-on-exit to work correctly.');
 console.log('Example: <div confirm-on-exit="collectionName">...</div>');
 }
 }
 }
 };
 });
 
 
})();

(Sorry about the line spacing – my code plugin needs to be replaced)

Instead of using the form’s name, we use angular.element(elem[0]) to grab the form because Angular uses jqLite natively. Another cool thing is that we will only warn the user if the form hasn’t been submitted. This way, the user’s good to navigate away after they clicked the submit button.

I just updated this with the ability to pass in a collection and watch that collection for changes – this will allow us to monitor more complex forms for changes.

Hope your day is going great!

 

Cheers,

Sam

 

Author: Sam

Tinkerer. I like making things.

Leave a Reply

Your email address will not be published. Required fields are marked *