Score:0

Form API: How to add arbitrary render element after specific field?

ru flag

I've got an input field in an entity form which needs quite a bit of extra documentation. This documentation is long and needs computed, contextual information, so a simple field description or #field_suffix just doesn't do it. I also want to keep the ability to move fields around in field UI. I'm trying to create a form_alter hook to inject a render element with my docs right after that field.

I already have a working prototype:

function MYMODULE_form_ENITY_FORMMODE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $fieldToEnhance = 'field_foo';
  if (!isset($form[$fieldToEnhance])) {
    return;
  }
  $computedDocumentationVariables = mymodule_get_infos();
  $renderElementToAppend[$fieldToEnhance . '_append'] = [
    '#type' => 'inline_template',
    '#template' => '<div>This is a very shortened and simplified placeholder.</div>',
    '#context' => $computedDocumentationVariables,
    '#parents' => $form[$fieldToEnhance]['#parents'] ?? [],
    '#weight' => isset($form[$fieldToEnhance]['#weight']) ? $form[$fieldToEnhance]['#weight'] + 1 : NULL,
  ];
  $positionOfField = array_search($fieldToEnhance, array_keys($form));
  // inject the render element right after the field in the form array
  $form = array_merge(
    array_merge(
      array_slice($form, 0, $positionOfField+1),
      $renderElementToAppend
    ),
    $form
  );
}

This works as exptected for a very plain form.

But as soon as I move my field inside a container from Field group module, the positioning doesn't work as expected. When viewing it in a debugger, I can see that my custom render element is at the correct position inside the $form array. But on screen it is not rendered at the correct position. (My documentation render element is on root level. I want it inside the container, right after the field.)

How do I make my hook aware of potential nesting containers from Field Group module?

4uk4 avatar
cn flag
*a simple field description doesn't do it* ... I don't think there is a limitation on what you can put in `#description`. This variable is printed in the field wrapper template below the field. You can change the position in the template, but I wouldn't change the form array structure if you want the form to work with other modules.
ru flag
I can't inject a render array neither in field descriptions nor the suffix. My desired solution is a render array containing multiple inline templates inside collapsible details elements.
Score:0
cn flag

#suffix and #description are very different. The first one is a Render API standard property common to all render elements and is handled by the Renderer service. The second one is handled as a normal template variable with no restrictions on the number of render elements or nesting levels. You only have to ensure it is printed the way you set it. Check Twig Debug to see which template is used. The standard form-element.html.twig is rendering a subkey content. Some input elements use their own wrapper template/preprocessing and don't have this subkey. You can also define a custom wrapper template with your own set of variables.

ru flag
I was overthinking this. In my initial tests I simply was on the wrong "level" in the render array, "going deeper" in the render array of the field solved all my issues.
apaderno avatar
us flag
`#description` is a form element property too, described in [`FormElement`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21FormElement.php/class/FormElement/9) (which does not list the properties in common with other render elements, listed in [`RenderElement`](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21RenderElement.php/class/RenderElement/9)). It is also used by most of the form elements.
4uk4 avatar
cn flag
Still, #suffix and #description are handled very differently. Only the second one is subjected to preprocessing and is printed as a template variable. I've tried to make the wording more clear.
apaderno avatar
us flag
[form-element.html.twig](https://api.drupal.org/api/drupal/core%21modules%21system%21templates%21form-element.html.twig/9) gets both `suffix` and `description`. The difference is that the value of `#description` is available as `description.content` while the content of `#suffix` is available as `suffix`. Indeed, they are handled differently, but they are both available to preprocessing and to the template file.
4uk4 avatar
cn flag
No, the content of `#field_suffix` is available as `suffix`. Then this is a normal template variable and can print nested render arrays. Strange they use the name of the reserved Render API property `#suffix`, which by the way still works the same as in any other template and has the restrictions I've described.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.