Custom Ajax Based Search Form I'm trying to create a custom search form that uses ajax to perform the actual search and return the located objects as html.

Currently I'm doing something like this:

function example_module_search_form($form, &amp;$form_state) {
   $form['title'] = array( 
     '#type' =&gt; 'textfield',
     '#value' =&gt; '',
   );
   $form['submit'] = array(
      '#type' =&gt; 'submit',
      '#ajax' =&gt; array(
        'callback' =&gt; 'example_module_search_callback',
        'wrapper' =&gt; 'bottom',
        'method' =&gt; 'replace',
      ),
      '#value' =&gt; t('Submit'),
   );
   return $form;
}

function example_module_search_callback($form, $form_state) {
   $title = $form_state['input']['title'];
   $objects = db_query('SELECT * FROM {node} n WHERE n.type = :type &amp;&amp; title = :title', array(':type' =&gt; 'object', ':title' =&gt; $title))-&gt;fetchAll();
   $output = '';
   foreach($objects as $object):
     $output .= '&lt;div style="border:1px solid red; width:200px; height:200px;"&gt;'.$object-&gt;title.'&lt;/div&gt;';
   endforeach;
   return $output;
}


The above works fine the first time I submit the form, but in subsequent requests the $form_state['input']['title'] is always the same?

Can someone point out what I'm doing wrong or perhaps there's a better approach. The reason why I would like to implement my own search form is that the form will have a very large set of controls which values will be used to query the database.
Usually in Drupal, the ajax callback is business logic free. You should associate your title (ie string) directly in the form, when ajax had run.
I use this :

function contact_contact_search_form($form, &amp;$form_state) {

$string = '';

//Retrive the string value
if (isset($form_state['values'])) { // Does ajax ran ?
   $string = $form_state['values']['string'];
}

$form['string'] = array(
  '#type' =&gt; 'textfield',
  '#title' =&gt; t('Name'),
);

$form['submit'] = array(
  '#type' =&gt; 'submit',
  '#ajax' =&gt; array(
      'callback' =&gt; 'contact_contact_search_ajax',
      'method' =&gt; 'replace',
      'effect' =&gt; 'none',
  ),
  '#value' =&gt; t('Submit'),
);


// Display the result in the form, but should work if the results are outside of your form
$form['table'] = array(
  '#type' =&gt; 'item',
  '#prefix' =&gt; '&lt;div id="search-table"&gt;',
  '#suffix' =&gt; '&lt;/div&gt;',
  '#markup' =&gt; contact_contact_display_searches($string),
);


return $form;
}


function contact_contact_search_ajax(&amp;$form, &amp;$form_state) {


   $commandes[] = ajax_command_replace('#search-table',drupal_render($form['table']));

   return array(
     '#type' =&gt; 'ajax',
     '#commands' =&gt; $commandes,
   );
}


// Create your table
function contact_contact_display_searches($string) {
// The query
  $query = db_select('node', 'n')
      -&gt;fields('n')
      -&gt;condition('n.title', '%' . db_like($string) . '%', 'LIKE');
      -&gt;range(0, 20)
      -&gt;execute()-&gt;fetchAll();

 // Prepare and clean the results
foreach ($results as $result) {
  $rows[$result-&gt;nid] = array(
      'type' =&gt; check_plain($types[$result-&gt;type]-&gt;name),
      'title' =&gt; l(check_plain($result-&gt;title), 'node/' . $result-&gt;nid),
      'email' =&gt; check_plain($result-&gt;field_pers_email_email),
  );
}

// Simple table theming
$out = theme('table', array(
    'header' =&gt; array(t('Type de contenu'), t('Intitulé'), t('email')),
    'rows' =&gt; $rows,
    'empty' =&gt; t('Aucun résultat avec ces critères de recherche')
        )
  );
}
return $out;


}