Need some help with your project? Contact me

How to create an autocomplete form element in Drupal 7

Have you ever wondered how you can programatically create an autocomplete form element in Drupal? This tutorial will show you how to develop a form that contains a text field autocomplete widget that uses the Drupal Ajax Framework to dynamically retrieve content from your database.

To illustrate this in an example, I have created a table in my database that holds some colors (green, blue, red, etc). In your case this will probably be something more advanced, but you’ll get the point. Moreover, I created a custom page that displays a form with an autocomplete widget that queries the database as you start typing in it:

OK, so this simple example requires three functions: one to register some URL paths with Drupal, one to create the form that houses our autocomplete widget and one that retrieves the data from the database. This is actually the order I am going to write them in, explaining after each what the purpose of the code is.

function demo_menu() {
  $items['demo-autocomplete-test'] = array(
    'title' => 'Test autocomplete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('demo_form'),
    'access arguments' => array('access content'),
    'type' => MENU_NORMAL_ITEM,
  );

  $items['demo-autocomplete-engine'] = array(
    'page callback' => 'demo_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

This first function implements the Drupal hook_menu(), one of the most useful ones out there. We use it to register with Drupal two URL paths: one for accessing our form that holds the autocomplete widget (demo-autcomplete-test) and one for the AJAX to use as a source for the autocompletion (demo-autocomplete-engine).

Without going too much into how to use this hook, I will make a couple of observations. The first menu item that is declared is given some attributes. We basically say that when a user navigates to www.example.com/demo-autocomplete-test, the respective page should have the title you see, and if the user has the appropriate access permissions, s/he should see the form called demo_form (that we will next create). The second menu item is not used for navigation but to provide the AJAX with a path to use for the autocompletion. You’ll see in a minute what I mean.

Let's see the second function:

function demo_form($form, &$form_state) {
  $form = array();

  $form['colors'] = array(
    '#title' => t('Colors'),
    '#type' => 'textfield',
    '#maxlength' => 60,
    '#autocomplete_path' => 'demo-autocomplete-engine',
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
  );

  return $form;
}

This function creates our testing form that contains an autocomplete widget. Two form items are declared: a textfield for the colors and a submit button. In order to turn a regular Drupal Form API textfield into an autocomplete widget, we need to give it a special attribute: #autocomplete_path -> the source for the autocompletion data to be run by the AJAX script. This is the path we registered in the previous function and gave it a callback function: demo_autocomplete.

So basically when the user starts typing into this textfield, an AJAX call will be made to that path, triggering this callback function that we now need to declare:

function demo_autocomplete($text) {
  $results = array();

  $query = db_select('colors', 'c');
  $query
      ->condition('c.color', '%' . db_like($text) . '%', 'LIKE')
      ->fields('c', array('color'))
      ->orderBy('color', 'ASC');
  $colors = $query->execute();

  foreach ($colors as $row) {
    $results[$row->color] = check_plain($row->color);
  }

  drupal_json_output($results);
}

I mentioned in the beginning that for this example I created a table to store color names in. Well this function does a simple thing: searches through that table using a wildcard and returns the results in a JSON format for the AJAX call. A parameter is passed to the function ($text) which represents the text string to be searched in the database. So to better understand what’s happening here, imagine the following:

As soon as the user types a letter into the texfield, an AJAX call is made and the search is performed in the table for all the colors that contain that letter. As the user types more letters, more searches are performed and the results displayed like in the image I showed you in the beginning. The cool thing is that this is being done dynamically without page refresh using AJAX.

Hope this was clear and helped you understand how you can use this technique in your development. If you have any questions, feel free to drop a line in the comments.

Comments

Hi, I've been struggling to get a working autocomplete field on a form all day, and I've created a very simple example module using just the code you have above. It installs fine on my site, but won't give me access to the actual form... it says 'access denied'. I'm the admin (although not user1) with every access permitted... but still can't get the form to show up -- and wondering why? I've tried rebuilding permissions -- logging in/out -- but still
Access denied
You are not authorized to access this page.

is there something I need to set elsewhere?

thank,
c

The correct argument is array('access content') not array('view published content')

Thanks for catching this!

D

not a great solution long term, but i'm just trying to get a sample one working and then will hammer out the problems. So, by setting the access callback in the menu_hook to TRUE -- it now at least begins to work...

meaning, when I type a letter in the form field -- the blue circle begins to spin... indicating to me that it's trying to run the query.. but then it spins and spins and spins... and eventually times out without retrieving any results....

I've been looking at my code and it matches yours -- but I can't figure out why the results aren't populating the field... any thoughts?

Hey there Chris,

In this article i just mentioned that I created a table that holds the colors. Did you also do this? And did you do it in a way in which the query function I gave as an example here makes sense in your context?

Cheers!

I didn't, but I tried replacing the references to that table with my node table -- selecting for title...

I'll try creating a simple table called colors with a single column called color with a few values ... I presume the table is simply located in the main Drupal database? so I can just use phpmyadmin to create this?

c

thanks -- after reading this, I had a friend look at my code, and he noticed I had incorrectly referenced the callback function...and now it's working perfectly. Thanks for a great submission!

You are very welcome Chris.

Glad it worked out!

Would you know how to create demo_element_info() hook to create a custom type:

'#type'=>'demo',

that auto_completes as per your demo?

I have a quick questions. I was able to use your examples and integrate this into my code and it works and does return results. The only issue that I am having is it doesn't seem to return results that match what I am type in. No matter what I type it always displays all the results, not only the items that match the text. Any idea how or why this would return everything and not just what is matching what I type in the textfield?

This is awesome great work.

Hi,

Thanks for the tutorial. But I was wondering how this will be achieved in drupal 8. I am new in module development so I am not able to make the above code work for Drupal 8. I am trying to create autocomplete field in my custom form which will fetch data for "City" from taxanomy which I created. Please help me out with this.

Thanks

Can you please explain how to show result with link and on click node title that go to node page.

function demo_autocomplete($text) {
  $results = array();
 
  $query = db_select('node', 'n');
  $query
      ->condition('n.title', '%' . db_like($text) . '%', 'LIKE')
        ->condition('status',1,'=');
  $nodes = $query->execute();
 
  foreach ($nodes as $row) {
$link  = l($row->title, 'node/'.$row->nid);    
    $results[$row->title] = check_plain($link);
  }
 
  drupal_json_output($results);
}

Is It Ok ?

i did it like this ... not really beautiful but you can improve it with JS

Implement the submit hook

/**
 * Implements hook_form_submit().
 */
function friendsearch_form_submit(&$form, &$form_state) {
    $form_state['redirect'] = '/node/' . trim($form_state['values']['names']);
}

And the foreach you can use this

  foreach ($nodes as $value) {
    $first_name = $value->field_first_name['und'][0]['value'];
    $name = $value->field_name['und'][0]['value'];
    $link  = $value->nid;

    $results[$link] = check_plain($first_name.' '.$name);
  }

Many many thanks for this kind article - helped me so much :)

How to add throbber in the textfield

Add new comment

You must have Javascript enabled to use this form.