Skip to main content
Creating editable table

Drupal 7 - Creating editable table with Form API

Introduction:

Lets think that we have a custom table to show list of records. Sometimes you might have to provide editable options for column. One simpler solution is to have a edit link and open up a form and let the user update the records. But a much more simple user experience will need inline editing of columns. Simply using theme('table') with textfields might not work since theme_table will run on the render array, your form elements will actually be rendered out to the screen, but the form won't have any knowledge of the values (as they are not technically children of the form) .

Implementation:

To overcome this limitation, define a new theme function,

  1. /**
  2.  * Implements hook_theme().
  3.  */
  4. function MODULE_quiz_theme($existing, $type, $theme, $path) {
  5.   return array(
  6.     'MODULE_textfield_table' => array(
  7.       'render element' => 'element'
  8.     ),
  9.   );
  10. }

then write the actual theme function that loops the elements and calls theme('table').

  1. function theme_MYMODULE_textfield_table($vars) {
  2.   $element = $vars['element'];
  3.  
  4.   $rows = array();
  5.   foreach (element_children($element) as $key) {
  6.     $rows[] = array(
  7.       array('data' => 'label'),
  8.       array('data' => $element[$key]['#extra_data']['fied1']),
  9.       array('data' => $element[$key]['#extra_data']['field2']),
  10.       array('data' => render($element[$key]))
  11.     );
  12.   }
  13.  
  14.   $header = array(t('Field Label'), t('Field1'), t('Field2'), t('Edit'));
  15.   return theme('table', array('header' => $header, 'rows' => $rows));
  16. }

Note that the render($element[$key] is the actual textfield that is going to be rendered. Then use the respective theme name inside our form,

  1. $form['things'] = array(
  2.   '#tree' => TRUE,
  3.   '#theme' => 'MYMODULE_textfield_table'
  4. );
  5.  
  6. foreach ($records as $record) {
  7.   $form['things'][] = array(
  8.     '#type' => 'textfield',
  9.     '#default_value' => $record->field3,
  10.     '#extra_data' => array('field1' => $record->field1, 'field1' => $record->field2)
  11.   );

Multiple editable fields:

You could similarly create a tabular form with multi field editing option as well. For eg,

  1. $form['things'] = array(
  2.   '#tree' => TRUE,
  3.   '#theme' => 'MYMODULE_textfield_table'
  4. );

lets assume we have rows of data in $rows,

  1. foreach ($rows as $row) {
  2.     $form['things'][$index]['#topic'] = t('Some text');
  3.     $form['things'][$index]['min_marks'] = array(
  4.       '#type' => 'textfield',
  5.       '#default_value' => $row->min_mark,
  6.       '#size' => 10
  7.     );
  8.     $form['things'][$index]['max_marks'] = array(
  9.       '#type' => 'textfield',
  10.       '#default_value' => $row->max_mark,
  11.       '#size' => 10
  12.     );
  13. }
in theme function,
 
  1. foreach (element_children($element) as $key) {
  2.   $rows[] = array(
  3.     array('data' => $element[$key]['#topic']),
  4.     array('data' => $element[$key]['min_marks']),
  5.     array('data' => $element[$key]['max_marks']),
  6.   );
  7. }
You will get rows of data with two input text boxes along with a label 'Some text' in first column. In the submit handler, you can receive the values from form_state['values'] as usual and do the needed work.
 
References:
http://drupal.stackexchange.com/questions/27264/drupal-form-themed-as-table-not-posting-data-correctly-or-displaying-correctly

 

 

Comments

Steve (not verified)

Mon, 06/10/2013 - 08:42

Unfortunately, I get all textfields rendered one under each other, and not in tabular style.

I developed the following (dummy) code from your example:

$form['things'] = array(
'#tree' => TRUE,
'#theme' => 'testmod_textfield_table'
);

$rows = array(
'1' => array('min_marks' => '5', 'max_marks' => '8'),
'2' => array('min_marks' => '25', 'max_marks' => '12'),
'3' => array('min_marks' => '315', 'max_marks' => '28'),
);

foreach ($rows as $row) {
$index++;
$form['things'][$index]['#topic'] = t('Some text');
$form['things'][$index]['min_marks'] = array(
'#type' => 'textfield',
'#default_value' => $row->min_mark,
'#size' => 10
);
$form['things'][$index]['max_marks'] = array(
'#type' => 'textfield',
'#default_value' => $row->max_mark,
'#size' => 10
);
}

and in the hook:

function theme_testmod_textfield_table($vars) {
$element = $vars['element'];

foreach (element_children($element) as $key) {
$rows[] = array(
array('data' => $element[$key]['#topic']),
array('data' => $element[$key]['min_marks']),
array('data' => $element[$key]['max_marks']),
);
}
return theme('table', array('header' => $header, 'rows' => $rows));
}

Can you tell me, what I'm missing?

pavel murnikov (not verified)

Sat, 12/27/2014 - 15:53

Many thanks for your post,

Are you sure that submit handler will get the user entered values in
$form_state['values']['things'][0]['min_marks']
My code which follows you suggestions ignores any user input and shows only the default_values