11

In Drupal 8, rendering Tables is still a lot like Drupal 7. You build out multi-dimensional arrays of rows and columns in PHP that Drupal transforms into a <tr> and <td>s respectively. There is still this confusing Drupalism known as 'data' that lets you add render array elements as cell data (not to be confused with data attributes).

I've been given a site where the developer chose to use 'data' to render the contents of the cell, but I cannot figure out how to add a class to the <td> around the data.

I've read the source code and documentation for Table.php and I'm aware of the new #wrapper_attributes but I cannot crack this.

I've tried at least four ways to add the class, and none work.

$table['row-' . $row_id] = [

  // Option 1: Class appears on <tr> tag
  '#attributes' => [
    'class' => ['option-1-row-attributes'],
    'id' => 'row-' . $row_id,
    'no_striping' => TRUE,
  ],

  // Option 2: Class appears on <td> tag of first column. 
  'item' => [
    '#markup' => $row['my_item']->label(),
    '#wrapper_attributes' => [   
      'class' => ['option-2-markup-wrapper-attributes'],
    ],
  ],

  // In the following section, the only item that works is
  // the class on the <a> tag.
  'edit_operation' => [
    'data' => [
      '#type' => 'link',
      '#url' => Url::fromRoute('my_module.my_route', ['item' => $row_id]),
      '#title' => $this->t('Edit'),
      '#attributes' => [
        // Option 3: Class appears on the anchor tag
        'class' => ['use-ajax', 'option-3-link-attributes'],
        'data-dialog-type' => 'modal',
        'data-dialog-options' => Json::encode([
          'width' => 700,
        ]),
      ],
      // Option 4: Has no effect.
      '#wrapper_attributes' => [
        'class' => ['option-4-data-wrapper-attributes'],
      ],
    ],
    // Option 5: Update: This appears to be the correct solution! 
    // Class appears on the <td>.
    '#wrapper_attributes' => [
      'class' => ['option-5-wrapper-attributes'],
    ],
    // Option 6: Has no effect.
    '#attributes' => [
      'class' => ['option-6-attributes'],
    ],
    // Option 7: Has no effect.
    'class' => ['option-7-attributes'],
  ],
];
12

After writing out the question in general terms, I've gone back to test again, and have determined that Option 5 in the OP with '#wrapper_attributes' at the same level of the 'data' element does work. I believe Drupal 8 was aggressively caching the table, because my changes were not showing up even after a drush cr.

The rules for adding classes to tables via backend PHP is:

  • Table class requires #attributes.
  • TR row class inside TBODY requires #attributes.
  • TD cell class inside TBODY requires #wrapper_attributes.
  • TR row class inside THEAD/TFOOT requires 'class' and 'data' containers.
    Neither #attributes nor #wrapper_attributes work here.
  • TH/TD cell class inside THEAD/TFOOT requires 'class' and 'data' containers.
    Neither #attributes nor #wrapper_attributes work here.
  • There is no way to add a class directly to a <thead> or <tfoot> tag without overriding a twig template.

Here is the most common example for adding classes to the <tr> & <td> tags inside the main <tbody>, as well as to the main <table> tag itself:

$table = [
  '#type' => 'table',
  '#attributes' => [
    'class' => ['table-class'],
  ],
  'row1' => [
    '#attributes' => [
      'class' => ['tr-class'],
    ],
    // Table data cell using 'data' for renderable elements.
    'column1' => [
      'data' => [
        '#type' => 'link', // Or any other renderable thing.
        '#attributes' => [
          'class' => ['link-class'],
        ],
        // Other elements required to render the link go here...
      ],
      '#wrapper_attributes' => [ // Watch out!
        'class' => ['td-class'],
      ],
    ],
    // Table data cell using '#markup'.
    'column2' => [
      '#markup' => '<span>' . $this->t('text') . '</span>',
      '#wrapper_attributes' => [   
        'class' => ['td-class'],
      ],
    ],
  ],
];

Note that the 'class' container will accept either a string or an array, but I suggest always using an array.

From here, the story gets more complicated. If you need to add classes to the TR or TD/TH tags inside a THEAD/TFOOT area, the rules change completely. Neither #attributes nor #wrapper_attributes work inside #header and #footer sections and trying to use them produces very strange effects.

The bare minimum structure for tables with header/footer data columns in Drupal 8 is this:

$table = [
  '#type' => 'table',
  // Produces <thead> <tr> <th>
  '#header' => [
    'Header 1',
    'Header 2',
    'Header 3',
  ],
  // Produces <tbody> <tr> <td>
  'row1' => [
    'Body 1',
    'Body 2',
    'Body 3',
  ],
  // Produces <tfoot> <tr> <td>
  '#footer' => [
    'Footer 1',
    'Footer 2',
    'Footer 3',
  ],
];

You must change the actual structure of the data and introduce two levels of additional multi-dimensional arrays, in order to leverage the 'class' array index which requires then also introducing the 'data' array index. This applies both to the row element and the data cell elements, as seen in the following example:

$table = [
  '#type' => 'table',
  // This example works the same way for '#footer'.
  '#header' => [
    // First, introduce an extra level to the array to provide a
    // place to store the class attribute on the <tr> element inside
    // the <thead>.
    [
      'class' => 'thead-tr-class',
      // Next place the columns inside a 'data' container, so that
      // the 'class' can be used.  '#wrapper_attributes' will not
      // work here.
      'data' => [
        // The following line produces data inside a <th>
        // without any class.
        'Header 1',

        // The following lines produce data inside a <th>
        // with a class: th-class.
        [
           'class' => 'th-class',
           'data' => 'Header 2',
           'colspan' => 2
        ],
      ],
    ],
  ],
];

The above example #header example produces:

<table>
  <thead>
    <tr class="thead-tr-class">
      <th>Header 1</th>
      <th class="th-class" colspan="2">Header 2</th>
    </tr>
  </thead>
</table>
| improve this answer | |
  • I'm trying to use a colspan in the table header but using your last example I get this errors: – Adrian Cid Almaguer Sep 12 '17 at 13:58
  • User error: "0" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core/lib/Drupal/Core/Render/Element.php). User error: "class" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core/lib/Drupal/Core/Render/Element.php). User error: "data" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core/lib/Drupal/Core/Render/Element.php). User error: "colspan" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core/lib/Drupal/Core/Render/Element.php). – Adrian Cid Almaguer Sep 12 '17 at 14:00
  • I just found another solution for the colspan, take a look here drupal.stackexchange.com/q/245710/28275 – Adrian Cid Almaguer Sep 12 '17 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.