I've been playing with the new ajax framework and have successfully used it for the admin form on the D7 version of Quicktabs - awesome stuff, the ajax callback is so simple! :-)
Anyway, I was also testing the use of the 'use-ajax' class to do just a regular ajax request when clicking on a link. My menu callback calls ajax_deliver() as long as ajax is passed in and not nojs. I can get this to work only by hacking ajax.js to tell it what element.wrapper and element.click should be, otherwise it causes a js error as these properties are missing and I can't figure out how to set them from the php side. I, perhaps naively, thought that something like this should work:

  $build['ajaxy_link'] = array(
    '#type' => 'link',
    '#title' => t('Click here'),
    '#href' => 'ajaxy/nojs/',
    '#attributes' => array('class' => array('use-ajax')),
    '#ajax' => array(
      'event' => 'click',
      'wrapper' => 'content',
    ),
  );

Anyway, this does not load ajax.js on the page and does not communicate the wrapper and event properties as required. It would be great if someone who understands how this should work could either add the explanation to the existing docs or give a brief instruction in this thread and I'll write it up in the docs.

Files: 
CommentFileSizeAuthor
#17 drupal.ajax_documentation_610068_17.patch8.67 KBkatbailey
PASSED: [[SimpleTest]]: [MySQL] 17,384 pass(es). View
#14 ajax_updated_ajax_inc.zip4.93 KBrfay
#13 drupal.ajax_documentation_610068_13.patch7.35 KBrfay
PASSED: [[SimpleTest]]: [MySQL] 17,385 pass(es). View
#7 ajax_links.patch26.83 KBkatbailey
Unable to apply patch ajax_links.patch View
#6 ajaxy.tar_.gz821 byteskatbailey
#6 d7_ajax_links.patch26.84 KBkatbailey
Unable to apply patch d7_ajax_links.patch View

Comments

Scott Reynolds’s picture

subscribing

rfay’s picture

Excellent - I plan to do full Ajax docs for the new system and will try to cover this. As of now, I haven't explored it.

rfay’s picture

I'm not to no-js yet, but

1. Updated the 6-to-7 documentation at http://drupal.org/update/modules/6/7#ahah-now-ajax
2. Committed the D7 version of the Ajax Example to the Examples Module

I would appreciate review of those two, plus contributions of more sophisticated AJAX examples. There's a lot more stuff in here!

The coolest thing in the world is how simple the callbacks have become. One line for each of my examples. I guess we could have figured out how to do away with the callback entirely if you wanted to replace the entire form.

rfay’s picture

Title: ajax.inc PHPDoc Improvements » Ajax Framework Documentation To-Do list

May I hijack this issue for a general To-Do list for Ajax Documentation? There are several different issues that need to be addressed. I will maintain this comment as time goes on.

Other items that should be covered? Chime in!

effulgentsia’s picture

Title: Ajax framework documentation only covers ajax forms » Ajax Framework Documentation To-Do list

For now, subscribing. When I'm able to, I'd also like to help.

katbailey’s picture

FileSize
26.84 KB
Unable to apply patch d7_ajax_links.patch View
821 bytes

Just want to add a bit of clarification regarding my initial question above. Rfay and I were discussing this on irc yesterday and the more I look at the code the more I realise that what I was trying to do above is definitely not possible, currently. The only time ajax.js gets automatically added is when ajax_process_form is called. So it seems it really is only intended for forms.
Nobody seems to know how the use-ajax class is supposed to be used currently (or if it can be) so I've been hacking away seeing what it would take to get it to work the way I initially assumed it did. The attached patch allows you to set up ajax functionality on a link (independent of any form) when the link is rendered as follows:

  $build['ajaxy_link'] = array(
    '#type' => 'link',
    '#title' => t('Click here'),
    '#href' => 'ajaxy/nojs/',
    '#options' => array('attributes' => array('class' => array('use-ajax'), 'id' => 'myId')),
    '#ajax' => array(
      'event' => 'click',
      'wrapper' => 'block-system-main',
    ),
    '#id' => 'myId',
  );
  $output = drupal_render($build);

I should have pointed out above that rendering a link this way is only possible with this patch: #602522: Links in renderable arrays and forms (e.g. "Operations") are not alterable
So, I'm attaching a patch which builds on this and, just as a proof of concept, adds an ajax_process_link() function - the first hunk in the patch - which replicates the main functionality in ajax_process_form. I assumed I could add this to the type definiton of the link element in system.module like this:

  $types['link'] = array(
    '#process' => array('ajax_process_link'),
    '#pre_render' => array('drupal_pre_render_link', 'drupal_pre_render_markup'),
  );

