Drupal Tutorials

Drupal and Ajax working together

Drupal is a fantastic CMS which I have been actively developing for, for some time now. Yesterday was the first time I had really ventured into the Ajax territory but I learnt a lot and hopefully this guide should help others.

Currently this example is Drupal 5.x only. I will update it for Drupal 6 in another post in due course.

I'm going to create a simple module to demonstrate the communication between client and server. There is already a module out there that does more or less exactly what this demo will do so I'd advise using that if you require the same functionality.

The demo module will be a simple username checker for new registrants. We will add a button to the registration form which will query the server for an existing user of that name. If it already exists we put up a warning otherwise we display a confirmation that they can safely use that name.

So, to begin, we will need the .info file which looks something like this:
; $Id$
name = Jquery Example
core = "5.x"
description = Quick example JQuery module. Adds a name checker to the register page.
version = "5.x-1.00"

Okay so now we want to create the .module file. We need 3 things: we need to hook into the registration page, a menu callback for our jquery and a function for it to call.

We'll start with the menu system.
<?php
/**
* Implementation of hook_menu()
*/
function jquery_example_menu($may_cache) {
   
$items = array();

    if(
$may_cache) {
       
$items[] = array(
           
'path' => 'usercheck',
           
'callback' => 'jquery_example_usercheck',
           
'access' => 1,
           
'type' => MENU_CALLBACK,
        );
    }

    return
$items;
}
?>

This should be fairly straightforward if you're familiar with writing modules.

Next stop, the callback function we just defined.
<?php
function jquery_example_usercheck() {
   
$name = check_plain($_POST['theName']);
   
   
// Query the database.
   
$result = db_num_rows(db_query("SELECT uid FROM {users} WHERE name = '%s'", $name));
   
   
$reply = array();
   
   
// different replies based on result.
   
if($result > 0) {
       
// user exists.
       
$reply['exists'] = true;
       
$reply['replyString'] = t('Sorry, username %theName has already been taken', array('%theName' => $name));
    } else {
       
$reply['exists'] = false;
       
$reply['replyString'] = t('Username %theName is available.', array('%theName' => $name));
    }
   
    print
drupal_to_js($reply);
    exit();
}
?>

So what have we done here? We grab the username from the POST array and make sure it's safe to use with our db query by using check_plain(). Then we do a very simple query to see if the name exists and return the number of rows of that name. We should never get more than one but a simple check to see if it is greater than zero will suffice.

The last call before the exit() is the one we are really interested in. This handy method will convert the array we sent it into a JSON formatted string. This is very important as we will need to undo this action on the client's machine to reconstitute the data. For more information on JSON and why we use it see here

If you're happy (and you know it) that you understand everything so far, the next step will be to hook into the registration form.
<?php
/**
* Implementation of hook_form_alter()
*/
function jquery_example_form_alter($form_id, &$form) {
   
drupal_add_js(drupal_get_path('module', 'jquery_example'). '/jquery_example.js');
     
drupal_add_js(array(
       
'username_check' => array(
       
'ajaxUrl' => url('usercheck', null, null, true),
         
'msgWait' => t('Checking username availability ...')
        ),
      ),
'setting');
 
    if(
$form_id == 'user_register') {
       
// we want to alter the form to add button and a placeholder for our reply
       
$form['checkUser'] = array(
           
'#type' => 'button',
           
'#value' => t('Check availability'),
          );
         
$form['replyMessage'] = array(
           
'#type' => 'markup',
           
'#prefix' => '<div id="reply-message">',
           
'#value' => t("Feel free to check your username before proceeding"),
           
'#suffix' => '</div>'
       
);
    }
}
?>

At the top if this function we are adding our as-yet non-existent javascript file plus a couple of settings. These settings have been lifted almost directly from the username_check module

There's not much else to this other than a quick mention that we are passing $form by reference so any changes we make to $form will be passed back to the originator.

We can now move on to the exciting bit; the javascript. JQuery is built in to the core of Drupal but I can recommend getting hold of the jquery_update module to make sure you have a more recent version. In Drupal 6 it is up to date at the time of writing.

