Fun with dojo.io.iframe.send
One of my projects has had functionality to export a map image for some time. To get the map image, I just call (javascript) window.open and point the url to my http handler that will return the map image (with additional querystring parameters that specify the map parameters). This was working fine but now the customer wants to include lots of other markup on the map, including stuff they’ve drawn on the map with the nifty drawing tools I’ve provided them. This presented a bit of a problem because if I continued to just use window.open, the querystring was gonna get really long. So I realized I’d have to post the map parameters to my handler. But as far as I know, there’s no way to post data when you call window.open. So what to do?
The first thing I thought of was that I could post the parameters using dojo.xhrPost and then prepare the image and return the url to it. Then I’d call window.open with the url returned from the first request. I didn’t really like that. For one thing, it would be two requests, but for another, I’d have to keep that map image around in some manner.
So I decided to use dojo.io.iframe.send. This ended up working very well but I did run into a few issues that I thought I’d share.
One thing that I didn’t realize at first is that dojo expects the response to be an html document. And if you want to return anything other than an html document (json or text for example), you must wrap it in an html textarea. Dojo will look inside the first textarea in the response document and return to your handle, load, or error functions whatever it finds there in the format you specified in handleAs (defaulting to text if you did not specify one).
Another thing I ran into was that my dojo.io.iframe.send call would only work once per page load. I’m not exactly sure why that is. I found a suggestion that you set a timeout on the __IoArgs object passed to iframe.send. That worked in the sense that I could call it again after the timeout expired but I have no idea how long this operation is going to take so it’s hard to know what to set the timeout to. My solution was to keep a reference to the dojo.Deferred object returned by iframe.send and call cancel on it before trying to send another request like so:
if (this.getMapImageDeferred)
{
this.getMapImageDeferred.cancel();
}
This seems to work fine but I do have to check in my errBack function whether the request was canceled:
_mapImageCallbackError: function(response, ioArgs)
{
if (response.message === 'Deferred Cancelled')
{
return response;
}
// handle the error...
},
The last thing I ran into really had me stumped. I was setting method = “post” in the __IoArgs object passed to iframe.send but it was always using get. I read a few things here and there that kind of seemed to suggest that if I passed a form with its method set to ‘post’ into __IoArgs that might do the trick. And it did! So here’s what I’m doing now:
//create a form with method=post
var form = document.createElement('form');
dojo.attr(form, 'method', 'post');
document.body.appendChild(form);
//create my postdata
var content = {
/*set a bunch of parameters here that
will be posted to the http handler*/
};
//make the request
this.getMapImageDeferred = dojo.io.iframe.send({
url: path + 'map.mapimage.aspx',
form: form,
content: content,
error: dojo.hitch(this, this._mapImageCallbackError)
});
//get rid of the form created above
document.body.removeChild(form);
I’d be interested to know if anyone else has run into these issues and how you resolved them.
February 10th, 2010 at 3:32 pm
Thanks for posting this. It helped me fix the problem where the save dialog would not show without refreshing the page. I fixed the first problem involving the text area last night before I found your site. That burned me for a little while.
February 10th, 2010 at 3:44 pm
Glad to help!
February 25th, 2010 at 12:46 pm
Creating the temporary form was key. It would have taken forever for me to figure that one out. I needed the form because I could find no other way to to set my contentType.
dojo.attr(form, “enctype”, “multipart/form-data”);
May 9th, 2010 at 1:26 pm
hey guys,
I had some problems when developped my home made multiple file uploader.
@mike, take a look at http://developers.sirika.com/mfu/
Im using dojo.io.iframe.send, check out the code to understand the trick
Cheers
October 1st, 2010 at 5:08 pm
Hi Mike, thanks a lot for this!
Solving the issue using a textarea, can you please give an example?
October 1st, 2010 at 5:52 pm
Hi again, my code is like this:
function downloadProof(oweId, event){
dojo.byId(“dispatch”).value = “downloadProof”;
dojo.byId(“oweId”).value = oweId;
dojo.io.iframe.send({
form: “ldfApprovalForm”,
handleAs: “html”,
load: function(response, args)
{return response;},
error: function(response, args)
{ dojo.byId(“errorAjaxMessage”).innerHTML = dojo.toJson(response);
dijit.byId(“errorAjaxDialog”).show();
return response;
}});}
Would be too much trouble if I ask for some suggestions on how to rewrite this function (is the onclick action of a list of buttons)? Thanks!
October 4th, 2010 at 10:24 am
Laura, I’m not sure exactly what you’re asking or what your code is supposed to do. But with regard to the textarea issue, the solution depends on your server side platform. What you need to do is have your server method return an html document with a textarea. And in that textarea, put whatever you want to return to your load/ error methods. Also note that when I wrote this post, I was using a much older version of dojo, so you may want to check the documentation for the current version.
October 5th, 2010 at 8:37 am
Hey Mike, thanks! What I want to do is to download some files. I have a list of buttons, and each one points to a file. In the original format, only one == the first button that I press returns me the file, any other after the first has no effect. I’m using Java & Struts, where I have mapped the corresponding form (which in JSP is “ldfApprovalForm”) and action, based on parameter dispatch. So when dispatch is downloadProof, it should run the code behind the method.
Your solution is very elegant, the thing is I cannot apply it in my case, because I already send the form I have in my page, and I cannot create another one and remove it at the end of the Javascript method. And if I create it, dispatch wouldn’t know what to do with it, and if I remove the form I already have then I’m messing up all the page. So it’s a little complicated.
I apologize for writing all this here, as I see the official forum is closed and I couldn’t find there a proper solution.
I want to try the textarea thing, but I don’t understand how am I suppose to add it to the JSP code, which now looks like this:
…
I’ve posted the original JS method upper. I’m sure everything else is working, but the dojo.io.iframe.send part. Lots of fun indeed
So I’m asking you kindly, if with your great experience and wisdom can guide me to solve this
October 5th, 2010 at 8:41 am
Dunno why the JSP code was removed, if you allow me here it is one more time:
…
October 5th, 2010 at 4:14 pm
So the problem is that it only works the first time? If that’s the case then the only solution I found is to keep a reference to the dojo.Deferred object returned by iframe.send and call cancel on it before trying to send another request. The only gotcha there is that calling cancel will cause it to hit your errback function so you just have to check the message for ‘Deferred Cancelled’ and return from the errback. I discuss this in my post above.
October 5th, 2010 at 4:17 pm
By the way, if you are using iframe.send to download files then you don’t need to worry about the textarea thing except if you want to return json or something in case of an error on the server. Note how in my code above there is no load parameter passed to iframe.send. That’s because I’m using it to download files too.
October 5th, 2010 at 4:26 pm
Wow Mike you’re awesome! What I’m trying now is to clone the form before I send it, so I can remove it after and add back to the page the clone, fingers crossed hope it works, if not I’ll try Deferred, I’ll let you know my progress
October 6th, 2010 at 4:22 pm
Ok, the form cloning didn’t work, I think I’ve been chasing my tail trying this one. testing Defereed right now, and for a strange reason cancelling it isn’t triggering (even if I don’t put any condition before cancel). I have my method like this:
function downloadProof(oweId, event)
{ deferred.cancel();
dojo.byId(“dispatch”).value = “downloadProof”;
dojo.byId(“oweId”).value = oweId;
deferred = dojo.io.iframe.send({
form: “ldfApprovalForm”,
handleAs: “html”,
load: function(response, args)
{return response;},
error: function(response, args)
{dojo.byId(“errorAjaxMessage”).innerHTML = dojo.toJson(response);
dijit.byId(“errorAjaxDialog”).show();
return response;
}
});
deferred.callback({success: true});
return deferred;
}
with “deferred” as global variable, and in doc init I’m initializing it like this:
deferred = new dojo.Deferred();
deferred.addErrback(downloadPFError);
Can you please tell me what am I doing so horrible wrong that the value of the fired property of deferred is always and for eternity 1? It doesn’t change, doesn’t enter errback, doesn’t become true or 0.
(deferred.fired==1 means error, according to http://api.dojotoolkit.org/jsdoc/1.3.2/dojo.Deferred)
Internet is so poor on resources regarding this……
October 7th, 2010 at 2:42 pm
Hey Mike, I solved my little problem without using iframe.send, just by submitting the form
Made some changes in the JSP form, so the JS function only does submit the form!
Next time I’ll take the simple way from the very beginning!
October 7th, 2010 at 3:00 pm
That’s great! Keep it simple!
January 20th, 2011 at 2:55 pm
Saved my ass again. Thanks for blogging about this Mike.
March 17th, 2011 at 4:15 pm
Mike, I’m a bit of a newbie in dojo. Can I use dojo.io.iframe to download a file from a servlet? Thanks in advance!
April 7th, 2011 at 8:40 am
John, Sorry I didn’t see your comment until just now. To answer your question: yes. Sorry I can’t elaborate right now.
June 15th, 2011 at 10:26 am
you can shorten up your code a bit by by combining some api calls.
var form = dojo.create(‘form’, { method: ‘POST’ }, dojo.body());
//iframe.send
dojo.destroy(form);
it’s cool your article comes up 1 in goog over dojo api stuff when i search for io.iframe.send.
June 15th, 2011 at 10:32 am
Yeah, this is by far my most popular post.
I left it a little more verbose figuring it might be slightly easier to understand. Also, I’m pretty sure this code used a version of dojo prior to the introduction of create/ destroy.
January 19th, 2012 at 12:46 am
I m using dojo.io.iframe.send method for CSV file downloading. I wanted to send some parameters which are required in my rest method. For this i m writing the parameters into the “content” parameter. My problem is how this “content” parameter can be accessed in my Rest API. Please let me know how can this be done.
January 20th, 2012 at 11:26 am
Mahesh, that depends what you’re using on the back end. I’m an Asp.NET MVC guy myself….
February 3rd, 2012 at 12:08 am
Hi Mike, am using dojo.io.iframe.send to retrieve data from a remote URL using their API service. I am able to post the data, but after successful submission, I get a window asking whether I want to save/open/cancel the response json file. The flow doesnt enter my load function, unless I do a refresh.
I dont want this window to appear, I want it to go to my load function directly. Can you suggest a solution.
I’ve given ‘handleAs: “json”‘, but since its a remote URL call, I dont know if they are returning the response in a textarea.
February 3rd, 2012 at 8:03 am
Shiji, If you don’t control the endpoint, this technique is probably not gonna work.