Problem/Motivation

Currently if you define a container element that is empty, you get empty markup. This may or may not be desirable, but currently you cannot define this behavior.

This was pointed out on #2892304: Introduce footer region to ContentEntityForm

Proposed resolution

Implement #optional property for the Container render element, in the same manner as Details.

Remaining tasks

Do it
Needs Followup, See #41, #42 and #43.

User interface changes

None

API changes

New method added: Container:preRenderContainer.

Data model changes

None

CommentFileSizeAuthor
#32 actions-optional-property-do-not-test.patch764 bytesManuel Garcia
#32 2893586-32.patch5.51 KBManuel Garcia
#32 interdiff.txt1.33 KBManuel Garcia
#29 2893586-29.patch6.25 KBManuel Garcia
#29 interdiff.txt1.52 KBManuel Garcia
#27 2893586-27.patch6.19 KBManuel Garcia
#27 interdiff.txt792 bytesManuel Garcia
#25 2893586-25.patch6.2 KBManuel Garcia
#25 interdiff.txt467 bytesManuel Garcia
#23 2893586-23.patch6.19 KBManuel Garcia
#23 interdiff.txt3.38 KBManuel Garcia
#20 interdiff.txt1.45 KBamateescu
#20 2893586-20.patch4.86 KBamateescu
#17 2893586-17.patch5.02 KBManuel Garcia
#17 interdiff.txt896 bytesManuel Garcia
#14 2893586-14.patch5.14 KBManuel Garcia
#14 interdiff.txt1.81 KBManuel Garcia
#12 2893586-12.patch5.4 KBManuel Garcia
#12 interdiff.txt1.51 KBManuel Garcia
#9 2893586-9.patch5.78 KBManuel Garcia
#9 interdiff.txt6.3 KBManuel Garcia
#7 2893586-7.patch3.47 KBManuel Garcia
#7 interdiff.txt3.16 KBManuel Garcia
#4 2893586-3.patch1.08 KBManuel Garcia
#4 interdiff.txt928 bytesManuel Garcia
#2 2893586-2.patch883 bytesManuel Garcia
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Manuel Garcia created an issue. See original summary.

Manuel Garcia’s picture

Assigned: Manuel Garcia » Unassigned
Status: Active » Needs review
FileSize
883 bytes

Status: Needs review » Needs work

The last submitted patch, 2: 2893586-2.patch, failed testing. View results

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
928 bytes
1.08 KB

Ok clearly i got too excited. Let's try this again...

amateescu’s picture

Title: Add #optional support to Container render element » Add #optional support to the Container render element
Issue tags: +Needs tests
Related issues: +#2892304: Introduce footer region to ContentEntityForm
+++ b/core/lib/Drupal/Core/Render/Element/Container.php
@@ -79,4 +80,19 @@ public static function processContainer(&$element, FormStateInterface $form_stat
+  public static function preRenderGroup($element) {
+    $element = parent::preRenderGroup($element);
+    // Do not render optional container elements if there are no children.
+    if (isset($element['#parents'])) {
+      $group = implode('][', $element['#parents']);
+      if (!empty($element['#optional']) && !Element::getVisibleChildren($element['#groups'][$group])) {
+        $element['#printed'] = TRUE;
+      }
+    }
+    return $element;
+  }

Let's not abuse the preRenderGroup() method and just add a new preRenderContainer() one like \Drupal\Core\Render\Element\Details::preRenderDetails().

And we also need test coverage :)

amateescu’s picture

Status: Needs review » Needs work
Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
3.16 KB
3.47 KB

Thanks @amateescu for the review, all good points.

Here is a patch addressing #5 and adding a WIP render test.

I believe the test cases are ok, but the test itself is not correct... I only seem to get the result of rendering what's inside the container, but not the wrapping divs around it, any clues how to do this properly? Unfortunately there is no test coverage for Details::preRenderDetails to have a look at.

