Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

How to create a custom Drupal AJAX module

Parent Feed: 

Ajax is becoming a standard, oft asked for feature in our Drupal sites. Clients like it because it makes their sites look 'slick'. We don't like it because it causes a few PITA's especially when it came time for us to integrate with Drupal's FAPI (Form API). Not so anymore. After needing to dig in and make a couple custom AJAX modules for a clients site, we got pretty familiar with the ins and outs of what is appropriately called AHAH. Now we're sharing it with you.

In this tutorial we'll show you a simple example of the anatomy of an AJAX module. Hopefully it will help you get started on your own AJAX fueled custom module(s). We'll assume you're somewhat comfortable with getting your hands dirty with a Drupal site

What is AHAH?
AHAH (Asynchronous HTML And HTTP) let you update web pages dynamically without refreshing the whole page. The advantage of it is that it decreases wait time for the user and requires less server resources.

You can download the example module used in this tutorial. To try it out, un-zip it to your /sites/all/modules folder and enable the module. Then go to Site Building -> Blocks and place the "AHAH Example" to the desired region of your site.

Step 1 - Create your .info file

name = AHAH Example
description = A module for demonstrating how to implement AHAH in Drupal.
version = "6.x-1.0"
core = "6.x"
project = "ahah_example"

Step 2 - Create your .module file
i.e. ahah_example.module

Step 3 - Define your menu callback function

<?php
/**
 * Implementation of hook_menu().
 */
function ahah_example_menu() {
	$items = array();
	$items['ahah_example/add'] = array(
		'page callback' => 'ahah_example_add',
		'access arguments' => array('access content'),
		'type' => MENU_CALLBACK,
	);
	return $items;
}
?>

In the above code we're doing two things. The first is to tell Drupal what URL is going to handle the AHAH request. In the example it's the $item arrays key (ahah_example/add). The second is to tell Drupal what function within our module is going to process the incoming request. In this case it's ahah_example_add.

Step 4 - Create a block

<?php
/**
 * Implementation of hook_block().
 */
function ahah_example_block($op = 'list', $delta = 0, $edit = array()) {
	global $user;
 
	switch ($op) {
		case 'list':
			$blocks[0] = array(
				'info' => t('AHAH Example'),
			);
			return $blocks;
		case 'view': default:
			switch ($delta) {
				case 0:
					$block['subject'] = t('AHAH Example - My Recent Pages');
					$block['content'] = drupal_get_form('ahah_example_form');
					break;
			}
			return $block;
	}
}
?>

Step 5 - Create your form
This is where you use Drupal's form API to define which form elements you'd like on your page.

<?php
/**
 * Implementation of hook_form().
 * A form to allow the user to create a new page node and show the 5 most recent pages created by the currently logged in user.
 */
function ahah_example_form($form_state) {
	global $user;
 
	// create a DIV to show the output of the AHAH request
	$form['new_row_wrapper'] = array(
		'#type' => 'markup',
		'#value' => '<br><div id="ahah-example-new-row-wrapper" style="clear: both;"></div>',
	);
 
	// show links to the latest 5 page nodes
	$sql = "SELECT n.nid, n.title
			FROM {node} AS n
			WHERE n.uid = '%d'
				AND n.type = 'page'
			ORDER BY n.nid DESC
			LIMIT 5";
	$db_result = db_query($sql, $user->uid);
	while ($row = db_fetch_object($db_result)) {
		$form['node_'.$row->nid] = array(
			'#type' => 'markup',
			'#value' => '<div><a href="'.url('node/'.$row->nid).'">Node '.$row->nid.' - '.$row->title.'</a></div>',
		);
	}	
 
	// create a form to allow the user to enter the new node's title and body
	$form['new_title'] = array(
		'#type' => 'textfield',
		'#title' => "Title",
		'#size' => 40,
		'#required' => TRUE,
	);
	$form['new_body'] = array(
		'#type' => 'textarea',
		'#title' => "Body",
		'#required' => TRUE,
	);
	$form['submit'] = array(
		'#type' => 'submit',
		'#value' => t('Add Node'),
		'#submit' => array(),
		'#ahah' => array(
			'path' => 'ahah_example/add',
			'wrapper' => 'ahah-example-new-row-wrapper',
			'method' => 'prepend',
			'effect' => 'fade',
			'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
		),
	);
	return $form;
}
?>

The most important part of the above code is:

<?php
		'#ahah' => array(
			'path' => 'ahah_example/add',
			'wrapper' => 'ahah-example-new-row-wrapper',
			'method' => 'prepend',
			'effect' => 'fade',
			'progress' => array('type' => 'bar', 'message' => t('Please wait...')),
		),
?>

The path tells Drupal where to send this form post. In this case it's the menu item we setup in Step 1 of this tutorial. When the user clicks "Add Node" the form values will be passed to our menu item, then onto the function we specified (ahah_example_add).

Wrapper is used to define the HTML element that we want the returned AJAX data to show up in. In our example it points to a <div> with an id of "ahah-example-new-row-wrapper".

Method defines the behavior of the returned HTML. In our case we chose 'prepend' which will add the data at the beginning of the <div>'s contents. Other possible values include: 'replace' (default), 'after', 'append', 'before', 'prepend'.

Effect defines the jquery effect the new data will be brought into view with. Possible values: 'none' (default), 'fade', 'slide'.

The progress option let's you choose from one of two default Drupal progress indicators. The typical throbber or if you're expecting your AJAX call to take a bit longer (e.g. file uploads) you can choose a progress bar instead.

Step 6 - Create your function to handle the AHAH request
Depending on what values you're expecting from the form post, you'll need to change the error checking logic and the logic that handles the submitted data, but if you're just following along these should sufficiently cover the basics.

<?php
function ahah_example_add() {
	global $user;
 
	$output = '';
 
	//error checking
	if (!$user->uid) {
		$output .= '<div class="messages error ahah-example-msg">Access denied.</div>';
	}
	if ($_REQUEST['new_title'] == '') {
		$output .= '<div class="messages error ahah-example-msg">Please enter a title</div>';
	}
	if ($_REQUEST['new_body'] == '') {
		$output .= '<div class="messages error ahah-example-msg">Body field is required.</div>';
	}
 
	//no error, save node
	if (!$output) {
		$node = new StdClass();
		$node->type = 'page';
		$node->status = 1;
		$node->uid = $user->uid;
		$node->title = $_REQUEST['new_title'];
		$node->body = $_REQUEST['new_body'];
		node_save($node);
		if ($node->nid) {
			$output = '
			<div class="messages status ahah-example-msg">Successfully saved your page.</div>
			<script type="text/javascript">
			$("#edit-new-title").val("");
			$("#edit-new-body").val("");
			</script>
			<div><a href="'.url('node/'.$node->nid).'">Node '.$node->nid.' - '.$node->title.'</a></div>';
		} else {
			$output = '<div class="messages error ahah-example-msg">An error occurred, we cannot add your page at this time.</div>';
		}
	}
 
	//remove status message after 10 seconds
	$output .= '
		<script type="text/javascript">
		$("#edit-new-title").val("");
		$("#edit-new-body").val("");
		setTimeout("$(\'.ahah-example-msg\').remove();", 10000);
		</script>';
 
	//send output back to browser
	drupal_json(array('status' => TRUE, 'data' => $output));
}
?>
Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web