Some months ago, I discussed multiple values in CCK using AJAX. A few people showed interest, so here's Part 1 of how AJAX can go together with Drupal.
We'll start with a common use case: checking that a username is available when a new user signs up. After you're done with this tutorial, you should be able to give the new user near-instant feedback on whether or not their username is available. I like to start at my desired result and work backwards, so that's what we'll do.
Let's create a module called demo_user.module and begin!
Step 1: Alter the user registration form to include a result field
The desired result here is to show the user whether or not their desired username is available, so let's alter the registration form to give us a div to place our results in.
/**
* Implemetation of hook_form_alter()
*/
function demo_user_form_alter($form_id, &$form) {
if ($form_id == 'user_register') {
$username_verify_result = '<div class="reg_form_username" id="username_result"></div>';
$form['account']['name']['#suffix'] = $username_verify_result;
}
}Step 2: Write a menu hook
The next thing we want to do is put together the logic that actually accomplishes our task. In this case, our task is to see if a given username is already taken. First, write a menu hook like so:
/**
* Implemetation of hook_menu()
*/
function demo_user_menu($may_cache){
$items = array();
if (!$may_cache) {
$items[] = array(
'path' => 'user/usernamecheck',
'title' => 'username check',
'callback' => 'demo_user_username_check',
'callback arguments' => array($_GET['username']),
'access' => true,
'type' => MENU_CALLBACK,
);
}
}This callback will take the username we pass in with GET (more on that later) and shoot it over to a function called demo_user_username_check(). (Note: don't forget to change the 'access' key to something appropriate for you!)
Step 3: Write some logic to compare the requested user name to what's already in the database.
We can check to see if the requested username is already taken with a simple sql query:
SELECT name FROM users WHERE name = $requested_name
(Note: if your users table is case-sensitive (this is rare), you might want to change the query to a LIKE)
This function takes the result of the check, and generates the appropriate user-facing response. I've used two graphics here, cross.gif for a failure and check.gif for success:
/**
* ajax user name check.
*
* Checks for the availability of a given username.
* @author Domenic Santangelo
* @param string $username The desired username
* @return string HTML-ified result of the check.
*/
function demo_user_username_check($username) {
$result = db_result(db_query("SELECT name FROM {users} WHERE name = '%s'", $username));
if($result) {
$icon = "cross.gif";
$msg = "It looks like this username is already on record. Do you want to <a href='/user/password'>retrieve your password?</a>";
}
else {
$icon = "check.gif";
$msg = "Thanks!";
}
echo "<img src='/".path_to_theme()."/images/{$icon}' />{$msg}";
}STEP 4: Wire the callback into JavaScript
Now let's write a JavaScript function that will handle passing the requested username to our callback. This is how we can get the asynchronous request to work. I like to have a little spinner graphic to show that the check is processing, so I stuck a graphic called spinner.gif in /sites/all/themes/mysite/images/. Now, using the magic of JQuery:
function usernameCheck(obj) {
//Spinner shows while the ajax call does its thing
$('#username_result').html('<img src="/sites/all/themes/mysite/images/spinner.gif" />Validating your username...');
$('#username_result').slideDown();
//Callback function that inserts the result of the username check in the div we set up in Step 1
function domCallback(msg) {
$('#username_result').html(msg);
$('#username_result').slideDown();
}
//Actually perform the call
$.ajax({
type: "GET",
url: '/user/usernamecheck',
data: "username="+obj.value,
success: function(msg){
domCallback(msg);
}
});
}STEP 5: Wire the JavaScript function into the Username field on the registration page
Let's go back to our hook_form_alter() implementation and let the form field know that we should fire the username check when the user enters a name and tabs/clicks out of the field
/**
* Implemetation of hook_form_alter()
*/
function demo_user_form_alter($form_id, &$form) {
if ($form_id == 'user_register') {
$username_verify_result = '<div class="reg_form_username" id="username_result"></div>';
$form['account']['name']['#suffix'] = $username_verify_result;
$form['account']['name']['#attributes'] = array('onblur' => 'usernameCheck(this)'); //added the call to our JS function
}
}Grats, you've implemented a rad-looking ajax username checker!
domenic's blog
Comments
In response to the comment
In response to the comment left at http://www.workhabit.org/multiple-form-values-cck-huge-problem#comment-7...
Hey! Thanks for reading, glad it was useful.
Regarding $form, it all depends on the structure of your own registration form. If, for example, your account field is NOT in a field group, $form['name']['#suffix'] would be correct.
Your point about the GET url in the javascript is correct as well.
About the validation graphic, yes it will disappear very quickly if you're working locally! I wouldn't bother with a sleep(), since there will be some latency when you push it live. What I do is style the #suffix with display: none; in CSS (which is why the JS callback has $('#username_result').slideDown(); in it). It has looked okay for me =)
Another question
Fine!
About $form, I've used a clean Drupal 5.7 install, so I think I've used the default user form registration (didn't alter any fieldset).
And the question, about the time of validation graphic, how you would do the call to the "ok-wrong" ajax function if you want to do that only after the php validation ends? This way you'll be always sure that the graphic stays there time enough and users will be able to see it.
Thanks again for the tutorial, hope you do some more on this subject!
cambrico.net
This snippet (taken from the
This snippet (taken from the main post):
$.ajax({type: "GET",
url: '/user/usernamecheck',
data: "username="+obj.value,
success: function(msg){
domCallback(msg);
}
});
should ensure that the ok/wrong graphic only appears once the ajax call is complete. ("success:" refers to the success of the ajax request). Are you working on a remote server or locally? If you're working locally, it will go really quickly.
Hi again! I've packed all
Hi again!
I've packed all this with some modifications (to make it work through hide() and show()) and I've blogged it, you can take a look (it's in spanish, sorry :)) here:
http://cambrico.net/04-05-2008/taller-de-drupal-y-ajax-comprobar-si-un-n...
The direct link to the module is this:
http://cambrico.net/sites/cambrico.net/files/demo_user.zip
Thanks!
http://www.cambrico.net
Post new comment