Save intermediate input by AJAX ICallbackEventHandler when form session is timeout

For large form with tons of editor controls, if user does not finish the input before it’s timeout, user will have to fill the form again from scratch. That would be frustrating :(

A user-friendly workaround is to prompt user the choice to give up intermediate result or save it, when user tries to save the form and find it is timeout. If user chooses to save it should save anything that have inputted by a partial request to server.

To implement ICallbackEventHandler interface for the editor page is able to control the round-way interaction between server code and client scripts.

The authentication of my project is in form based mode, so have to compromise the security to move the editor page out of the authentication scope. By achieving that, need add following code to web.config.

  <location path ="FormPath/FormPageName.aspx">
   
<system.web>
     
<authorization>
       
<allow users="*"/>
      </
authorization>
   
</system.web>
 
</location> 

The FormPageName.aspx is derived from PageBase.cs, so the session timeout code will be put in the OnInit method of the base page. We need define an event in the base class, which is used to notify inheritted page when timeout happens.

       public delegate void TimeOutDelegate();
       public TimeOutDelegate OnTimeOut;
        protected override void OnInit(EventArgs e)
       
{
          
 if (Context.Session != null)
           
{
               
if (Session.IsNewSession)
               
{
                   
string strCookieHeader = Request.Headers["Cookie"];
                   
if (strCookieHeader != null && strCookieHeader.ToUpper().IndexOf("ASP.NET_SESSIONID") >= 0)
                   
{
                       
//On timeouts, redirect to timeout page.
                                             
                           
if (Page.IsPostBack && OnTimeOut != null)
                           
{
                               
TimeOutArgs arg = new TimeOutArgs();
                               
OnTimeOut();
                           
}                       
                   
}
               
}
           
}
           base.OnInit(e);
               }

In actual page, we need implement ICallbackEventHandler, that’s the critical part of this solution. That requires to implement RaiseCallbackEvent method and GetCallbackResult function, which are respectively process the AJAX request from client side and return a string typed result back to client.

public partial class FormPageName : PageBase,ICallbackEventHandler
 
        private string _callbackResult = "false";
       
public string GetCallbackResult()
       
{
           
return _callbackResult;
       
}

       
public void RaiseCallbackEvent(string eventArgument)
       
{
           
string[] args = eventArgument.Split(new string[] { "^^" }, StringSplitOptions.None);
           
if (args.Length == 3 && args[0].Equals("SaveTempChanges"))
           
{
               
try
               
{
                   
int companyID =0;
                   
if (Int32.TryParse(args[2], out companyID) == true
                       
&& companyID != 0
                       
&& !String.IsNullOrEmpty(args[1]))
                       
SaveData(companyID, args[1].ToString());
                    _callbackResult = “true”;
               
}
               
catch
               
{ }
           
}
       
}
In the OnInit override of FormPageName.aspx, it should also register the listener & actual processing routine for he OnTimeOut event to respond the particular notification message from base page.
        protected override void OnInit(EventArgs e)
       
{
           
string cbReference = this.ClientScript.GetCallbackEventReference(this, 
                                  "arg"
, "ReceiveServerData", "context", false);
           
string callbackScript = "function CallServer(arg, context)" 
                                    
+ "{ " + cbReference + ";}";
           
this.ClientScript.RegisterClientScriptBlock(this.GetType(), 
                                    "CallServer"
, callbackScript, true);
           
base.OnTimeOut += new TimeOutDelegate(TimeOutProcessing);
           
base.OnInit(e);
       
}

       
protected void TimeOutProcessing()
       
{
           
if(Page.IsPostBack)
               
ClientScript.RegisterStartupScript(this.Page.GetType(), 
                   "callTimeoutProcessing"
, "ConfirmSaveTempPageState();", true);
       
}

The code line “this.ClientScript.GetCallbackEventReference(this,  "arg", "ReceiveServerData", "context", false); ” is to generate a JavaScript code to make partial request to server end function. It indicates the name of JS callback function as “ReceiveServerData”, which should be coded to process the result from the partially requested server function. Parameters “arg” and “context” respectively represents the argument list and context info that should be passed via the request; and finally the generated partial request JS code will be wrapped up in the function called as CallServer.  When page is loaded, it will generate scripts like this:

<script language="javascript" type="text/javascript">
function CallServer(arg, context){ 
   
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</script>
The actual delegate routine is to call a JS function, which is supposed to prompt user the timeout event, so in FormPageName.aspx, we need add script function ConfirmSaveTempPageState and the ReceiveServer function is to do post processing on client side after the partial request is completed on server end.
      function confirmCallBackFn(arg) {
           
if (arg == "true") {                
               
var context = $("input[id$='hidUserID'")[0].value + "^^" + $("input[id$='hidCompanyID'")[0].value;
               
CallServer("SaveTempChanges^^" + context, context);
           
} 
       
}
       
function ReceiveServerData(rValue, context) {
           
CloseAndRebind();
       
}
       
function ConfirmSaveTempPageState() {
           
if (confirm('Your session is timeout, do you want to save your current changes?'))
               
confirmCallBackFn("true");            
           
else
               
CloseAndRebind();
       
}
 

The confirm popup will leave user the choice to persist middle result or give it up. If user chooses not to save, it will close the current page, and refresh the back-end openner page for routing back to login page. However, if user chooses to save, it will make the partial request by calling CallServer function, which will consequently trigger the function WebForm_DoCallback to call server end function, so that the RaiseCallbackEvent routine will get invoked and after completing save the page data, the GetCallbackResult function will return client side a response. The response will be processed by the client function ReceiveServerData and the argument ‘rValue’ contains the returned string of GetCallbackResult function.

posted @ 2011-04-03 16:20  Allen Xu  阅读(348)  评论(0编辑  收藏  举报