After reading Tim Tripcony’s blog post , I thought about a way how to cancel a partial refresh via server side javascript. To bring this to life, there are just three things to do:
- Abort the processing of the request on the server
- Give feedback to the client that request is canceled
- Stop Dojo to process the XHR request
To stop the processing of a request on the server and to send an empty response to the client, this SSJS code can be used:
var response = facesContext.getExternalContext().getResponse();
response.reset();
response.commitResponse();
The client will receive an empty HTTP response body:
A HTTP Header has to be added to the response to inform Dojo that the request was canceled. In this example it is “X-Partial-Refresh-Cancel“:
response.setHeader("X-Partial-Refresh-Cancel", "1");
The header is now added to the HTTP response and sent back to the client:
The XHR response must be hijacked before it is processed by Dojo. To do this, a new XHR function has to be injected between the existing one. Here is a basic code snippet:
var xhrLoad = null; // placeholder for the original function
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
dojo.xhr = function(){
var args = arguments[1];
xhrLoad = args["load"]; // "save" the original load function
// and overwrite with a new one
args["load"] = function( a, ioArgs ){
// execute custom code
// call the original load function:
xhrLoad( a, ioArgs );
}
// do XHR request
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
The load function of a Dojo XHR request has a parameter ioArgs. This parameter gives a handle to an object that allows to access the HTTP response headers, so the response can be identified as canceled:
var canceled = ioArgs.xhr &&
ioArgs.xhr.getResponseHeader("X-Partial-Refresh-Cancel");
if( canceled ){
XSP.allowSubmit();
return;
}
If the HTTP header is set, the processing of the request can be stopped. After stopping the request, the XSP object is not allowed to fire the next event. To allow this again, the function XSP.allowSubmit() must be called.
Here is a demonstration XPage with a button and a label. The partial refresh will never be processed, instead a “Canceled” message will occur.
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="partial" refreshId="label1">
</xp:eventHandler>
</xp:button>
<xp:br/><xp:br/>
<xp:label id="label1">
<xp:this.value>
<![CDATA[#{javascript:
var response = facesContext.getExternalContext().getResponse();
var ajax = new com.ibm.xsp.ajax.AjaxUtil();
if( ajax.isAjaxPartialRefresh(facesContext) == true){
response.setHeader("X-Partial-Refresh-Cancel", "1");
response.reset();
response.commitResponse();
return;
}
java.lang.System.currentTimeMillis();}]]>
</xp:this.value>
</xp:label>
<xp:br/>
<xp:br/>
<xp:scriptBlock id="scriptBlockXHRHandler">
<xp:this.value><![CDATA[
var xhrLoad = null;
dojo.addOnLoad( function(){
if( !dojo._xhr )
dojo._xhr = dojo.xhr;
dojo.xhr = function(){
try{
var args = arguments[1];
xhrLoad = args["load"];
args["load"] = function( a, ioArgs ){
var canceled = ioArgs.xhr &&
ioArgs.xhr.getResponseHeader("X-Partial-Refresh-Cancel");
if( canceled ){
alert("Canceled!");
XSP.allowSubmit();
return;
}
xhrLoad( a, ioArgs );
}
}catch(e){}
dojo._xhr( arguments[0], arguments[1], arguments[2] );
}
});]]>
</xp:this.value>
</xp:scriptBlock>
</xp:view>