Preventing inadvertent loss of information in ASP.Net

6/11/2008

One of the more annoying things with the web is that doing pretty much anything in between when you first start filling out a form and submitting it causes you to lose everything. At least most of the time. And I've known many, many people that have hit the back button only to lose whatever they were working on. So how can we help stop this inadvertent loss of information? Simple, we give them a second chance.

Using javascript (or the confirmbuttonextender in the AJAX Toolkit) we can simply have a confirm box pop up if they hit a button, link, etc. that is going to take them away from the page. But what about instances where they accidentally close the browser? That's where this little extender comes into play:

ChangeConfirmerExtender.cs

   1: using System;
   2: using System.Web.UI.WebControls;
   3: using System.Web.UI;
   4: using System.ComponentModel;
   5: using System.ComponentModel.Design;
   6: using AjaxControlToolkit;
   7:  
   8: [assembly: System.Web.UI.WebResource("AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.js", "text/javascript")]
   9:  
  10: namespace AJAXControls.ChangeConfirmer
  11: {
  12:     [Designer(typeof(ChangeConfirmerDesigner))]
  13:     [ClientScriptResource("AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior", "AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.js")]
  14:     [TargetControlType(typeof(Control))]
  15:     public class ChangeConfirmerExtender : ExtenderControlBase
  16:     {
  17:         // TODO: Add your property accessors here.
  18:         //
  19:         [ExtenderControlProperty()]
  20:         [RequiredProperty()]
  21:         [IDReferenceProperty(typeof(Button))]
  22:         public string SubmitButtonID
  23:         {
  24:             get
  25:             {
  26:                 return GetPropertyValue("SubmitButtonID", "");
  27:             }
  28:             set
  29:             {
  30:                 SetPropertyValue("SubmitButtonID", value);
  31:             }
  32:         }
  33:     }
  34: }

ChangeConfirmerBehavior.js

   1: // README
   2: //
   3: // There are two steps to adding a property:
   4: //
   5: // 1. Create a member variable to store your property
   6: // 2. Add the get_ and set_ accessors for your property
   7: //
   8: // Remember that both are case sensitive!
   9:  
  10:  
  11: /// <reference name="MicrosoftAjaxTimer.debug.js" />
  12: /// <reference name="MicrosoftAjaxWebForms.debug.js" />
  13: /// <reference name="AjaxControlToolkit.ExtenderBase.BaseScripts.js" assembly="AjaxControlToolkit" />
  14:  
  15:  
  16: Type.registerNamespace('AJAXControls.ChangeConfirmer');
  17:  
  18: AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior = function(element) {
  19:     AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.initializeBase(this, [element]);
  20:  
  21:     // TODO : (Step 1) Add your property variables here
  22:     this._DataChanged=false;
  23:     this._Saving=false;
  24:     this._SubmitButtonValue=null;
  25: }
  26: AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.prototype = {
  27:     initialize : function() {
  28:         AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.callBaseMethod(this, 'initialize');
  29:  
  30:         // TODO: Add your initalization code here
  31:         Sys.UI.DomEvent.addHandler(this.get_element(), 'keyup',Function.createDelegate(this, this._onchange));
  32:         Sys.UI.DomEvent.addHandler($get(this._SubmitButtonValue), 'click',Function.createDelegate(this, this._onclick));
  33:         Sys.UI.DomEvent.addHandler(window, 'beforeunload',Function.createDelegate(this, this._onunload));
  34:     },
  35:     
  36:     _onchange:function(){
  37:         this._DataChanged=true;
  38:     },
  39:     
  40:     _onclick:function(){
  41:         this._Saving=true;
  42:     },
  43:     
  44:     _onunload:function(event){
  45:         if(!this._Saving&&this._DataChanged)
  46:         {
  47:             event.preventDefault();
  48:         }
  49:     },
  50:  
  51:     dispose : function() {
  52:         // TODO: Add your cleanup code here
  53:  
  54:         AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.callBaseMethod(this, 'dispose');
  55:     },
  56:  
  57:     // TODO: (Step 2) Add your property accessors here
  58:     get_SubmitButtonID : function() {
  59:         return this._SubmitButtonIDValue;
  60:     },
  61:  
  62:     set_SubmitButtonID : function(value) {
  63:         this._SubmitButtonValue = value;
  64:     }
  65: }
  66: AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior.registerClass('AJAXControls.ChangeConfirmer.ChangeConfirmerBehavior', AjaxControlToolkit.BehaviorBase);

If you can't tell by now, I'm terrible at naming stuff... And this extender is really basic at that, so you'll have to go in and edit it yourself. But it does give you a good basis for building something more substantial. At present it just attaches itself to a textbox and a submit button. When the textbox's contents are changed, it registers the change. If the person tries to navigate away from the page, close the browser, etc. without hitting the submit button, it asks if the person wants to actually move away from the current page. Pretty basic really. And it wouldn't be that difficult to attach it to a set of inputs. Just switch the target to a div or form or something, find all inputs within said item, attach the _onchange function to each one and that would be it. The rest of the code wouldn't need to change. However, if you wanted to change the text in the popup, you'll have to rewrite the _onunload function. It simply uses the event's preventDefault function to stop the unloading of the page. Anyway, it should give you a good basis upon which to create your own control, so use the code, leave feedback, and happy coding.



Comments