How to POST cross domain and access the returned data using ExtJS

Please refer to my earlier post for background on this project.

Here is how to post data to a server on a different domain and receive data back using ExtJS (with inspiration from the GWT implementation).

First the FormPanel. The fields are standard and in the JS file we define two variables that are critical to making this work as they’ll allow us to keep track of what’s happening:

//These two variables are critical to the success of this operation

var xdm_formSubmitted = false;
var xdm_sameDomainRestored = false;
var renderDivId='myWidget';

var consigneeForm = new Ext.FormPanel(
{
 id: 'customerWidget',
 renderTo:renderDivId,
//By default, ExtJS uses XmlHttpRequest, this has to be a standard submit POST
standardSubmit: true,
 url: 'http://www.iamonanotherdomain.com/addThisCustomerForMe',
 border: false,
 hideBorders: true,
 items:
[
{
 xtype: 'textfield',
 name: 'customerFirstName'
 },
{
 xtype: 'textfield',
 name: 'customerLastName'
 }

],

bbar:
[
//This is where the form post is triggerd
{
 xtype: 'tbspacer',
 width: 300
} ,

{
 text: 'Save',
 handler: function()

{
 var form = Ext.getCmp('customerWidget').getForm();
 form.getEl().dom.target=iframePostContainerName;
 //On Submit we set this variable to true, you'll understand why in the event handler function
 xdm_formSubmitted = true;
 form.submit();
}
}
]

Notice that before submitting, we are assigning the target of the post to an iframe that exists on the page (form.getEl().dom.target=iframeName). This is the second and crucial step of the process. The iframe we are using is defined in the same js file:


//This hidden iframe is used to receive the results of the form POST:

{
 hidden: true,
 renderTo:'myHiddenIFrame',
 html:'<iframe id="' +iframePostContainerName + '" name="' +iframePostContainerName+ '"   onload=\"setupIFrameOnLoad(\'' + callbackFunction +  '\')"></iframe>'

}
//The callbackFunction variable is passed to the script by the calling page.

Which brings us to the third critical piece of the process which is to attach an event handler function to the onload event of the iframe, and this is where all the magic will happen. As you can see i’ve name my event handler function setupIFrameOnLoad and here is the definition:


setupIFrameOnLoad = function()
{
 //If the form was submitted and we have loaded data from our own domain, we are good. Thank you for coming
 //and here is your data! It's gonna be 5 dollars, Thank you!
 if(xdm_formSubmitted && xdm_sameDomainRestored)
 {
      //You can access your data now using the window.name property, but
      //I've got other plans in my callbackFunction so until such time...
      var callbackFunctionCall = callbackFunction + "()";
      eval(callbackFunctionCall);
 }
 //If this is the initial response from the POST, we are still in the POST server's domain
 else if(xdm_formSubmitted && !xdm_sameDomainRestored)
 {
       //Now you know we're about to restore the local domain right?
       xdm_sameDomainRestored = true;
       //localResourceUrl is passed by the calling page and points to a local 1px image or empty page
       document.getElementById(iframeName).contentWindow.location = localResourceUrl;
 }
 else
 {
       return ;
 }
}

What is happening here is basically the solution to the cross-domain issue in being able to pass data back to the calling page. Once your form is submitted, the response will be posted back to your iframe within your form page. Since the response is coming back from the server, security on the browser will keep us from accessing any of the properties on the window. To be able to access the data in window.name(which could be in JSON format if you wanted), the solution is simply to load a local resource file from your environment. It could be a simple blank 1px image or an empty HTML file, anything really to switch the iframe’s source content to be on the same domain where you are calling from. Once the iframe has reloaded you can simply access your data in the window.name property. To close the cycle, the response from your server needs to be a script which sets the window.name property. Using Spring, here is how my action was set up:

</pre>
private void addThisCustomerForMe(HttpServletRequest req, HttpServletResponse response) throws Exception
{

Long customerId = customerController.addOrUpdateCustomer(req);
response.setContentType("text/javascript");
OutputStreamWriter os = new OutputStreamWriter(response.getOutputStream());
StringBuffer buf = new StringBuffer("");
buf.append("<html>");
buf.append("<script type=\"text/javascript\">");
buf.append("  window.name="+customerId+";");    // can set json data here if you want
buf.append("</script>");
buf.append("</html>");

os.write(buf.toString());
os.flush();
os.close();

}

Cross Domain Form Post (XDM) in ExtJS with returned data

I’ve helped a colleague of mine work on a cross domain widget we are building for our applications that allows a user from an application to add information to another application through a pop up widget. Implementation wise, we used a  a simple form implemented using ExtJS FormPanel class. Some of the form fields like State and County dropdowns are  dynamically loaded after rendering, again using ExtJS’s ScriptTagProxy for cross-domain retrieval, no issue here and for more information on cross-domain data retrieval you can check out this post here.

The issue rose when it came to posting the data back to the server on another domain for processing. I did a bit of googling around and stumbled upon this post which explains how it can be done using a combination of server side programming and a little bit of Javascript and browser knowledge. To make a long story short, basically you can post data to a remote domain, and get data back by using the window.name property of an hidden iframe within your form page. Simply ingenious if you ask me,  I knew GWT was using the technique but did not know the the inner workings. Check out this post here for the actual implementation.

By the way if you a looking for a good XDM library, check out EasyXDM.

Addendum: As noted by Oyvind Sean Kinsey in the comments below, what i was trying to accomplish here was XDM, Cross Domain Messaging and not XSS Cross Site Scripting which is actually a security exploit. Thanks again Sean for the clarification, i understand now why the EasyXDM library was renamed from being initially called EasyXSS.

Invalid Argument Error using ScriptTagProxy and IE6

As explained in my earlier post, i used Ext.data.ScriptTag proxy for some cross-domain AJAX data retrieval. Testing in IE6, I kept getting an “Invalid Argument” error in IE traced to this line in the code. A search on the Sencha forum turned up only this post, with no good solution posted. I traced the problem to the this.head.removeChild lines, for some reason, IE6 chokes on it. The fix is quite simple, i overrode the function to use parentNode.removeChild:

Ext.override(Ext.data.ScriptTagProxy, {
	destroyTrans : function(trans, isLoaded){
	try
	{
    	this.head.removeChild(document.getElementById(trans.scriptId));
	}
	catch(e)
	{
		//IE6 does not like removeChild() to be called directly from the parent element
		  document.getElementById(trans.scriptId).parentNode.removeChild(document.getElementById(trans.scriptId))
	}
    clearTimeout(trans.timeoutId);
    if(isLoaded){
        window[trans.cb] = undefined;
        try{
            delete window[trans.cb];
        }catch(e){}
    }else{
        // if hasn't been loaded, wait for load to remove it to prevent script error
        window[trans.cb] = function(){
            window[trans.cb] = undefined;
            try{
                delete window[trans.cb];
            }catch(e){}
        };
    }
}
	});