Split from #2177469: Move node base widgets to the top level of the form

Problem / current behavior:

Currently, #group only works for element types that add RenderElementBase::processGroup() and RenderElementBase::preRenderGroup() to their processing and rendering callbacks.

This already works for textfield, textarea and checkbox, but not for select or checkboxes, for example. This seems a bit arbitrary and doesn't scale well.

Nesting an element inside a container is another option, but changes it's location in the form array. Whether that also changes the submitted value structure depends on #tree and #parents.

Proposed solution:

Add the grouping callbacks centrally in ElementInfoManager, so that individual element types no longer need to add them themselves. Existing element-specific callbacks and their order should be preserved.

This allows to visually group elements without moving them in the form array:

$form['contact'] = [
  '#type' => 'details',
  '#title' => $this->t('Contact details'),
  '#open' => TRUE,
];

$form['contact_method'] = [
  '#type' => 'select',
  '#title' => $this->t('Contact method'),
  '#options' => [
    'email' => $this->t('Email'),
    'phone' => $this->t('Phone'),
  ],
  '#group' => 'contact',
];

The select is displayed inside the details element, but its value is still available through $form_state->getValue('contact_method'). Both this approach and normal nesting remain supported.

While looking into this, an existing core issue was also found where group members can be rendered twice. This happens when the group target renders the element again to output its children. It reproduces on clean main without this MR and is now covered by a test in #2926030: Fully test #render_children rendering and fix/find any bugs. This MR enables grouping for more element types and would therefor expose that problem in more cases.

TODO:

  1. Review the implementation, added tests and remaining CI warnings.
  2. Document how to use #group and how it relates to the form array and submitted values.
  3. Review the performance impact of adding the callbacks to all element types.
  4. Decide whether #2926030: Fully test #render_children rendering and fix/find any bugs needs to be fixed before this can be merged.

Issue fork drupal-2190333

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

pancho’s picture

Important, currently incomplete feature that allows decoupling visual form output from form value structure.

@tstoeckler in #2177469-45: Move node base widgets to the top level of the form:

There are different use-cases here that need to be consider. If I just want to change the #default_value or the #description of a form element this patch makes that easier because I can just blindly to that in $form[$field_name]['#foo'] and don't need to bother with possible nesting.

Another use-case, however, is changing the *output* structure of the form, i.e. moving form elements into different groups, adding form elements into existing groups, perhaps nesting groups into each other, etc. With fieldgroup.module in conrib this is currently a non-trivial task that requires deep knowledge about the different structures and array keys that fieldgroup module uses. It's not as easy as simply setting/altering $form[$field_name]['#group'] or anything similar.

@sun in #1856178-1: '#group' Form API property only works with <details> elements:

We should turn the #group facility into a ultra-generic thing.

Currently, the feature is incomplete and inconsistent. There's no reason why '#group' works with textfields and a single checkbox, but not with selects or multiple checkboxes.

@sun in #2177469-41: Move node base widgets to the top level of the form:

It would be a very good idea to start an effort to convert all forms throughout core to the new concept, so as to ensure and guarantee a good and consistent DX. [...] The form_process_group() and form_pre_render_group() callbacks should be removed and the concept should be refactored directly into FormBuilder.

