Viziontech Software Solutions
Software development is delicate.
We handle it carefully.

Drupal & Ajax - Basic Tutorial

The following tutorial will guide you step by step how to create a quick implementation of Ajax in Drupal. We will dynamically update a page with content we will read from the server without the need to refresh the page. I tried to make it as simple as possible, just to describe the very basic blocks required to implement Ajax. I suggest you take it further and try to implement more complex implementations according to your site's needs.

Let's start...... I was building an e-commerce website based on Drupal 6 & Ubercart the other day, and wanted to ajaxify the way products are being displayed. I wanted the users to be able to select a products category from a list, and based on that selection, to fetch the list of products for that category.
Note: It is recommended that you will be familiar to Drupal module development (A tutorial can be found here).

Step 1 – Create your “Data Module”
In order to be able to return data to the client, we will start by creating a module that will return the data. Let’s name our new module “dynamic_products”. First create a folder for our new module. This folder should be created in your site’s “modules” folder (Usually /sites/default/modules folder). Let’s name it as our module name (i.e. “dynamic_products”). All the files described later in this section, should be created under this folder. Then, create the .info file for our module. This file provides Drupal information about your module, which is necessary to enable it.
More info about creating the .info file can be found here.
We’ll use the following for our .info file:
 
; $Id$
name = "Dynamic Products"
description = "Returns a list of products according to category."
core = 6.x
package = Example Ajax Modules
Copy, pase & save the above code into “dynamic_products.info”. After we have our .info file created, we continue to create our .module file. This file includes the actual server side code.Go ahead and create “dynamic_products.module” file in our module’s folder.
In order to be able to make a call to the server, and fetch some data, we need a menu_hook that will be used to “intercept” the ajax call. The following function will used as our menu_hook:
 
<?php
function dynamic_products_menu() {

  $items = array();

  $items['products/get'] = array(
    'title' => 'Dynamic Products',
    'page callback' => 'dynamic_products_get_by_category_id',
    'access arguments' => array('access dynamic_products content'),
    'type' => MENU_CALLBACK
  );

  return $items;
}
This tells Drupal to intercept all calls to “http://www.example.com/?q=products/get” or http://www.example.com/products/get, and call the dynamic_products_get_by_category_id callback function.
We should now let Drupal know, who has the permissions to use your module. For that, we will implement Drupal's hook_perm().
 
function dynamic_products_perm() {
   return array('access dynamic_products content');
}
More info about setting permissions can be found here.

It is now time to create the callback function that will return the data. For the purpose of this tutorial, we will create a simple list of products. In real life, the data can be a view output, or anything else you need.
function dynamic_products_get_by_category_id($cat_id){
  $items = '';
  switch($cat_id){
    case 12: 
     $items = '<ul><li>Product 1</li><li>Product 2</li></ul>';
     break;
    case 20:
     $items = '<ul><li>Product 3</li><li>Product 4</li></ul>';
     break;  
  }
// create a JSON object. The object will contain a property named “products” that will be set with the $items variable. 
  return drupal_json(array('products'=>$items));
  exit;
}
Our callback function, accepts a single parameter ($cat_id). Depending on its value, a different list is returned.
The function can have as many parameters as we need, and the way Drupal passes the parameters to the function, is by using parts of the Url that is used by our ajax call.

As you remember, earlier we defined the menu_hook function (dynamic_products_menu), and instructed our module to intercept requests made to “http://www.example.com/products/get”.
In order to pass the category id to our callback function, we simply add it to the request url.
So for example, if we want to use the value 12 as the category id, we simply use the following url http://www.example.com/products/get/12.

We’re almost done with our dynamic_products.module file. We will return to it later on, so go on and save it for now.

