Update multiple fields with one ajax request response
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')); ?>
on July 4, 2008 on 8:55 am
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 ?
on July 4, 2008 on 8:56 am
Oh, that’s version 1.6.0.1 of prototype.js btw
on July 5, 2008 on 8:59 am
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.
on July 5, 2008 on 9:03 am
Again there is a smiley inside the code, just replace it with ‘)’ .
on July 7, 2008 on 9:25 am
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 ?
on July 7, 2008 on 9:48 am
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?
on July 7, 2008 on 12:30 pm
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.
on July 7, 2008 on 1:57 pm
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’)));
on July 7, 2008 on 2:25 pm
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.