We may refactor the logic directly into FormBuilder, deprecating the existing callbacks. But as a first step, let's add the callbacks to all elements in ElementInfoManager, then let's see which elements need special consideration (VerticalTabs for sure, but also Container after #2893586: Add #optional support to the Container render element.

Here's a first patch. Let's see how it tests.

Status: Needs review » Needs work

The last submitted patch, 7: element_group_feature_2190333-7.patch, failed testing. View results

pancho’s picture

Version: 8.6.x-dev » 8.8.x-dev
Status: Needs work » Needs review
StatusFileSize
new9.77 KB
new2.76 KB

Not too bad.

Fixing the test failure.

Now we may also simplify FieldLayoutBuilder::buildForm() a bit, removing the #group processing code added in #2796173-39: Add experimental Field Layout module to allow entity view/form modes to switch between layouts.

This patch landing should also be unblocking #2846393: [PP-1] Investigate alternative approaches to moving fields in FieldLayoutBuilder::buildView().

Furthermore, we may refactor many forms, as far as there are no BC concerns, replacing the value-reparenting with '#parents' by form-reparenting using '#group'. This may be a followup or rolled in. If we opted for rolling it in, we'd immediately get more test coverage for '#group'.

Status: Needs review » Needs work

The last submitted patch, 9: element_group_feature_2190333-9.patch, failed testing. View results

pancho’s picture

Status: Needs work » Needs review
StatusFileSize
new7.57 KB
new1.77 KB

Okay, let's rewind and leave layout builder alone for now.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

lowfidelity’s picture

Patch is working well on a rather complex D 8.7.8 project.
Thanks a lot!

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

kawaljeet singh’s picture

Not working with Ajax callback, if we Replace select options using ajax callback.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

grimreaper’s picture

ranjith_kumar_k_u’s picture

StatusFileSize
new7.53 KB

Re-rolled # 11 for 9.4.

Status: Needs review » Needs work

The last submitted patch, 20: 2190333-20.patch, failed testing. View results

anybody’s picture

Issue summary: View changes
Issue tags: +Needs documentation

Just ran into the same issue. The key point is, that quite often a way is required to group fields (and groups) without changing the field / form data structure.

While the nesting functionality works great, when you have full control over the code or even want nesting, everything is all right. But if form values are stored by a third party module or core, you may simply change the display, but not the data structure.

#group seems to be a good approach for that case, where you don't want to change the nesting structure.

Adding the "Needs documentation" tag, once this is available. Currently this only seems to be documented on the Drupal 6 documentation page: https://www.drupal.org/node/262758

I only found this blog article about using #group for this, while the documentation page only shows nesting examples. #tree doesn't seem to be helpful to prevent

Example case: Extend views StylePluginBase and add further fields. Use "details" field group to structure the additional settings, without breaking the form save logic. => Impossible (without manually massaging the nested values).

I wrote a blog post about that with examples: https://julian.pustkuchen.com/en/drupal-9-form-api-grouping-without-affe...

As this isn't really documented, what's the expected behavior and what's the plan for that in core?

anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes
anybody’s picture

Issue summary: View changes

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jayhuskins’s picture

The SystemMenuBlock gets around this issue with a #process value on the container elements that calls processMenuLevelParents to remove the container from the form values.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

carlitus’s picture

Tested and for the moment it works well.

Without a Checkbox, for example, works well with a details parent but not a Radios with a details parent.

With the #20 patch it works well with the Radios child.

I think getting this is a big step for Drupal, since as @Anybody says many times you can't modify the form structure but you do need to be able to group it.

Needed to make this patch: https://www.drupal.org/project/style_options/issues/3310055#comment-1519...

anybody’s picture

@Grimreaper and @Pancho any ideas how we can take this issue forward and perhaps get some frontend framework manager feedback on this?

I think before we make the next coding steps here, we should get some feedback from the core team, what they think about the key aspects?

carlitus’s picture

I've see that this doesn't work with managed_file and i don't know why.

It shows in the right position but when you select a file the ajax doesn't work well and disapears.

carlitus’s picture

StatusFileSize
new8.13 KB

New patch to support managedFile and Ajax Callback.

I think this is not the best way to do it, but now al least works in my manual tests. I am sure that a person with more control of the core can do it in the most appropriate way.

I've see that the problem was when in the ajax callback to upload the file, the preRenderGroup was not needed and i've make a unset of the #groups in uploadAjaxCallback to skip this function.

carlitus’s picture

StatusFileSize
new8.12 KB

New patch, the last one was wrong

loze’s picture

I also encountered this extending a field widget formatter. The patch in #34 seems to work with Drupal 10.4.2

luenemann’s picture

I've needed to group the actions container and came to the following workaround

      $form['actions']['#group'] = 'off_canvas_sticky_header';
      // remove when https://www.drupal.org/project/drupal/issues/2190333 fixed.
      $form['actions']['#process'][] = [Actions::class, "processGroup"];
      $form['actions']['#pre_render'][] = [Actions::class, 'preRenderGroup'];

The current patch needs to be converted to a Merge Request.

anjaliprasannan made their first commit to this issue’s fork.

anjaliprasannan’s picture

prabha1997 made their first commit to this issue’s fork.

berliner’s picture

That MR looks buggy. The patch in #34 seems to also work with Drupal 10.5

aunv’s picture

StatusFileSize
new7.88 KB

a new patch for Drupal 11.2.2 and PHP 8.4

aunv’s picture

StatusFileSize
new7.19 KB

Update patch #42

carlitus’s picture

I see that the issue has the tag Needs documentation.

¿What means this? ¿There are some documentation to know what are the steps to make this documentation?

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

berliner’s picture

Status: Needs work » Needs review
StatusFileSize
new20.09 KB

I have rebased the MR onto main and fixed the callbacks, the textarea attachments and re-added the container callback that went missing for some reason.
Also added some more tests and fixed /core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php that failed because the MR adds the group callbacks now for all elements which changes the test expectations.

Attaching a static patch file against Drupal 11.4.7.

Generated with the help of an LLM.

anybody’s picture

@berliner thank you very much, that looks good!
Do you think other tests are needed? At least the issue summary says so, looks like it needs an update?

berliner’s picture

I have added additional test coverage for the AJAX select case reported as failing in #2190333-16: Make #group FAPI / render feature work on all form/render #types out of the box. Test-only change that switches select options multiple times and confirms that the select stays in its group, is not duplicated and correctly submits at the end. This passes locally, let's see what the test bot thinks.

While looking into this I also found something that appears to be an existing core bug that should probably be handled separately: Some elements can be rendered twice when assigned to a group of an element that renders the element again to render its children. I have reproduced this locally on clean main, without this MR applied, with the datetime element. If a textfield is assigned to a datetime element using #group, that textfield is rendered twice. I have added that observation with more details to #2926030: Fully test #render_children rendering and fix/find any bugs.

berliner’s picture

berliner’s picture

@anybody I have updated the issue summary with the current state as I see it. I also pushed another commit, improving the managed file ajax update cycle as the original fix for that issue in #33 kind of worked, but had the side effect that other elements grouped inside a managed file widget disappeared after an ajax update.

I don't see anything else that requires tests for now, but let's see what other reviewers find.

berliner changed the visibility of the branch berliner/2190333-form-groups to hidden.