views:

430

answers:

2

We need to universally handle changed data on forms in ASP.NET MVC. Our application has ~100 forms, and the user should be prompted if they start editing a form and click on anything other than Save (i.e. something like "Your data has been changed. Click OK to return to the form, or Cancel to lose all changes.").

It looks like SO implements this (while asking a question) using JavaScript. In general, is this the best way? Also, any tips on how best to implement this?

+2  A: 

The way I've done this is to use javascript to store the initial values of inputs when the page loads. Then I have a beforeunload handler that checks to see if any of the inputs have a different value than when the page was loaded. If any inputs are changed, it prompts the user to confirm that they want to leave the page, canceling the action if they cancel. In my submit logic, I set a flag that keeps the beforeunload check from happening so a submit doesn't get the prompt.

I suspect there is a jQuery plugin to do this, but I haven't implemented this since I started using jQuery. My earlier code used Prototype.

Edit: Couldn't find a jQuery plugin, but I could have just missed it. Here's a sample of how I might do it. Obviously, there's more that could be done. Note I wasn't able to get it to work with pure jQuery -- not sure exactly why, the popup would appear twice.

This should work with all input elements. You might want to change it to ignore/handle buttons, though. I only adjusted it to ignore a submit button (so it can post back without the popup). If other button types can cause a page unload, you may need to address that.

var CheckOnClose = function() {
    this.initialize();
}

CheckOnClose.prototype = {
    submitting: false,
    initialize: function() {
     var that = this;
     window.onbeforeunload = function() { return that.checkLeavePage(); }
    },
    isChanged: function() {
     var changed = false;
     $('input:not(:submit)').each( function() {
      var iv = $(this).data('initialValue');
      if ($(this).val() != iv) {
       changed = true;
       return false;
      }
     });
     return changed;
    },
    setSubmitting: function() {
     this.submitting = true;
    },
    checkLeavePage: function() {
     if (!this.submitting && this.isChanged()) {
      return 'You have some unsaved changes.';
     }
    }
}

var checker = new CheckOnClose();

$(document).ready(function() {

    $(':input:not(:submit)').each( function() {
     $(this).data('initialValue',$(this).val() );
    });
    $(':submit').click( function() {
     checker.setSubmitting();
    });
});
tvanfosson
I would suggest that the function that checks before/after values is called from a the onblur event... that way you can easily scale it with jquery ($('.check_changes').blur(func);). In order to make this even easier, at page load time I'd save the initial value into a new property of the element... you can do this via jquery, too.
James S
I should add that if that function determines that the element has changed, then it should modify an isDirty variable at the page scope. Then, when you're deciding whether or not to allow the user to leave, you only have to check this variable.
James S
@jamesshannon -- using jQuery, I'd suggest using the data() method and store the initial value that way.
tvanfosson
@jamesshannon or @tvanfosson - would this automatically apply to all controls on the form, or would each one need to be added individually? I want to make sure someone doesn't need to remember to add a control to a list of values to check (since inevitably they'll forget).
Jess
@LuckyLindy -- my code sample works with all inputs. If your submission uses ajax and makes non-standard use of other controls as inputs, you'll need to adjust the function that records initial values/checks changes.
tvanfosson
@jamesshannon -- I tried your blur handler to retrieve changes, but it didn't reliably run when changing an input then clicking a link. I think that the beforeunload event was occurring prior to the blur. I had to change it back to look for changes in the beforeunload handler.
tvanfosson
@tvanfosson - this looks awesome, thanks! I'll try implementing over the next few days.
Jess
+1  A: 

JavaScript is your only shot for doing this. It doesn't even have to be a complicated bunch of code. All you have to do is have a global variable to flag if the form is in editing stages (var formEdited = false; would do), and then add this snippet to your page:

 window.onbeforeunload = confirmExit;
    function confirmExit()
    {
        if (formEdited)
        {
                return "You have attempted to leave this page.  If you have made any changes to the fields without Submitting the form, your changes will be lost.  Are you sure you want to exit this page?";
         }
          // no changes - return nothing      
    }
synhershko
Of course jQuery/Prototype/other libraries could be used as well, but the basic implementation is as simple as the above snippet.
synhershko