Status: Needs review » Needs work

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

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
6.3 KB
5.78 KB

Alright I can't figure that one out, so taking a different route now by defining a new BrowserTest instead.

I'm getting a weird InvalidArgumentException: Invalid database prefix: in Drupal\Core\Test\TestDatabase->__construct() (line 81 of /var/www/drupal8/core/lib/Drupal/Core/Test/TestDatabase.php). error locally on all tests making use of form_test module, hoping it's just me, let's see what the bot says...

Manuel Garcia’s picture

Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 9: 2893586-9.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
1.51 KB
5.4 KB

OK at least its only me with the errors. Hopefuly this will come back green.

Any clue why somone would get this error?

$ sudo -u www-data PHP_IDE_CONFIG="serverName=drupal8" SIMPLETEST_DB=mysql://admin:admin@localhost/drupal8_tests SIMPLETEST_BASE_URL=http://drupal8 ../vendor/bin/phpunit -v /var/www/drupal8/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php
[sudo] password for manuel: 
PHPUnit 4.8.35 by Sebastian Bergmann and contributors.

Runtime:	PHP 7.0.18-0ubuntu0.16.04.1 with Xdebug 2.4.0
Configuration:	/var/www/drupal8/core/phpunit.xml.dist

Testing Drupal\Tests\system\Functional\Form\ElementsContainerTest
E

Time: 12.32 seconds, Memory: 6.00MB

There was 1 error:

1) Drupal\Tests\system\Functional\Form\ElementsContainerTest::testOptionalContainerElements
ReflectionException: Class Drupal\form_test\EventSubscriber\FormTestEventSubscriber does not exist

/var/www/drupal8/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterEventSubscribersPass.php:30
/var/www/drupal8/vendor/symfony/dependency-injection/Compiler/Compiler.php:120
/var/www/drupal8/vendor/symfony/dependency-injection/ContainerBuilder.php:573
/var/www/drupal8/core/lib/Drupal/Core/DrupalKernel.php:1299
/var/www/drupal8/core/lib/Drupal/Core/DrupalKernel.php:883
/var/www/drupal8/core/lib/Drupal/Core/DrupalKernel.php:797
/var/www/drupal8/core/lib/Drupal/Core/Extension/ModuleInstaller.php:544
/var/www/drupal8/core/lib/Drupal/Core/Extension/ModuleInstaller.php:195
/var/www/drupal8/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php:83
/var/www/drupal8/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php:419
/var/www/drupal8/core/tests/Drupal/Tests/BrowserTestBase.php:978
/var/www/drupal8/core/tests/Drupal/Tests/BrowserTestBase.php:443

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

Status: Needs review » Needs work

The last submitted patch, 12: 2893586-12.patch, failed testing. View results

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
1.81 KB
5.14 KB

OK this is embarrassing... let's try it again.

Status: Needs review » Needs work

The last submitted patch, 14: 2893586-14.patch, failed testing. View results

Manuel Garcia’s picture

OK the failure seems correct, so it looks like we need to have a look at preRenderContainer.

That said I cannot work on this until I can actually run the test locally (see #12), so essentially I'm blocked.

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
896 bytes
5.02 KB

OK did a bit of thinking, and decided to try this, Container is not the same as Details, so I think doing it like this makes more sense since you can just add child elements directly under the container in the array.

Also, please keep in mind that I am still unable to run this test locally, so I have to rely on the bot's feedback :)

Status: Needs review » Needs work

The last submitted patch, 17: 2893586-17.patch, failed testing. View results

Manuel Garcia’s picture

Perhaps #17 wasn't such a good idea... can't really do much else here sorry, but we should continue from #14.

amateescu’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
4.86 KB
1.45 KB

Actually, the logic in #17 is spot on! The only problem is that the test is missing the point of "visible" children: \Drupal\Core\Render\Element::getVisibleChildren() does not refer to elements which print some markup or not, it is about visible render arrays.

You can see the test coverage from \Drupal\Tests\Core\Render\ElementTest::testVisibleChildren() and \Drupal\Tests\Core\Render\ElementTest::providerVisibleChildren() about what exactly is a visible render element :)