Create a new file called jquery_example.js and add this code:
if (Drupal.jsEnabled) {
  $(document).ready(function() {
    $("#edit-checkUser").bind("click", function() {
      var username = $("#edit-name").val();
     
      $.post(Drupal.settings.jquery_example.ajaxUrl,
        {theName: username},
         function (data) {
           var result = Drupal.parseJson(data);
          
           var message = $("#replyMessage");
           message.html(result['replyString']);
          
           message.show();
         });
      return false;
    });

    $("#replyMessage").hide();
  });
}

Lets go through this. Firstly we find out if javascript is enabled for the site with the line:
if (Drupal.jsEnabled) {
Then we ask JQuery to perform our function when the document is loaded and ready.
$(document).ready(function() {
$("#edit-checkUser").bind("click", function() {

The second line of this will tell JQuery to find the item with ID of 'edit-checkUser' (remember that Drupal adds 'edit-' to form elements) and then bind the click event to a function declared straight after.
var username = $("#edit-name").val();

$.post(Drupal.settings.jquery_example.ajaxUrl,
        {theName: username},
         function (data) {
           var result = Drupal.parseJson(data);
          
           var message = $("#replyMessage");
           message.html(result['replyString']);
          
           message.show();
         });
      return false;

So what are we doing? We grab the name that has been typed into the username textfield 'edit-name' by using the JQuery val() function.
We now do a call to the server with $.post(). On the JQuery website www.jquery.com the signature of this function is:
jQuery.post( url, [data], [callback], [type] )
Items in [] brackets are optional. If you look at this and apply it to our call you will see that we are reading the url from the Drupal.settings object. After this we send some data as an object {theName: username} as key-value pairs and then our callback will be the next function declaration.
function (data) {
           var result = Drupal.parseJson(data);
          
           var message = $("#replyMessage");
           message.html(result['replyString']);
          
           message.show();
         }

Very simply this will parse the reply using Drupal's JSON parser helper and we can do what we want once we have the information back. In this case we find the message div we set up in the form and was previously hidden with $("#replyMessage").hide(); declared further down the page just inside the $(document).ready() function. Once we have the message box we set the value and tell it to unhide itself with .show();

So there you have it. A simple ajax call to the server. Perhaps as an exercise you might like to see the effects of this javascript code:
if (Drupal.jsEnabled) {
  $(document).ready(function() {
    $("#edit-checkUser").bind("click", function() {
      var username = $("#edit-name").val();
     
      $.post(Drupal.settings.jquery_example.ajaxUrl,
        {theName: username},
         function (data) {
           var result = Drupal.parseJson(data);
          
           var message = $("#replyMessage");
           message.html(result['replyString']);
           message.removeClass('username-message-progress');
           if(result.exists === true){
              message.removeClass('username-accepted');
              message.addClass('username-rejected');
           }
           else{
              message.removeClass('username-rejected');
              message.addClass('username-accepted');
           }
           message.show();
         });
      return false;
    });
   
    $("#replyMessage").ajaxStart(function(){
      $(this).html(Drupal.settings.jquery_example.msgWait);
      $(this).removeClass('username-accepted');
      $(this).removeClass('username-rejected');
      $(this).addClass('username-message-progress');
      $(this).show();
    });

    $("#replyMessage").hide();
  });
}

Note the new classes being added. You'll need to add these to your css file for your theme. Also the .ajaxStart() function is useful for doing things once a call to the server is being made. Disabling buttons and altering messages is a good way to inform the user that something is going on.

That's all for now. Any questions please leave a comment or contact me from the contact page.

Commenting on this Tutorial is closed.

Categories:

Comments (6)

Many thanks!
A few corrections:
1)
var message = $(”#reply-message”);
instead of:
var message = $(”#replyMessage”);
2)
$.post(Drupal.settings.usercheck.ajaxUrl
instead of
$.post(Drupal.settings.jquery_example.ajaxUrl

The Adhere Creative's picture

Aaah yes, well noticed. I took this from a live project and tried to make it generic and obviously missed things. So if anyone chooses to use this code, please apply the above changes. Thanks dfediuk

Don’t stop writing, you’ve given me lots of good info!

Kennaday,
SEO