I have a module that updates a node via ajax when a link is clicked.
The link is a toggle, it should update the node with value 1 on first click, then with value 0 on a subsequent click, etc. Like turning something on/off.
The code below works on the first click after page load, but not on subsequent clicks. I believe Drupal.attachBehaviors has to be called/triggered after each clicks, but I can't figure out how to do this.
The Module
function mymodule_menu() { $items['mypath/%/%/ajax'] = array( 'title' => 'My title', 'page callback' => 'mymodule_ajax_callback', 'page arguments' => array(1,2), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); ... } function mymodule_ajax_callback($id, $status) { //Validation[...] //Node Update using $id as the nid and $status as the field value[...] // Define a new array to hold our AJAX commands. $ajax_commands = array(); // Create a new AJAX command that replaces the #div. $replacedivid = '#status'.$id; $replacestring = '<div id="status'.$id.'"><a data-url="'.base_path().'mypath/'.$id.'/'.$new_status.'/ajax" title="This item is marked as '.$status_text.'" id="statuslink'.$id.'" class="midui">'.$status_text.'</a></div>'; $ajax_commands[] = ajax_command_replace($replacedivid, $replacestring); return drupal_json_output($ajax_commands); }
Javascript
(function ($) { Drupal.behaviors.mymodule = { attach: function(context, settings) { var $uilink = $('.midui'); //find all links for (var i=0;i<$uilink.length;i++) { //Loop var $link = $('#' + $uilink[i].id); if (!$link.hasClass("middone")) { new Drupal.ajax('#' + $uilink[i].id, $link, { url: $link.attr('data-url'), effect: 'fade', settings: {}, progress: { type: 'throbber' }, event: 'click tap' }); $link.addClass("middone"); //add class when we're done } } } } })(jQuery);
What I have tried so far:
(a)Add an ajax_command_invoke(NULL, 'mymodule');
to the $ajax_commands array coupled with a $.fn.mymodule
function
(b)Add $('body').ajaxSuccess(Drupal.attachBehaviors);
to my javascript. ajaxComplete tried as well. Tried it on the document as well.
(c)Create a custom command as detailed here http://www.jaypan.com/tutorial/calling-function-after-ajax-event-drupal-7
Note : I know that it's just a matter of triggering attachBehaviors after each click to 'ajaxify' the new html being inserted/modified. If I click the link and then type Drupal.attachBehaviors() in the console, the link will be processed again by my javascript, as evidenced by the addition of the 'middone' class, and can be clicked again.
Note: Also of interest, if I leave the $ajax_commands
empty and return it (empty array) at the end of the callback function, the link will remain clickable on the first and subsequent clicks. It will have the functionality that I am looking for (a toggle). However, since no change is made to the html after each click, there is no way for the user to know if the toggle is on or off.
Any pointers would be greatly appreciated.
=======================================================
A partial Answer :
Drupal ajax.js success function only re-attaches behaviors for forms (I think ?)
if (this.form) {
var settings = this.settings || Drupal.settings;
Drupal.attachBehaviors(this.form, settings);
}
so I decided to hack all my ajax objects' success function.
The Javascript now becomes
(function ($) {
Drupal.behaviors.mymodule = {
attach: function(context, settings) {
var $uilink = $('.midui'); //find all links
for (var i=0;i<$uilink.length;i++) { //Loop
var $link = $('#' + $uilink[i].id);
if (!$link.hasClass("middone")) {
myAjax = new Drupal.ajax('#' + $uilink[i].id, $link, {
url: $link.attr('data-url'),
effect: 'fade',
settings: {},
progress: {
type: 'throbber'
},
event: 'click tap'
});
myAjax.options.success = function (response, status) {
//Trigger Attach Behaviors
setTimeout(function(){Drupal.attachBehaviors($(myAjax.selector))}, 0);
// Sanity check for browser support (object expected).
// When using iFrame uploads, responses must be returned as a string.
if (typeof response == 'string') {
response = $.parseJSON(response);
}
return myAjax.success(response, status);
}
$link.addClass("middone"); //add class when we're done
}
}
}
}
})(jQuery);
The success function is a copy paste of the default from ajax.js with an added line for reattaching behaviors.
For some reason, Drupal.attachBehaviors
has to be within a timer. I can't just have it on its own for a reason that I ignore.
I'll leave this question open for few in case someone can either find a more elegant solution and/or explain the timer oddity.
Many thanks