Manuel Garcia’s picture

OH I get it... also had a look at Element::isVisibleElement, which helped finally clear this out for me.

So a visible element has nothing to do with whether or not when you render the element it actually produces any markup. A visible element is just an element who's #type is not hidden, value nor token, and that if it has an #access property, it is (or returns) TRUE.

Thanks so much @amateescu for helping out on this, much appreciated!

I think we're good to go with this now :D

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/Container.php
    @@ -79,4 +81,22 @@ public static function processContainer(&$element, FormStateInterface $form_stat
    +   * @param $element
    ...
    +   * @return
    ...
    +  public static function preRenderContainer($element) {
    

    Nitpick: Let's document its an array.

  2. +++ b/core/lib/Drupal/Core/Render/Element/Container.php
    @@ -79,4 +81,22 @@ public static function processContainer(&$element, FormStateInterface $form_stat
    +    if (!empty($element['#optional']) && !Element::getVisibleChildren($element)) {
    

    This introduces '#optional' as a new feature, so let's add a change record, but also maybe add the default value to \Drupal\Core\Render\Element\Container::getInfo ? On top of that I'm wondering whether we should skip if there is any value already set in '#printed'

  3. +++ b/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php
    @@ -0,0 +1,32 @@
    +    $this->assertSession()->elementNotExists('css', 'div.empty_optional');
    +    $this->assertSession()->elementExists('css', 'div.empty_nonoptional');
    +    $this->assertSession()->elementExists('css', 'div.nonempty_optional');
    +    $this->assertSession()->elementExists('css', 'div.nonempty_nonoptional');
    

    Note: You can store the result of assertSession() in a variable. I've seen @larowlan requesting it before.

  4. \Drupal\Core\Render\Element\Actions::getInfo inherits from container, so should it inherit this pre_render function as well?
Manuel Garcia’s picture

Thanks @dawehner for the review!
#22.1 Done
#22.2 All makes sense to me, Code changes done, change record coming soon.
#22.3 Done
#22.4 Don't see why not, done.

Status: Needs review » Needs work

The last submitted patch, 23: 2893586-23.patch, failed testing. View results

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
467 bytes
6.2 KB

Missing comma... oops!

Added CR: https://www.drupal.org/node/2895127 :)

Status: Needs review » Needs work

The last submitted patch, 25: 2893586-25.patch, failed testing. View results

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
792 bytes
6.19 KB

It's good to have tests. Fixing logic introduced in #23...

vijaycs85’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/Container.php
    @@ -11,6 +12,10 @@
    + * - #optional: Indicates whether the container should not render when it has
    

    probably rephrase to say "should render" instead of "should not"?

  2. +++ b/core/lib/Drupal/Core/Render/Element/Container.php
    @@ -79,4 +86,22 @@ public static function processContainer(&$element, FormStateInterface $form_stat
    +    if (empty($element['#printed']) && !empty($element['#optional']) && !Element::getVisibleChildren($element)) {
    

    Now makes me wonder why don't we have '#printed' check on details (i.e. core/lib/Drupal/Core/Render/Element/Details.php:87)

  3. +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php
    @@ -0,0 +1,59 @@
    +      'child' => [],
    ...
    +      'child' => [],
    

    probably add child like normal do? i.e. $form['container_name']['child_1_name']?

  4. +++ b/core/modules/system/tests/src/Functional/Form/ElementsContainerTest.php
    @@ -0,0 +1,33 @@
    +class ElementsContainerTest extends BrowserTestBase {
    

    Manuel Garcia++ for extensive test coverage.

Manuel Garcia’s picture

Thanks @vijaycs85 for the review!
Re #28:

  1. I forget comments are for humans - good call, done.
  2. Checking #printed prevents unneeded calls to Element::getVisibleChildren(), so we should do that in Details as well. Probably doesn't occur too often but every little bit counts I suppose. Either way, not in scope here =)
  3. I suppose that makes the test easier to understand, done.
  4. Thanks =)
amateescu’s picture

  1. +++ b/core/lib/Drupal/Core/Render/Element/Actions.php
    @@ -32,12 +32,16 @@ class Actions extends Container {
    +      '#optional' => FALSE,
    ...
    +      '#pre_render' => [
    +        [$class, 'preRenderContainer'],
    +      ],
    

    FWIW, I don't agree that we should also bring the Actions element in the scope of this issue.

  2. +++ b/core/lib/Drupal/Core/Render/Element/Container.php
    @@ -79,4 +86,22 @@ public static function processContainer(&$element, FormStateInterface $form_stat
    +   * @param $element
    

    Missed a doxygen spot here :P

Status: Needs review » Needs work

The last submitted patch, 29: 2893586-29.patch, failed testing. View results

Manuel Garcia’s picture

Status: Needs work » Needs review
FileSize
1.33 KB
5.51 KB
764 bytes

Yeah I guess the changes to Actions is of out of scope, and probably very rarely this would have a use-case there. We can always add it later if people want it. Removing those changes here.

I also attach a patch which we could use should we ever want to add the optional property to Actions.

Fixing also #30.2 here.

Manuel Garcia’s picture

Updated the CR removing references to the Actions element.
https://www.drupal.org/node/2895127

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

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now 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.

Manuel Garcia’s picture

Ping...

Sorry to bring this up, but it is sort of blocking #2892304: Introduce footer region to ContentEntityForm, which in turn is making us repeat unnecessary code for published check-boxes (like on #2834546: UI for publishing/unpublishing block_content blocks)... :S

vijaycs85’s picture

Status: Needs review » Reviewed & tested by the community

Thanks @Manuel Garcia. Looks like the review comments in #28 and #30 are addressed and patch is looking good. Let's get this in!

lauriii’s picture

Status: Reviewed & tested by the community » Fixed

It took me a long time to understand this issue because of the issue summary was quite confusing. For anyone revisiting this issue, reading the change record and comments #21 and #22 helped me understand the change better.

Committed cff5bad and pushed to 8.5.x. Thanks!

  • lauriii committed cff5bad on 8.5.x
    Issue #2893586 by Manuel Garcia, amateescu, vijaycs85, dawehner: Add #...
Manuel Garcia’s picture

Apologies @lauriii - I should've updated the description here after #21, thanks for the extra effort in understanding the issue!

Wim Leers’s picture

This actually won't work correctly in all cases: if the sole child is a placeholder, then it is impossible to know when the container is being rendered whether it will be empty.

See #953034: [meta] Themes improperly check renderable arrays when determining visibility.

Was this considered? Should the change record and docs for #optional mention this?

Berdir’s picture

#optional already exists for e.g details elements, I don't see why we have to solve this here. The 90% use case for this is imho within forms and conditional/access-controlled form elements

Manuel Garcia’s picture

Yup details probably suffers from the same bug... should we be patching Element::isVisibleElement to solve it globally? Not sure if that would tackle the problem with the themes that wim mentions. Either way, I don't think we should be fixing this here.

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

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now 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.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now 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.

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.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.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.

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.

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.

quietone’s picture

Issue summary: View changes
Status: Active » Needs work
Issue tags: +Needs followup

Comments #41 and #42 ask for the issue in #41 to be solved in a another issue. Adding tag for a follow up and setting to NW.

quietone’s picture

Version: 9.4.x-dev » 8.5.x-dev
Status: Needs work » Fixed
Issue tags: -Needs followup

Added a followup, #3279600: Add support for a sole child placeholder to the Container render element.

This issue was committed to 8.5.x, restoring the version and returning to fixed.

Thanks.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.