Step 2 – Create the javascript file
We will now create a javascript file that will include the client side that will make the ajax call. Create a new file in the module’s folder and name it dynamic_products.js. The following code defines a behavior to handle the click event of one of the html elements in our page.
// $Id$
Drupal.behaviors.dynamic_products = function (context) {
  $('a.categoryLink:not(.categoryLink-processed)', context).click(function () {
    // This function will get exceuted after the ajax request is completed successfully
    var updateProducts = function(data) {
      // The data parameter is a JSON object. The “products” property is the list of products items that was returned from the server response to the ajax request.
      $('#divProducts').html(data.products);
    }
    $.ajax({
      type: 'POST',
      url: this.href, // Which url should be handle the ajax request. This is the url defined in the <a> html tag
      success: updateProducts, // The js function that will be called upon success request
      dataType: 'json', //define the type of data that is going to get back from the server
      data: 'js=1' //Pass a key/value pair
    });
    return false;  // return false so the navigation stops here and not continue to the page in the link
}).addClass('categoryLink-processed');
}
Copy, paste and save the above code into the dynamic_products.js file. We are done with the js file.
Step 3 – Load the javascript file
In order for our client side to load the .js file, we need to return and edit our .module file. We will need to use init_hook to load the .js file when our module is initialized. In order to do that, add the following functions to the .module file:
 
function dynamic_products_theme() {
   return array(
      'dynamic_products_javascript' => array(
         'arguments' => array(),
      ),
   );   
}

function dynamic_products_init() {
  theme('dynamic_products_javascript');
}

function theme_dynamic_products_javascript() {
  drupal_add_js(drupal_get_path('module', 'dynamic_products') . '/dynamic_products.js');
}
Save the .module file.

Note:  Remember to enable our new module via the "admin/build/modules" page.

Step 4 – Create the Html
We are almost done now. We only need to create the html in our page that will make the ajax call. To make it simple, let’s create 2 divs one on top of the other. The top div, will include 2 links, one for each category of products. When one of these links is clicked, we will make a request to the server, and load the content from the response to the bottom div.
 
<div id=”topDiv”>
<a class="categoryLink" href="/products/get/12">Cat. 1</a>
<a class="categoryLink" href="/products/get/20">Cat. 2</a>
</div>
<div id="divProducts"></div>
Create your Drupal page, copy and paste the above code, and that’s it. When you view the page, if you will click one of the “categories” links, a call will be made to the server, and the page will display a list of products specific to the category clicked.

For a more advanced related tutorials, please see:


Comments


divProducts

Hello, thank you for this tutorial that makes me advance in my work. But I have a problem. I adapted the code to feat my needs. as you can see the text below :

Checking options : 15x21 Numérique [Valider] | 10x15 Numérique [Valider] | Tout valider

They are links appearing in a "" that of the class "divCheckingBar" instead of "divProducts". And the links that must make Ajax call are the one you see ("[Valider]"). After they call Ajax, they replace the "divCheckingBar" with an updated checking options. The problem is there are many other checking options for every products listed in a signle page, but all in a ". If a click on another link, only the first "divCheckingBar" is updated with data that isn't its. How can I make a difference between all those "divCheckingBar" ?

The second problem is that after the first click, all others links of class "StatusLink" the equivalent of "categoryLink" from you example don't make anymore Ajax call. They only refresh the page with the json return.

How can I get out of this issue ?

Thank you much.

Thanks! Great!

Thanks! Great!

thank you for this great

thank you for this great tutorial

thanks

I am really enjoying reading your well written articles. I think you spend numerous effort and time updating your blog. I have bookmarked it and I am taking a look ahead to reading new articles. Please keep up the good articles!

Comparison to xmlrpc.php

Hi, thanks for a great post that got me started with jQuery ajax in no time!

I have used the xmlrpc.php approach, where I implemented the hook_xmlrpc(), so I was thinking it must be a quicker solution since it is in drupal core.
But as it turns out, both xmlrpc.php and index.php call the drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL), where essentially all work happens.

Conclusion: The jQuery ajax solution you have presented here is the simpler one, and equally effiecient! Thanks again!

An huge thank you for this

An huge thank you for this tutorial ,please if you have other tutorial or resources on this subject please post it

For who is receiving:
{ "products": "\x3cul\x3e\x3cli\x3eProduct 3\x3c/li\x3e\x3cli\x3eProduct 4\x3c/li\x3e\x3c/ul\x3e" }

please try to post the content

Cat. 1
Cat. 2

in your page and save the input format as full html .
Thanks for cooperation

Great example that got me