... but that didn't work so instead I'm calling it from within drupal_pre_render_link.

Anyway, I'd love if someone could look at this and comment, even if it's just to say that these are the ravings of a crazy person ;-)
The attached module should help test it - just enable the one block in a sidebar. This assumes your page has an element with id of 'block-system-main'.

thanks!

K

Edit: I know this is no time for proofs of concept. It's just that there does seem to be a gap in the ajax framework and I'm wondering if this is the way to fill it...

katbailey’s picture

FileSize
26.83 KB
Unable to apply patch ajax_links.patch View

Attaching an updated patch as there was a problem with the href in the last one...

katbailey’s picture

OK, finally got this one figured out, after chatting with merlinofchaos in irc - I had misunderstood how this was supposed to work and now it all makes sense. A tiny patch was required though and you'll find it here: #615504: Ajax framework's use-ajax class doesn't work.
And here's an example of how you'd use the use-ajax class:

// function that outputs the content with the ajax link
function my_content_with_link() {
  drupal_add_js('misc/ajax.js');
  $output = l('Click here', 'ajaxy/nojs/', array('attributes' => array('class' => array('use-ajax'))));
  $output .= '<div id="myDiv"></div>';
  return $output;
}

// menu callback for the 'ajaxy' path
function ajaxy_ajax_response($type = 'ajax') {
  $output = '<div>Hello</div>';
  if ($type == 'ajax') {
    $commands = array();
    $commands[] = ajax_command_append('#myDiv', $output);
    $page = array('#type' => 'ajax_commands', '#ajax_commands' => $commands);
    ajax_deliver($page);
  }
  else {
    return $output;
  }
}
rfay’s picture

I confirm that this approach works. Thanks so much for the detective work!

Roi Danton’s picture

Thanks for mentioning this issue in IRC (was afk therefore I couldn't reply there). The examples (also the ones in examples module) are helpful when working with ctools wizard ajax render callback (D6), too. There in the ajax-responder.js the click event is attached to links.

te-brian’s picture

I was playing around with the AJAX framework this morning, and came across a relatively new use case: adding new custom commands.

I wanted to add some commands for working with a jQuery UI Dialog. An example of my approach, which is working but not fully tested, follows:

mymodule.module

/**
 * Return an ajax command array to set the dialog options.
 *
 * @param $options
 * An array of jQuery UI dialog options, keyed by option name.
 */
function mymodule_ajax_command_dialog_options($selector, $options, $settings = NULL) {
  return array(
    'command' => 'mymodule_dialog_options',
    'options' => $options,
    'settings' => $settings,
  );
}

mymodule.js

Drupal.behaviors.mymodule = {
  attach: function(context, settings) {    
    // Add custom commands to Drupal.ajax objects.
    for (var i in Drupal.ajax) {
      Drupal.mymodule.addCommands(Drupal.ajax[i]);
    }
  }
};

// Add custom commands to a Drupal.ajax object.
Drupal.mymodule.addCommands = function (ajax) {
  //Add options dialog command to ajax object.
  ajax.commands.mymodule_dialog_options = function (ajax, response, status) {
    options = response.options;
    if (options) {
      for (var option in options) {
        $(response.selector).dialog('option', option, options[option]);
      }
    }
  }
}

The result is that I can add the 'mymodule_dialog_options' command in my ajax callbacks and my custom command function runs on the client side.

So here is an actual question :)

Is there a better way to add these commands to the Drupal.ajax object(s)? The way I got it working, is adding the command to each Drupal.ajax object that exists one-by-one. The better approach, which I could not get working, would be to add it to all Drupal.ajax objects in one swoop. This is where my javascript mastery falls a bit short. I initialy tried to do this:

Drupal.ajax.commands.prototype.mymodule_dialog_options = function() { ...

But I guess the Drupal.ajax object is not extendable in this way?

rfay’s picture

Committed update to forms_api_reference.html updating to current AJAX situation.

http://drupal.org/cvs?commit=294182

rfay’s picture

Status: Active » Needs review
FileSize
7.35 KB
PASSED: [[SimpleTest]]: [MySQL] 17,385 pass(es). View

This documentation-only patch adds significant information to the AJAX overview in ajax.inc.

New topics covered or with improved coverage include:

  • A simple example
  • How status messages are handled.

Plus lots of little cleanup.

rfay’s picture

FileSize
4.93 KB

It's nearly impossible to review a doc change as a patch, so attached is the HTML rendered by the API module. It doesn't show what's changed, but really, everything is open for improvement.

rfay’s picture

katbailey’s picture

This is a great enhancement to the ajax docs - it's very helpful to have example code in there.
There's still one part of the docs that I have a problem with:

 * Each command is an object. $object->command is the type of command and will
 * be used to find the method (it will correlate directly to a method in
 * the Drupal.ajax[command] space). The object may contain any other data that
 * the command needs to process.

Maybe I'm missing something, but on the php side, each command is just an associative array and only becomes an object on the js side. So, would something like the following make more sense:

 * Each command item is an associative array which will be converted to a command
 * object on the JavaScript side. $command_item['command'] is the type of
 * command, e.g. 'alert' or 'replace', and will correspond to a method in the
 * Drupal.ajax[command] space). The command array may contain any other
 * data that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
