A lovely photo of our dear friend, Ben Koren.

BenKoren.com


The number one site for all things Ben Koren.


Form-specific Element Templates in Drupal 8

Out of the box, Drupal 8 uses a common template for all form elements called form-element.html.twig. Today we’ll explore how to override what Drupal gives us by using form-specific and element-specific template suggestions, such as form-element--user-login--username.html.twig, where user-login is our form ID and username is our form element name. As another example, we could override the template for the search box within a specific search form that doesn’t affect any other form element on the site, even if they share the same element name.

On the surface, this seems like it’d be a simple template suggestion. However, this was a little more complicated than meets the eye because when you’re working with a form element in the preprocess layer (or within a suggestion hook), Drupal does not give you any context outside of the element. This means that an element doesn’t know anything about the form that’s rendering it, but it is this context we need to be able to set a form-specific template suggestion.

First we do a hook_form_alter to add in a #form_id attribute to all children of the form:

<?php
function _MYMODULE_form_alter_elements(&$element, $form_id) {
  $children = Element::children($element);
  foreach ($children as $child_key) {
    $element[$child_key]['#form_id'] = $form_id;
    _kraken_form_alter_elements($element[$child_key], $form_id);
  }
}
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  _MYMODULE_form_alter_elements($form, $form_id);
  // ...
}

Once we have the form ID available then it’s just a simple suggestions alter:

<?php
function MYMODULE_theme_suggestions_form_element_alter(array &$suggestions, array $variables) {
  $elementId = str_replace('-', '_', $variables['element']['#id']);
  $formId = str_replace('-', '_', $variables['element']['#form_id']);
  $suggestions[] = 'form_element__' . $formId . '__' . $elementId;
}

If you’re looking for some other template suggestions for form elements such as form-element--[form-id]--[element-type].html.twig or form-element--type--[element-type].html.twig, you can use the mechanism shown above, or you could try out the Themable forms module. As of this writing it’s in Alpha status, but the code is dead-simple; I consider it reasonable for production use.