function Table::attachTabledrag

Same name and namespace in other branches
  1. 11.x core/lib/Drupal/Core/Render/Element/Table.php \Drupal\Core\Render\Element\Table::attachTabledrag()

Assists in attaching the tableDrag JavaScript behavior to a themed table.

Draggable tables should be used wherever an outline or list of sortable items needs to be arranged by an end-user. Draggable tables are very flexible and can manipulate the value of form elements placed within individual columns.

To set up a table to use drag and drop in place of weight select-lists or in place of a form that contains parent relationships, the form must be themed into a table. The table must have an ID attribute set and it maybe set as follows:

$table = [
  '#type' => 'table',
  '#header' => $header,
  '#rows' => $rows,
  '#attributes' => [
    'id' => 'my-module-table',
  ],
];
return \Drupal::service('renderer')->render($table);

In the theme function for the form, a special class must be added to each form element within the same column, "grouping" them together.

In a situation where a single weight column is being sorted in the table, the classes could be added like this (in the theme function):

$form['my_elements'][$delta]['weight']['#attributes']['class'] = [
  'my-elements-weight',
];

Each row of the table must also have a class of "draggable" in order to enable the drag handles:


$row = [...];
$rows[] = [
  'data' => $row,
  'class' => ['draggable'],
];

When tree relationships are present, the two additional classes 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior:

  • Rows with the 'tabledrag-leaf' class cannot have child rows.
  • Rows with the 'tabledrag-root' class cannot be nested under a parent row.

Calling Table::attachTabledrag() would then be written as such:


Table::attachTabledrag('my-module-table', [
  'action' => 'order',
  'relationship' => 'sibling',
  'group' => 'my-elements-weight',
];

In a more complex case where there are several groups in one column (such as the block regions on the admin/structure/block page), a separate subgroup class must also be added to differentiate the groups.

$form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = [
  'my-elements-weight',
  'my-elements-weight-' . $region,
];

The 'group' option is still 'my-element-weight', and the additional 'subgroup' option will be passed in as 'my-elements-weight-' . $region. This also means that you'll need to call Table::attachTabledrag() once for every region added.

foreach ($regions as $region) {
  Table::attachTabledrag('my-module-table', [
    'action' => 'order',
    'relationship' => 'sibling',
    'group' => 'my-elements-weight',
    'subgroup' => 'my-elements-weight-' . $region,
  ]);
}

In a situation where tree relationships are present, adding multiple subgroups is not necessary, because the table will contain indentations that provide enough information about the sibling and parent relationships. See MenuForm::BuildOverviewForm for an example creating a table containing parent relationships.

Parameters

array $element: A form element to attach the tableDrag behavior to.

array $options: These options are used to generate JavaScript settings necessary to configure the tableDrag behavior appropriately for this particular table. An associative array containing the following keys:

  • 'table_id': String containing the target table's id attribute. If the table does not have an id, one will need to be set, such as <table id="my-module-table">.
  • 'action': String describing the action to be done on the form item. Either 'match' 'depth', or 'order':

    • 'match' is typically used for parent relationships.
    • 'order' is typically used to set weights on other form elements with the same group.
    • 'depth' updates the target element with the current indentation.
  • 'relationship': String describing where the "action" option should be performed. Either 'parent', 'sibling', 'group', or 'self':

    • 'parent' will only look for fields up the tree.
    • 'sibling' will look for fields in the same group in rows above and below it.
    • 'self' affects the dragged row itself.
    • 'group' affects the dragged row, plus any children below it (the entire dragged group).
  • 'group': A class name applied on all related form elements for this action.
  • 'subgroup': (optional) If the group has several subgroups within it, this string should contain the class name identifying fields in the same subgroup.
  • 'source': (optional) If the $action is 'match', this string should contain the classname identifying what field will be used as the source value when matching the value in $subgroup.
  • 'hidden': (optional) The column containing the field elements may be entirely hidden from view dynamically when the JavaScript is loaded. Set to FALSE if the column should not be hidden.
  • 'limit': (optional) Limit the maximum amount of parenting in this table.

See also

\Drupal\menu_ui\MenuForm::buildOverviewForm()

3 calls to Table::attachTabledrag()
drupal_attach_tabledrag in core/includes/common.inc
Assists in attaching the tableDrag JavaScript behavior to a themed table.
RendererLegacyTest::testTableDrag in core/tests/Drupal/KernelTests/Core/Render/RendererLegacyTest.php
Tests deprecation of drupal_attach_tabledrag().
ViewsUiThemeHooks::preprocessRearrangeFilterForm in core/modules/views_ui/src/Hook/ViewsUiThemeHooks.php
Prepares variables for Views UI rearrange filter form templates.

File

core/lib/Drupal/Core/Render/Element/Table.php, line 590

Class

Table
Provides a render element for a table.

Namespace

Drupal\Core\Render\Element

Code

public static function attachTabledrag(array &$element, array $options) : void {
  // Add default values to elements.
  $options += [
    'subgroup' => NULL,
    'source' => NULL,
    'hidden' => TRUE,
    'limit' => 0,
  ];
  $group = $options['group'];
  // The counter for tableDrag instances is stored in the memory cache
  // instead of a static property, so it does not bleed across requests in
  // long-running PHP processes (e.g., FrankenPHP, RoadRunner), where memory
  // cache bins are reset between requests.
  $cache = \Drupal::cache('memory');
  $cache_item = $cache->get(static::TABLE_DRAG_CACHE_ID);
  $tabledrag_id = $cache_item ? $cache_item->data + 1 : 0;
  $cache->set(static::TABLE_DRAG_CACHE_ID, $tabledrag_id);
  // If a subgroup or source isn't set, assume it is the same as the group.
  $target = $options['subgroup'] ?? $group;
  $source = $options['source'] ?? $target;
  $element['#attached']['drupalSettings']['tableDrag'][$options['table_id']][$group][$tabledrag_id] = [
    'target' => $target,
    'source' => $source,
    'relationship' => $options['relationship'],
    'action' => $options['action'],
    'hidden' => $options['hidden'],
    'limit' => $options['limit'],
  ];
  $element['#attached']['library'][] = 'core/drupal.tabledrag';
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.