katbailey’s picture

FileSize
8.67 KB
PASSED: [[SimpleTest]]: [MySQL] 17,384 pass(es). View

Attached patch makes the change in #16.

rfay’s picture

Title: Ajax Framework Documentation To-Do list » ajax.inc PHPDoc Improvements
Status: Needs review » Reviewed & tested by the community

Good by me. Marking RTBC - thanks for the review and improvement.

The bot approved #17 despite the lack of marking on it: See http://qa.drupal.org/pifr/test/26734

For other reviewers, #17 is still exactly the same as #13 (which you can read as HTML in the attachment to #14) - except for the following change.

--- includes/ajax.inc
+++ includes/ajax.inc
@@ -152,10 +152,11 @@
  * be converted to a JSON object and returned to the client, which will then
  * iterate over the array and process it like a macro language.
  *
- * Each command is an object. $object->command is the type of command and will
- * be used to find the method (it will correlate directly to a method in
- * the Drupal.ajax[command] space). The object may contain any other data that
- * the command needs to process.
+ * Each command item is an associative array which will be converted to a command
+ * object on the JavaScript side. $command_item['command'] is the type of
+ * command, e.g. 'alert' or 'replace', and will correspond to a method in the
+ * Drupal.ajax[command] space. The command array may contain any other data
+ * that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
  *
  * Commands are usually created with a couple of helper functions, so they
  * look like this:
Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

rfay’s picture

Status: Fixed » Active

Remaining items to do in #4

rfay’s picture

Title: Ajax Framework Documentation To-Do list » AJAX Documentation items to be completed

I think that no-js and use-ajax are the last remaining items to be documented in this issue

rfay’s picture

Title: AJAX Documentation items to be completed » Document AJAX no-js and use-ajax
Assigned: Unassigned » rfay

I'll put this on my list for docs day.

@katbailey, could you write a quick description here of what you think should be said? 'twould be much appreciated.

rfay’s picture

Status: Active » Fixed

I'm going to say that http://api.drupal.org/api/group/ajax/7 + the AJAX link example are good enough.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

rfay’s picture

Status: Closed (fixed) » Needs work

Hmm... I don't find anything about class="no-js" *anywhere* in D7 code or documentation. Did I miss something?

ñull’s picture

// menu callback for the 'ajaxy' path
function ajaxy_ajax_response($type = 'ajax') {
  $output=strftime('%c');
  if ($type == 'ajax') {
    $commands = array();
    $commands[] = ajax_command_append('#myDiv', $output);
    $page = array('#type' => 'ajax_commands', '#ajax_commands' => $commands);
    ajax_deliver($page);
  }
  else {
    return $output;
  }

This code as proposed by katbaily is unsuitable for dynamic content. I always get the same date/time here (see changed ajax response code above). No effect when I clear the browser cache in between. Only reloading the page where this link is helps. How do I "uncache" it? Would be good to include that too in the examples and documentation.

ñull’s picture

Forget my post. For some obscure reason now it works.

nod_’s picture

Version: 7.x-dev » 8.x-dev
LewisNyman’s picture

Issue summary: View changes
Issue tags: +JavaScript, +frontend

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

  • Dries committed 0fa98a7 on 8.3.x
    - Patch #610068 by katbailey, rfay: improved phpDoc.
    
    

  • Dries committed 0fa98a7 on 8.3.x
    - Patch #610068 by katbailey, rfay: improved phpDoc.
    
    

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

  • Dries committed 0fa98a7 on 8.4.x
    - Patch #610068 by katbailey, rfay: improved phpDoc.
    
    

  • Dries committed 0fa98a7 on 8.4.x
    - Patch #610068 by katbailey, rfay: improved phpDoc.
    
    
andypost’s picture

Version: 8.2.x-dev » 8.4.x-dev
Assigned: rfay » Unassigned