Schneimi’s Dev Weblog


Update multiple fields with one ajax request response

Posted in Ajax, CakePHP by schneimi on the October 27, 2007
Tags: , , ,

I searched for a possibility to update multiple fields with each ajax request made by a remoteTimer, but didn’t find any satisfying explanation on the web. To get that right, each field should get updated with the same response of a request.

So I had a look closer into the ajax helper and the prototype framework and came up with a little hack that suffice my needs and might be also helpful for others.

In the ajax helper (cake/libs/helpers/ajax.php) we first have to look into what happens when the options[update] parameter is set to an array with the different fields we want to update. That leads us to the ‘remoteFunction’ where following happens in that case:

$func = "new Ajax.Updater(document.createElement('div'),";

I have no clue what the creation of the div is really good for, but anyway we have to replace it with a JavaScript array holding the id’s of our fields:

$update = '[';

foreach($options['update'] as $option) {
  $update .= "'" . $option . "',";
}            

$update .= ']';                

$func = "new Ajax.Updater({$update},";

Now we must have a look into the prototype framework (app/webroot/js/prototype.js) and make sure the function Ajax.Updater can handle that array. The important function there is called updateContent:

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {

  [...]

  updateContent: function() {

    [...]

    if (receiver = $(receiver)) {
      if (this.options.insertion)
        new this.options.insertion(receiver, response);
      else
        receiver.update(response);
    }

    [...]
  }
});

Because the receiver is now our array, we must step through it and send a response to each of it:

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {

  [...]

  updateContent: function() {

    [...]

    if (receiver.constructor.toString().indexOf("Array") != -1) {
      for(var i = 0; i < receiver.length; i++) {
        if(r = $(receiver[i])) {
      if (this.options.insertion)
        new this.options.insertion(r, response);
      else
        r.update(response);
    }
      }
    } else {
      if (receiver = $(receiver)) {
        if (this.options.insertion)
          new this.options.insertion(receiver, response);
        else
          receiver.update(response);
      }
    }

    [...]
  }
});

After checking if the receiver is actually an array, we step through it and send the response to every our fields.

Finished!

You can now use it like:

<?php echo $ajax->remoteTimer(array('url' => 'controller/action', 'update' => array('field1', 'field2', 'field3'), 'frequency' => '5')); ?>

9 Responses to 'Update multiple fields with one ajax request response'

Subscribe to comments with RSS or TrackBack to 'Update multiple fields with one ajax request response'.

  1. Ian said,

    Hi schneimi,

    This is *exactly* what I’ve been looking for but I’m not a JS wizard and the code in my version of prototype.js is not the same as your example – it now looks like this:

    updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
    options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
    if (options.insertion) {
    if (Object.isString(options.insertion)) {
    var insertion = { }; insertion[options.insertion] = responseText;
    receiver.insert(insertion);
    }
    else options.insertion(receiver, responseText);
    }
    else receiver.update(responseText);
    }
    }

    Any chance you could figure out what to change here ?

  2. Ian said,

    Oh, that’s version 1.6.0.1 of prototype.js btw ;-)

  3. schneimi said,

    Hi,

    I just noticed that my code isn’t presented correctly by wordpress, there is inserted the code for a smiley. ;-)

    But besides that, i think you only have to replace the code below your line:

    if (!options.evalScripts) responseText = responseText.stripScripts();

    with the corrected code above:

    if (receiver.constructor.toString().indexOf(“Array”) != -1) {
    for(var i = 0; i < receiver.length; i++) {
    if(r = $(receiver[i])) {
    if (this.options.insertion)
    new this.options.insertion(r, response);
    else
    r.update(response);
    }
    }
    } else {
    if (receiver = $(receiver)) {
    if (this.options.insertion)
    new this.options.insertion(receiver, response);
    else
    receiver.update(response);
    }
    }

    But I remember reading about another method for multiple updates on google groups without the need of hacking. The idea was to call a js-function on “onComplete” that does all the updates. But I am not sure how to pass the DIV-IDs, and I can’t find the thread about it again. Anyway, I hope this helps you.

  4. schneimi said,

    Again there is a smiley inside the code, just replace it with ‘)’ . ;-)

  5. Ian said,

    Thanks for that but I now get the two divs updated with the same content e.g. it looks like my requestAction for one of the divs is being ignored and the primary requestAction is being used to fill out the content in both.

    I was hoping that the “update” would trigger the request action in each div – is this not the case ?

  6. schneimi said,

    No sorry, this is not the case, it’s all about one requestAction and multiple DIVs getting updated with the same content.

    I am not sure what you want to do, if the action would be triggered again for the other DIV, wouldn’t you get the same content as well?

  7. Ian said,

    I essentially have a list of tags that I can assign to an object (HABTM relationship) and I’d like to be able to drag tags onto an object to assign them and then update the list of available tags to show what’s assigned / left to assign. The drop correctly modifies the relationship and the assigned div get’s updated correctly but I have to “refresh” the page to update the div containing the list of available tags.

  8. schneimi said,

    Ok now I understand what you need, this is much simpler and has nothing to do with my article. ;-)

    You just need to tell the ajax request that is triggered on drop, to update the tags-div on completion.

    The $options for this ajax request could look like:

    $options = array(…, ‘complete’ => $ajax->remoteFunction(array(‘url’ => ‘tags/list’, ‘update’ => ‘tags_div’)));

  9. Ian said,

    that’s it – great !

    Thanks for the help – I think I’ll leave the modified prototype stuff as it may come in useful somewhere else.


Leave a Reply