Creating a Simple AJAX Interface with Views
I had a client recently who had set up a view with a list of node titles down the left side of the page. He wanted to set up the interface so that when the user clicked on the node titles on the left, the node would display in a panel on the ride side using AJAX. Let's take a look at how to do something like that.
The Set Up
I've got a 'Basic Page' node. This page will serve as our AJAX interface. Its only content is a simple bit of HTML that we'll use to wrap the nodes that get inserted:
<div id='mymodule-ajax-wrapper'><br><p>Click on a title on the left side to view the node!</p><br></div>
I've also got a simple view, displayed as a block, that shows 'Article' node titles.
Working with Views
First, we'll mess around with the view. We'll start by turning AJAX on.
Even if you don't have a pager or anything that will be AJAX-fied on the view itself, turning on 'Use AJAX' in views will include Drupal's AJAX JavaScript on the page. There are other ways to do that, but this way is pretty darn convenient.
Next we'll have to include the node id as a field in our view. We don't want the nid to show to the end user, but we need its data so we can pass it along with the AJAX request. So we add it as a field, and check the box for 'Exclude from display.'
You'll want to rearrange the fields in views so that the nid is above the node title. Even though the nid isn't visible, it needs to come before the title so that we can use it inside the title's settings as you'll see in a moment.
The last thing to do is to adjust the title's display in views's field settings. You'll want to uncheck 'Link this field to the original piece of content.' Instead, open up the 'Rewrite Results' fieldset and check 'Output this field as a link.' Here, we set the link path to be a custom ajax callback path we're going to set up in a moment, 'mymodule-ajax/nojs/[nid]'. There's a couple of things to note in this url. Drupal will automagically switch the 'nojs' piece of the link over to 'ajax' if the user has JavaScript enabled in their browser. We can detect this later on the server side to make sure that our AJAX degrades nicely for folks without JS. The '[nid]' piece will be replaced by the node's nid that we just set up, so that our server side callback knows what node we're asking to insert.
Also note that we've added the class 'use-ajax' to the link. This is important for telling Drupal's ajax javascript that this link should be ajaxified
Save the view and try it out. If everything goes according to plan, you should be able to click a title link within the view block, see the ajax throbber for a moment, and then get a big ugly error message in an alert box. This is an error 404, because we haven't written the code on the server side yet!
Custom Module
We're going to make a very simple custom module that listens for page requests at the path we set for our title links (mymodule-ajax/nojs/[nid]) and returns ajax commands to display the node requested by that nid. I assume you know how to start off a custom module, which I've called mymodule. Make yourself a .info file and an empty .module file, blah blah blah.
We need two functions here. The first will be an implementation of hook_menu() which will register our ajax callback at the url mymodule-ajax/nojs/[nid].
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_menu</span><span style="color: #007700">() {<br> </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #DD0000">'mymodule-ajax/%/%node'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'access arguments' </span><span style="color: #007700">=> array(</span><span style="color: #DD0000">'access content'</span><span style="color: #007700">),<br> </span><span style="color: #DD0000">'page callback' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'mymodule_ajax_callback'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'page arguments' </span><span style="color: #007700">=> array(</span><span style="color: #0000BB">1</span><span style="color: #007700">,</span><span style="color: #0000BB">2</span><span style="color: #007700">),<br> </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=> </span><span style="color: #0000BB">MENU_CALLBACK</span><span style="color: #007700">,<br> );<br> return </span><span style="color: #0000BB">$items</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?></span></span>
The key to the menu items array is the path that will be answered by our ajax callback: 'mymodule-ajax/%/%node'. The first percent sign is the first variable in the path. This will either be nojs or ajax, depending on whether the user has JS or not. The second variable, %node, will be a node id that will automatically be passed to node_load() and given to our callback as a full $node object. I don't want to get too far into the details of hook_menu() here. There's great documentation on api.d.o and I've written about it in an earlier post. After we've got this hook_menu() all squared away its time to write our simple ajax callback, mymodule_ajax_callback().
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_ajax_callback</span><span style="color: #007700">(</span><span style="color: #0000BB">$js</span><span style="color: #007700">, </span><span style="color: #0000BB">$node</span><span style="color: #007700">) {<br> </span><span style="color: #FF8000">// If the nojs didn't get changed to ajax, the user has no javascript.<br> // Send them to the regular node page instead.<br> </span><span style="color: #007700">if (</span><span style="color: #0000BB">$js </span><span style="color: #007700">== </span><span style="color: #DD0000">'nojs'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">drupal_goto</span><span style="color: #007700">(</span><span style="color: #DD0000">'node/' </span><span style="color: #007700">. </span><span style="color: #0000BB">$node</span><span style="color: #007700">-></span><span style="color: #0000BB">nid</span><span style="color: #007700">);<br> }<br><br> </span><span style="color: #FF8000">// Get the render array for the node.<br> </span><span style="color: #0000BB">$build </span><span style="color: #007700">= </span><span style="color: #0000BB">node_view</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">);<br><br> </span><span style="color: #FF8000">// Render the node into html.<br> </span><span style="color: #0000BB">$html </span><span style="color: #007700">= </span><span style="color: #0000BB">render</span><span style="color: #007700">(</span><span style="color: #0000BB">$build</span><span style="color: #007700">);<br><br> </span><span style="color: #FF8000">// Prepare an ajax command to insert the node html into our ajax wrapper.<br> </span><span style="color: #0000BB">$commands </span><span style="color: #007700">= array();<br> </span><span style="color: #0000BB">$commands</span><span style="color: #007700">[] = </span><span style="color: #0000BB">ajax_command_html</span><span style="color: #007700">(</span><span style="color: #DD0000">'#mymodule-ajax-wrapper'</span><span style="color: #007700">, </span><span style="color: #0000BB">$html</span><span style="color: #007700">);<br><br> </span><span style="color: #FF8000">// Render the commands into JSON and print them.<br> </span><span style="color: #007700">print </span><span style="color: #0000BB">ajax_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$commands</span><span style="color: #007700">);<br> exit; </span><span style="color: #FF8000">// Exit so Drupal doesn't have a chance to build a whole page.<br></span><span style="color: #007700">}<br></span><span style="color: #0000BB">?></span></span>
It only takes a few lines of PHP to make this AJAX interface work. First off, we check to see if the 'nojs' piece of the request url is still 'nojs'. If it is, it means that the user doesn't have JS. For accessibility's sake, we forward this user on to the normal node page to view it there. Then, for users that do have JS, we convert the node into html using node_view() and drupal_render(). Lastly, we use Drupal's built-in AJAX commands to insert our node's html within the '#mymodule-ajax-wrapper' div we set up at the start. The ajax_command_html() command simply invokes jQuery's .html() method for inserting content within the selected wrapper. When we've finished constructing the array of ajax commands, we simply render them into JSON using ajax_render() and print them on the page.
That's it! Enable the module / clear the cache and give it a whirl. Drupal's AJAX API is amazing - you don't need to write any JavaScript, only PHP!