4

I am using Drupal 8 and would like to customize how form elements are being displayed. Specifically, I don't like how uneditable, populated textfields are displayed as plain text. I would have it being displayed as an editable textfield (or have the text look like it is in an uneditable textfield). I have looked at various hook functions to try and achieve this but nothing seems to work.

I figure the best way to go about this is if I can render the form fields individually myself and then create a twig file that displays the individual fields as I would like them to be displayed. Here is what I would like the twig field to look like:

<div class="from">
  {{ form.mail }}
</div>

<div class="message">
  {{ form.message }}
</div>

<div class="actions">
  {{ form.actions }}
</div>

This is what I came up with so far:

function theme_preprocess_form(&$variables) {

  $elements = $variables['element'];
  foreach($elements as $key => $element) {
    if (substr( $key, 0, 1 ) !== "#" ) {
      // $variables['form'][$key] = render[$element];
      // This is where I need the code to actually render the element
    }
  }

}

What is the function I need to call to return a fully rendered form element?

To clarify, I would like to accomplish this by only using they my_theme.theme file for my_theme.

Thanks.

2 Answers 2

11
+50

The base hook form from the question is for rendering the HTML form tag. To render single form elements you need to specify a separate custom template.

1. Register the custom template in a theme hook

mytheme.theme or mymodule.module:

/**
 * Implements hook_theme().
 */
function mytheme/mymodule_theme() {
  return ['entity_moderation_form' => ['render element' => 'form']];
}

2. Add the custom template to the form

While building the form

EntityModerationForm::buildForm():

  public function buildForm(array $form, FormStateInterface $form_state, ContentEntityInterface $entity = NULL) {
    ...
    $form['#theme'] = ['entity_moderation_form'];
    return $form;
  }

or to an existing form in a form alter hook:

mytheme.theme or mymodule.module:

/**
 * Implements hook_form_FORM_ID_alter().
 */
function mytheme/mymodule_form_FORM_ID_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['#theme'] = ['entity_moderation_form'];  
}

You can skip this step if you use as template name the form id, see the comment from @BradJ.

3. Use the custom template to style single form elements

entity-moderation-form.html.twig:

{{ attach_library('content_moderation/content_moderation') }}
<ul class="entity-moderation-form">
  <li>{{ form.current }}</li>
  <li>{{ form.new_state }}</li>
  <li>{{ form.revision_log }}</li>
  <li>{{ form.submit }}</li>
</ul>
{{ form|without('current', 'new_state', 'revision_log', 'submit') }}

The example is from the core module Content Moderation.

6
  • This is a very good answer but it deals with theming at the module level. I was looking for a solution that I could implement by only using the my_theme.theme file for my_theme.
    – user5013
    Commented Sep 10, 2017 at 16:26
  • The example is from a module, but this works the same for themes, probably then by a form alter hook in *.theme, see the edited answer.
    – 4uk4
    Commented Sep 10, 2017 at 17:58
  • I have tried it in 8.5.2 but it does not work for me :( Commented Jun 22, 2018 at 1:25
  • @InspiredCoder, this is an example from core. Ask if you have a question how to implement it.
    – 4uk4
    Commented Jun 22, 2018 at 6:29
  • 1
    This is mostly correct, however it can actually be simpler. The form builder sets #theme to the form ID, so there is not necessarily a reason to specify it in the form alter or ::buildForm() if the template will be the same. I was initially confused about the role of form.html.twig, but that is explained in the form builder's inline documentation; it is set as the #theme_wrapper, and renders the #children key of the element. It does so because when, as is usally the case, the renderer doesn't find a theme == the form ID, it directly renders the element't children. See Renderer:445.
    – Brad J
    Commented Sep 24, 2018 at 22:26
2

Drupal 8 tend to move towards to TWIG template files rather than write theme_hooks (however that has still a big role right now). I suggest you enable TWIG Debuging and that will "spam" the DOM with comments. Before each element there are two types of comments (for your purpose):

  1. How to name your template file to make effect on that element
  2. Which theme_hook to use to modify its outlook/value etc.

I would definitely start on field template level first. If the widget of the field is hard to manipulate, you could also create your own widget type (extending from the current one) and use that. More on that: here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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