A better tabledrag example

Public

An example of using a Drupal tabledrag table without the need of creating a theme function for the complete form. This makes it a lot easier to build a form which contains multiple form elements/fieldsets.

Get raw version
php
  1. <?php
  2.  
  3. /**
  4.  * @file
  5.  * A tabledrag example - without theming the whole form.
  6.  */
  7.  
  8. /**
  9.  * Implements hook_menu().
  10.  */
  11. function better_tabledrag_example_menu() {
  12. $items = array();
  13. $items['tabledrag'] = array(
  14. 'title' => 'A tabledrag example',
  15. 'page callback' => 'drupal_get_form',
  16. 'page arguments' => array('better_tabledrag_example_form'),
  17. 'access callback' => TRUE,
  18. );
  19. return $items;
  20. }
  21.  
  22. /**
  23.  * Form callback: A tabledrag example.
  24.  */
  25. function better_tabledrag_example_form($form, &$form_state) {
  26. $form = array();
  27. $rows = array();
  28. $row_elements = array();
  29.  
  30. // Put it into a fieldset for no reason.
  31. $form['data_table'] = array(
  32. '#type' => 'fieldset',
  33. '#title' => t('Data table'),
  34. );
  35.  
  36. // Collect your data.
  37. $data = array(
  38. 'some-id-1' => array(
  39. 'enable' => TRUE,
  40. 'default' => TRUE,
  41. 'weight' => 1,
  42. 'name' => 'some text from config',
  43. 'description' => 'some description text',
  44. ),
  45. 'some-id-2' => array(
  46. 'enable' => TRUE,
  47. 'default' => FALSE,
  48. 'weight' => 3,
  49. 'name' => 'some more text from config',
  50. 'description' => 'more description text',
  51. ),
  52. 'some-id-3' => array(
  53. 'enable' => FALSE,
  54. 'default' => TRUE,
  55. 'weight' => 2,
  56. 'name' => 'and even more text from config',
  57. 'description' => 'mooore description text',
  58. ),
  59. );
  60.  
  61. // Sort the rows.
  62. uasort($data, '_better_tabledrag_example_form_weight_arraysort');
  63.  
  64. // Build the rows.
  65. foreach ($data as $id => $entry) {
  66. // Build the table rows.
  67. $rows[$id] = array(
  68. 'data' => array(
  69. // Cell for the cross drag&drop element.
  70. array('class' => array('entry-cross')),
  71. // Weight item for the tabledrag.
  72. array('data' => array(
  73. '#type' => 'weight',
  74. '#title' => t('Weight'),
  75. '#title_display' => 'invisible',
  76. '#default_value' => $entry['weight'],
  77. '#parents' => array('data_table', $id, 'weight'),
  78. '#attributes' => array(
  79. 'class' => array('entry-order-weight'),
  80. ),
  81. )),
  82. // Enabled checkbox.
  83. array('data' => array(
  84. '#type' => 'checkbox',
  85. '#title' => t('Enable'),
  86. '#title_display' => 'invisible',
  87. '#default_value' => $entry['enable'],
  88. '#parents' => array('data_table', $id, 'enabled'),
  89. )),
  90. // Default checkbox.
  91. array('data' => array(
  92. '#type' => 'checkbox',
  93. '#title' => t('Default'),
  94. '#title_display' => 'invisible',
  95. '#default_value' => $entry['default'],
  96. '#parents' => array('data_table', $id, 'default'),
  97. )),
  98. // Name textfield.
  99. array('data' => array(
  100. '#type' => 'textfield',
  101. '#size' => 10,
  102. '#title' => t('Name'),
  103. '#title_display' => 'invisible',
  104. '#default_value' => $entry['name'],
  105. '#parents' => array('data_table', $id, 'name'),
  106. )),
  107. // Entry description.
  108. check_plain($entry['description']),
  109. // Operations.
  110. array('data' => array(
  111. '#theme' => 'link',
  112. '#text' => t('Edit settings'),
  113. '#path' => 'tabledrag/' . $id . '/edit',
  114. '#options' => array('attributes' => array(), 'html' => FALSE),
  115. )),
  116. array('data' => array(
  117. '#theme' => 'link',
  118. '#text' => t('Delete entry'),
  119. '#path' => 'tabledrag/' . $id . '/delete',
  120. '#options' => array('attributes' => array(), 'html' => FALSE),
  121. )),
  122. ),
  123. 'class' => array('draggable'),
  124. );
  125. // Build rows of the form elements in the table.
  126. $row_elements[$id] = array(
  127. 'weight' => &$rows[$id]['data'][1]['data'],
  128. 'enabled' => &$rows[$id]['data'][2]['data'],
  129. 'default' => &$rows[$id]['data'][3]['data'],
  130. 'name' => &$rows[$id]['data'][4]['data'],
  131. );
  132. }
  133.  
  134. // Add the table to the form.
  135. $form['data_table']['table'] = array(
  136. '#theme' => 'table',
  137. // The row form elements need to be processed and build,
  138. // therefore pass them as element children.
  139. 'elements' => $row_elements,
  140. '#header' => array(
  141. // We need two empty columns for the weigth field and the cross.
  142. array('data' => NULL, 'colspan' => 2),
  143. t('Enabled'),
  144. t('Default'),
  145. t('Name'),
  146. t('Description'),
  147. array('data' => t('Operations'), 'colspan' => 2),
  148. ),
  149. '#rows' => $rows,
  150. '#empty' => t('There are no entries available.'),
  151. '#attributes' => array('id' => 'entry-order'),
  152. );
  153. drupal_add_tabledrag('entry-order', 'order', 'sibling', 'entry-order-weight');
  154.  
  155. return $form;
  156. }
  157.  
  158. /**
  159.  * Helper function for sorting entry weights.
  160.  */
  161. function _better_tabledrag_example_form_weight_arraysort($a, $b) {
  162. if (isset($a['weight']) && isset($b['weight'])) {
  163. return $a['weight'] < $b['weight'] ? -1 : 1;
  164. }
  165. return 0;
  166. }

