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.

24 Responses to “Fun with dojo.io.iframe.send”

  1. Bob McKeever Says:

    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.

  2. mike Says:

    Glad to help!

  3. Ashley Says:

    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”);

  4. Valdelievre Says:

    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

  5. Laura Says:

    Hi Mike, thanks a lot for this!
    Solving the issue using a textarea, can you please give an example?

  6. Laura Says:

    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!

  7. mike Says:

    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.

  8. Laura Says:

    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 :)

  9. Laura Says:

    Dunno why the JSP code was removed, if you allow me here it is one more time:

  10. mike Says:

    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.

  11. mike Says:

    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.

  12. Laura Says:

    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 :)

  13. Laura Says:

    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……

  14. Laura Says:

    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!

  15. mike Says:

    That’s great! Keep it simple!

  16. M Hayden Says:

    Saved my ass again. Thanks for blogging about this Mike.

  17. John Says:

    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!

  18. mike Says:

    John, Sorry I didn’t see your comment until just now. To answer your question: yes. Sorry I can’t elaborate right now.

  19. Steve Gourley Says:

    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.

  20. mike Says:

    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.

  21. Mahesh Says:

    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.

  22. mike Says:

    Mahesh, that depends what you’re using on the back end. I’m an Asp.NET MVC guy myself….

  23. Shiji Says:

    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.

  24. mike Says:

    Shiji, If you don’t control the endpoint, this technique is probably not gonna work.

Leave a Reply