Great example that got me going.

I was new to drupal and php this morning and i did manage to create some portfolio style search thanks to this.

Using jQuery 1.4 and above

Just a heads up to anyone following this tutorial running jQuery Update. Everything seemed to work fine for me except the fact that my div would not get updated. After adding some error checking to the javascript to see what the problem was, it turns out that Drupal 6 generates invalid JSON (try validating it at http://www.jsonlint.com). jQuery 1.4 and above are more strict about this, so we need to fix the json. For this tutorial you would add the following to your .module file (the alternative is hacking core common.inc, which is bad). The new line compared to core drupal_to_js is under the 'string' case. Source post is at http://drupal.org/node/818138#comment-3859108 Here is the code to add/modify from the examples:

Under get_by_category_id function:
REPLACE:
// create a JSON object. The object will contain a property named “products” that will be set with the $items variable.
return drupal_json(array('products'=>$items));

WITH:
// create a JSON object. The object will contain a property named “products” that will be set with the $items variable.
return dynamic_products_json(array('products'=>$items));

ADD TO .module FILE:

function dynamic_products_to_js($var) {
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false'; // Lowercase necessary!
case 'integer':
case 'double':
return $var;
case 'resource':
case 'string':
return str_replace(array("<", ">", "&"), array('\u003c', '\u003e', '\u0026'), json_encode($var));
case 'array':
// Arrays in JSON can't be associative. If the array is empty or if it
// has sequential whole number keys starting with 0, it's not associative
// so we can go ahead and convert it as an array.
if (empty ($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
$output = array();
foreach ($var as $v) {
$output[] = dynamic_products_to_js($v);
}
return '[ '. implode(', ', $output) .' ]';
}
// Otherwise, fall through to convert the array as an object.
case 'object':
$output = array();
foreach ($var as $k => $v) {
$output[] = dynamic_products_to_js(strval($k)) .': '. dynamic_products_to_js($v);
}
return '{ '. implode(', ', $output) .' }';
default:
return 'null';
}
}

function dynamic_products_json($var = NULL) {
// We are returning JavaScript, so tell the browser.
drupal_set_header('Content-Type: text/javascript; charset=utf-8');

if (isset($var)) {
echo dynamic_products_to_js($var);
}
}

Awesome!

This is the first post that help me understand about ajax in Drupal. Thank a ton!
Truyenle

Fade in and out

Thanks so much for the well put together tutorial. So useful. Having some problems, I'm a new comer to Jquery and have been trying to implement a fade in and out effect. How could I do that? Many thanks in advance.

Great Tutorial

Hi

Great tutorial , in this every point is clear, i understand it easily thanks

The information is very much

The information is very much applicable to our current project. Thanks.

Vigilon Security

Thanks

I wanted to write a quick note to express my thanks. I'm really impressed by a blog unique and perfectly chosen and organized!

Thanks fot the information:god bless you

Thanks fot the information.

thanks a million

Great tutorial, will be implementing this on my sites thanks to you.

Small change to fix block admin theme issue

I followed this and the second tutorial to build an ajax_view module. Because I have a separate admin theme the blocks interface no longer gave me access to my theme (see comments above).

My work around:
(my module is ajax_view not dynamic_products)

function ajax_view_init() {
//theme('ajax_view_javascript');
drupal_add_js(drupal_get_path('module', 'ajax_view') . '/ajax_view.js');
/* Calling drupal_add_js directly prevents issues with the hook_init() and theme()
functions which prevents loading an alternate themes block interface. */
}

I do not know if this there are any negative side effects from doing this. I do know that the module functions correctly, a view is loaded dynamically and displays correctly.

My particular view makes heavy use of the theming engine, there are template files which control the page, rows, and fields. Needless to say I was very happy that this change allowed my version of the module to work without interfering with the blocks interface.

Thank you for the excellent tutorial, and site.

Helpful hint

To help anyone who is struggling. Make sure you put the module inside of your drupal root module folder. It is mentioned in the post to do it this way, it might save you a lot of time to know this. I had mine in the sites/all folder which was causing inconsistency's.

Props to the writer and to the helpful commenter's on this post.

Modules dir location

FYI, the tutorial author said (now incorrect) /sites/default/modules, not Drupal root module folder which per other comments I am pretty sure is supposed to be only for core modules. My understanding is that /sites/all/modules is correct at least for later Drupal 6.

Not recommended

I'd have to advice against this, the root /modules directory is for Drupal Core modules only. Community modules and custom made modules should go into either /sites/all/modules, /sites/SITENAME/modules.

Page not found

Anybody who is getting page not found errors, or clicking the link just doesn't do anything, see if you have clean urls turned on. If not, you need to change the links in the html page to use the non-clean format. So href="/products/get/12" becomes href="/q?=products/get/12". Also, since this is plain old html the path is relative to the server root, which might not necessarily be the drupal root.

Took me way too long to figure this out, hope this saves someone else a facepalm moment.

Hi Its simple to

Hi

Its simple to understand and i could get a better idea about creating a module and the ajax implemntation.But whe i clike on the category links this was not doing anything, so i have added error call back in the jQuery ajax method and i could get a long message in alert that requested RL was not found on the server(object not found error), Can anybody help me to reolve ths

With thanks and regards
Nizar

url not found error

Please follow the section that describes how to define a menu item.
After you implement this section correctly, you will need to re-install your module.

Hope this helps,
Zion

Bingo

This article hit the 'bulls eye'

Great
Keep it up

Amazing Post

Hello Sir.,

First of all: Congratulations! Amazing POST, easy to understand and so far the best kick off on how to create your own Drupal Module.

I was wondering in the .js, is there a way to setup the Ajax setInterval function to keep the paging updating while it`s open, intead of the click event handler for ?

How the .js would be?

Thanks in Advanced.

Regards,

Wellington Chaves

403 error with non Firefox browser

Firstly, thanks for this great and well explained tutorial, which helped me a lot. However, I get a weird behavior while trying to run the tutorial code under non Firefox browsers. Indeed, under Firefox I obtain the expected results but with IE, Safari and Chrome I get a 403 error when trying to access the address products/get/.

I guess it may be caused by my web server configuration, do you encounter the same issue on your side? Any idea of how to solve it?

Thanks a lot for your answer

http 403

Dear David,

I've never seen anything similar to what you describe above.
Try to provide more usage info on the differences between FF and the other browsers. Are you using FF to administer the website, so you are logged in to the site as an admin, using FF? and using other browsers to test the site as anonymous user?

http 403

Dear Zion,

You were right, it was effectively a permissions matter, only admin users could manipulate the module content. I added rights to anonymous and authenticated users on this module and everything is working great.

Thanks for your help

Conflict with the Blocks module

Hi,

Thanks for this very useful and well explained post that already helped me a lot! Only small issue I get when having this module running, when I go to the blocks listing page, there seems to have a conflict: the blocks aren't listed with the right theme but with the Garland default one and all custom blocks are now placed in the disabled area...

Otherwise the site still works, but if I want do some changes to the blocks, I must disable my module first... not very efficient!

Have you ever heard of a similar problem?

Thanks you

I also have this problem

I also have this problem

This is a known problem. See:

This is a known problem. See: http://drupal.org/node/219910

Re: Conflict with the Blocks module

Dear David,

I tested the module and I'm not getting the behavior you described.
There is not reason that there would be such a conflict, so I would be very surprised to find otherwise.
Did you try it a clean drupal installation?
Is the Blocks interface working ok when you disable the module you created using this tutorial?

In any case, this tutorial doesn't provide a working module to be used on a drupal site, but tries to help developers understand how to implement Ajax in their modules.

regards,
Zion

Many Thanks

Hi,

was looking for a good tutorial for AJAX and Drupal. After many hours browsing and finally studying your code I managed to get a simple page up and running in one shot.

Now, thanks to yourselves, I understand the basics so I can go on and extend this concept however I so desire. Your code here has been a lifesaver, thanks.

Keith

a big thanks

Thanks for making me understand quickly how to create a module and use ajax/views at the same time !

Among the best tutorials I have seen

I learned more about Drupal in 30 minutes than I have in days of study. Your example is so elegantly simple that I was able to easily modify it for my specific use. I look forward to studying the following tutorial, which is also something I need to implement. Many thanks!

just what i needed thanks! :)

just what i needed
thanks! :)

To whoever getting this

To whoever getting this "...\x3c/a\x3e\x3c/li\x3e\x3cli\x3e\x3ca..."

i think there is a filename typo, the file dynamicproducts.js should be named as dynamic_products.js

This generally happens when

This generally happens when you did not include the JS file properly and you endup getting the json response in a new page

Thanks

for letting me know.

Great Tutorial. Thanks a lot.

Great Tutorial. Thanks a lot. My first Ajax program.

This tutorial has certainly

This tutorial has certainly gave me light about Ajax and Drupal, too bad I'm still confused, I guess I'll just keep on reading more of your articles.

What a great blog!

What a great blog! It's a pity that i can't find your rrs address. If you can offer rrs subscription service, i can track your blog easier!

can't get this to work

I keep getting the response on a new page, i.e. the menu link that calls drupal function e.g. http://example.com/cities/get/22 and the contents like this
{ "cities": "\x3cul\x3e\x3cli\x3e\x3ca href=\"/retail/get/26\" class=\"categoryCity\" title=\"Chicago\"\x3eChicago\x3c/a\x3e\x3c/li\x3e\x3cli\x3e\x3ca href=\"/retail/get/24\" class=\"categoryCity\" title=\"New York\"\x3eNew York\x3c/a\x3e\x3c/li\x3e\x3cli\x3e\x3ca href=\"/retail/get/25\" class=\"categoryCity\" title=\"Dallas\"\x3eDallas\x3c/a\x3e\x3c/li\x3e\x3c/ul\x3e" }

So i am a bit confused, my .js file looks like this.

// $Id$
if (Drupal.jsEnabled) {
$(document).ready(function(content) {
$('a.categoryLink:not(.categoryLink-processed)',content).click(function () {
// This function will get exceuted after the ajax request is completed successfully
// var updateCities =
// alert(this.href);
$.ajax({
type: 'POST',
url: $(this).href,
success: function(data) {$('#cityId').html(data.cities);}, // The js function that will be called upon success request
dataType: 'json', //define the type of data that is going to get back from the server
data: 'js=1' //Pass a key/value pair
});
return false; // return false so the navigation stops here and not continue to the page in the link
}).addClass('.categoryLink-processed');
});
}

exit after posting JSON

Hi Albert,

Looks like you don't have "exit;" right after you post back the JSON object data.
How does the callback function looks like?

getting same error

Hello Boss

Thank you very much for the great tutorial.
I have created the modules and pages as per your tutorial and given the link url as ?q=products/get/12.
but I am getting the error result "{ "products": "\x3cul\x3e\x3cli\x3eProduct 3\x3c/li\x3e\x3cli\x3eProduct 4\x3c/li\x3e\x3c/ul\x3e" }" in a new page.

This is not an error.

It is the json object returned to your browser.
Can you post the php function that returns the json data, and the JS code?

rebuilt the site from scratch

rebuilt the site from scratch a couple of times, and using chrome, i get the json object in the browser.

http://www.50tees.co.uk/drupal/

using firefox, my menu (i had to make a menu-item to make this work - doesnt even turn up!

fantastic. googled in

fantastic. googled in despair and this article came through.
Thank you

Great Tutorial

I would love to see how we could basically do the same but loading a node from within drupal dynamically. I want to dynamically load a node in place of the one i am currently on.

Thanks for the tutorial!

Page Arguments

Great post first off.

I'm wondering why in hook_menu your not implementing 'page arguments' =>array(2), on the MENU_CALLBACK you setup? I see that it works without it, but should that be there following drupal standards?

BTW, hundreds of teachers will be benefiting from this write up you did, it gave me the jumpstart i needed for my module.

Thanks for the comment

Regarding your question about the page arguments, I tried to make this tutorial as simple as possible, and ignored few other Drupal standards I'm aware of.
The tutorial is, as you said, only a a jump-start for other developers, and each should take responsibility for the way they write their code and follow coding standards.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters (without spaces) shown in the image.