Comments

merzikain's picture

A few aesthetic improvements.

  1. $rows[$id] = array(
  2. 'data' => array(
  3. // Name textfield.
  4. array('data' => array(
  5. '#type' => 'textfield',
  6. '#size' => 10,
  7. '#title' => t('Name'),
  8. '#title_display' => 'invisible',
  9. '#default_value' => $entry['name'],
  10. '#parents' => array('data_table', $id, 'name'),
  11. )
  12. 'class' => array('entry-cross')),
  13. // Enabled checkbox.
  14. array('data' => array(
  15. '#type' => 'checkbox',
  16. '#title' => t('Enable'),
  17. '#title_display' => 'invisible',
  18. '#default_value' => $entry['enable'],
  19. '#parents' => array('data_table', $id, 'enabled'),
  20. )),
  21. // Default checkbox.
  22. array('data' => array(
  23. '#type' => 'checkbox',
  24. '#title' => t('Default'),
  25. '#title_display' => 'invisible',
  26. '#default_value' => $entry['default'],
  27. '#parents' => array('data_table', $id, 'default'),
  28. )),
  29. // Entry description.
  30. check_plain($entry['description']),
  31. // Operations.
  32. array('data' => array(
  33. '#theme' => 'link',
  34. '#text' => t('Edit settings'),
  35. '#path' => 'tabledrag/' . $id . '/edit',
  36. '#options' => array('attributes' => array(), 'html' => FALSE),
  37. )),
  38. array('data' => array(
  39. '#theme' => 'link',
  40. '#text' => t('Delete entry'),
  41. '#path' => 'tabledrag/' . $id . '/delete',
  42. '#options' => array('attributes' => array(), 'html' => FALSE),
  43. )),
  44. // Weight item for the tabledrag.
  45. array('data' => array(
  46. '#type' => 'weight',
  47. '#title' => t('Weight'),
  48. '#title_display' => 'invisible',
  49. '#default_value' => $entry['weight'],
  50. '#parents' => array('data_table', $id, 'weight'),
  51. '#attributes' => array(
  52. 'class' => array('entry-order-weight'),
  53. ),
  54. )
  55. 'class' => array('tabledrag-hide')),
  56. ),
  57. 'class' => array('draggable'),
  58. );
  59. // Build rows of the form elements in the table.
  60. $row_elements[$id] = array(
  61. 'weight' => &$rows[$id]['data'][1]['data'],
  62. 'enabled' => &$rows[$id]['data'][2]['data'],
  63. 'default' => &$rows[$id]['data'][3]['data'],
  64. 'name' => &$rows[$id]['data'][4]['data'],
  65. );
  66. }
  67.  
  68. // Add the table to the form.
  69. $form['data_table']['table'] = array(
  70. '#theme' => 'table',
  71. // The row form elements need to be processed and build,
  72. // therefore pass them as element children.
  73. 'elements' => $row_elements,
  74. '#header' => array(
  75. // We need two empty columns for the weigth field and the cross.
  76. t('Name'),
  77. t('Enabled'),
  78. t('Default'),
  79. t('Description'),
  80. array('data' => t('Operations'), 'colspan' => 2),
  81. array('data' => t('Weight'), 'class' => array('tabledrag-hide')),
  82. ),
  83. '#rows' => $rows,
  84. '#empty' => t('There are no entries available.'),
  85. '#attributes' => array('id' => 'entry-order'),
  86. );

What that will do is move "Name" to the first position in the table and add the "move" cross bar in front of it in the same column. It will hide the weight column by default and then when you click on the "Show row weights" link that appears above the table it will hide the cross bar and show the weight column like other draggable tables. It looks cleaner and functions naturally.

Jasper's picture

Nice!
However I'm facing some issues... I have integrated the table into a bigger form and its behavior is ok. The problem comes when pressing the "submit" button. The rest of the fields of the form keeps the selected value but all the chackboxes from the draggable table returns to their previous default value.
It is not keeping the selected values.

What I'm doing wrong? Thank you in advance!

Pol's picture

Thanks for this wonderful example, I'm using it in the upcoming version of the module OpenLayers !