Skip to main content

Quick Guide to using drupal_add_tabledrag and enjoying jquery drag and drop loveliness

6th Mar 2009

Mike Dixon

Senior Mind

We are finding that the feature exciting most end users in Drupal 6 is the lovely new jquery based drag and drop, as seen on the blocks and menu edit pages - we will be quite happy never have to explain the concept of "weights" again. The best news is that you can add this functionality to your own forms for free - and here is how.

Build and theme the form

We are assuming your vaguely familiar with the form API - you can brush up here http://api.drupal.org/api/file/developer/topics/forms_api.html - so we won't go into too much detail here. Essentially we need to create a form that will be themed into a table, each row in the table will need a form element capable of holding the "weight" or "order" value.


  function example_form(&$form_state){

    //fetch the data from the DB
    $result = db_query("SELECT id, value1, value2, weight 
                        FROM {foo} 
                        ORDER BY weight ASC");

    while ($row = db_fetch_object($result)){
      //create a partial table row containing the data from the table
      $data = array(
        $row->value1,
        $row->value2
      );

      //add our static "row" data into a form value
      $form['rows'][$row->id]['data']=array(
                                   '#type' => 'value',
                                   '#value' => $data
                                 );

      //now create the weight form element.  
      //NOTE how we add the id into the element key
      $form['rows'][$row->id]['weight-'.$row->id]=array(
        '#type'=>'textfield',
        '#size'=>5,
        '#default_value'=>$weight,
        //add a specific class in here - we need this later
        '#attributes' => array('class'=>'weight'),
      );
    }    
    
    //Don't forget the submit button
    $form['submit']=array(
      '#type'=>'submit',
      '#value'=>t('Save changes'),
    );

    return $form;
  }

That should create a pretty basic and ugly form - we now need to theme this as a table


  function theme_example_form($form){
    //loop through each "row" in the table array
    foreach($form['rows'] as $id => $row){
      //we are only interested in numeric keys
      if (intval($id)){  
        $this_row = $row['data']['#value'];

        //Add the weight field to the row
        $this_row[] = drupal_render($form['rows'][$id]['weight-'.$sid]);
   
        //Add the row to the array of rows
        $table_rows[] = array('data'=>$this_row, 'class'=>'draggable');
      }
    }
    
    //Make sure the header count matches the column count
    $header=array(
      "Value1","Value2","Order"
    );

    $output = theme('table',$header,$table_rows,array('id'=>'example-table'));
    $output .= drupal_render($form);
 
    // Call add_tabledrag to add and setup the JS for us
    // The key thing here is the first param - the table ID
    // and the 4th param, the class of the form item which holds the weight
    drupal_add_tabledrag('example-table', 'order', 'sibling', 'weight');      

    return $output;
  }

Don't forget - we are in Drupal 6 land now so we need to register our theme function before it will take affect (how many times have I forgotten this already!)


function example_module_theme() {
  return array(
    'example_form' => array(
      'arguments' => array('form' => NULL),
    ),
  );
}

Handle the submit

We should now have a working drag and drop enabled form themed as a table, we now need to handle the submit and store our new weight values.


function example_form_submit($form, &$form_state) {
  foreach($form_state['values'] as $key=>$data){
    //we are only interested in weight elements
    if (substr($key,0,6)=='weight'){
      //cunningly we have the DB id of the row in the element name
      $id = str_replace('weight-','',$key);
      db_query("UPDATE {foo} SET weight=%d WHERE id=%d",$data,$id);
    }
  }
  //optionally set the redirect value in form_submit ...
}