Move views-element-container to Twig template

Created on 9 May 2016, almost 9 years ago
Updated 18 September 2023, over 1 year ago

Problem/Motivation

Views module is adding an extra <div> element with class "views-element-container" for no apparent reason when rendering a view block or EVA attachment. As if having the wrapper from both the block and views-view templates wasn't enough, there is a third coming through via the Views element pre-render callback, which sets #theme_wrappers container:

<?php
  if (empty($view->display_handler->getPluginDefinition()['returns_response'])) {
    $element['#attributes']['class'][] = 'views-element-container';
    $element['#theme_wrappers'] = array('container');
  }
<div class="block block-blah-blah">
  <div class="views-element-container">
    <div class="view view-name blah-blah">
      ...
    </div> 
  </div>
</div>

This extra div is added any time ::buildRenderable() is called, such as this custom code:

$view_id = 'someview';
$view_display = 'block_1';
if ($view = Views::getView($view_id)) {
  $view->setDisplay($view_display);
  $view->execute();
  $results = $view->buildRenderable($view_display);
}

Site Studio Views Element module is also affected.

Since Drupal 8, classes and divs should preferentially be added in Twig templates when possible, but because this div and class are added in a preRender function, it makes things very difficult to override and/or remove.

Proposed resolution

Move the div.views-element-container out of the View::preRenderViewElement function and into a new Twig template views-element-container.html.twig which outputs a div + classname by default. This way, we maintain backward compatibility for existing sites while also allowing this otherwise useless wrapper div to be removed by front end developers at the theme level via a template override.

Developers will then be able to fix this in themes/THEMENAME/templates/views/views-element-container.html.twig with the following template override:

{# 
/**
 * @file
 *
 * Override views element container to reduce the divitis.
 */
#}
{# <div{{attributes.addClass('views-element-container')}}> #}
{{ children }}
{# </div> #}

Note: comment #18 on #2575405-18: Is the

with class "views-element-container" around views pages needed? → identified one possible use case for this wrapper having to do with #states api to conditionally hide/show form elements based on presence of a view. It might make sense to determine if this is a valid use case or only speculative. In order to add a TEST for this to ensure that moving into a Twig template will not break anything.

Remaining tasks

TBD

User interface changes

None.

API changes

None.

Data model changes

None.

🐛 Bug report
Status

Needs work

Version

11.0 🔥

Component
Views 

Last updated about 2 hours ago

Created by

🇺🇸United States jacine New York

Live updates comments and jobs are added and updated live.
  • Needs change record

    A change record needs to be drafted before an issue is committed. Note: Change records used to be called change notifications.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

    Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

  • 🇮🇳India ranjith_kumar_k_u Kerala

    Re-rolled #52

  • Status changed to Needs review about 2 years ago
  • Status changed to Needs work about 2 years ago
  • 🇺🇸United States smustgrave

    That todo may need to be added back. Was the plan to get in for D8 and removed in D9? Would the plan be the same just different versions?

    Can't tell if the issue summary update ever happened but it was tagged for that + change record.

  • Status changed to Needs review about 2 years ago
  • Added back * @todo to be removed altogether in 9.x to #58

  • Status changed to Needs work about 2 years ago
  • 🇺🇸United States smustgrave

    Still needs an issue summary and #58 was an open question not be implemented. Why would we remove in 9 when 9 is EOL?

  • 🇫🇷France dqd London | N.Y.C | Paris | Hamburg | Berlin

    @#44 thanks for finally one report back then :-) much appreciated. after my last patches a lot of time has gone so I am happy to see more progress on it lately again. Thanks for all the work on it!

  • 🇪🇨Ecuador jwilson3

    Issue summary updated.

    And here are a couple more things for context:

    1. This problem isn't limited to blocks and EVA, but literally any time ::buildRenderable() is called, such as this custom code:
      $view_id = 'someview';
      $view_display = 'block_1';
      if ($view = Views::getView($view_id)) {
        $view->setDisplay($view_display);
        $view->execute();
        $results = $view->buildRenderable($view_display);
      }
      

      The Site Studio Views Element module is also affected.

    2. There is an issue that predates this one, whereby someone had done some investigation that identified one possible use case for this wrapper having to do with #states api to conditionally hide/show form elements based on presence of a view.

      See comment #18 here: #2575405-18: Is the

      with class "views-element-container" around views pages needed? → .

      I'm not 100% certain where this #states api use case came from or whether core or contrib uses it in any way, or if that use case is purely speculative.

    Therefore, I suggest we:

    • Consider closing this issue as a duplicate of #2575405, and moving latest patches, and issue credit to the older issue.
    • Combine forces and refocus the effort around first moving the div+class out of a preRender into a Twig template where it belongs because A) theming this to override and remove it from the preRender is really hard, and B) HTML DOM and classes are supposed to be in templates as a best practice since Drupal 8.
    • Then, determine if the #states api use case is a valid use-case, because it will dictate whether we need to introduce a new TEST that actually leverages the #states api to prove that moving this to a Twig template doesn't break that functionality. Sorry. :(
    • Finally, discuss whether we can mark the template and functionality as deprecated in a follow-up commit and remove it in next major version (Drupal 11 at this point) with an appropriate change record, eg to show how code that leverages #states api can be reworked (if it is determined that this is a real use case).
  • Status changed to Needs review over 1 year ago
  • last update over 1 year ago
    30,146 pass
  • 🇪🇨Ecuador jwilson3

    I've updated the 9.x to 11.x to address #60, however... since Drupal dev branch naming has changed, the @todo as written is no longer intuitive, because 11.x branch is where Drupal 10 development is taking place. ¯\_(ツ)_/¯

    Maybe it should say:

    * @todo to be removed altogether in next major version

  • last update over 1 year ago
    30,146 pass
  • 🇪🇨Ecuador jwilson3

    I decided to go with:

     * @todo Remove as part of https://www.drupal.org/node/2721899
    

    To avoid more needless rerolls in the future ;)

  • 🇪🇨Ecuador jwilson3

    Grammar fix issue title

  • 🇪🇨Ecuador jwilson3

    Re #63:

    discuss whether we can mark the template and functionality as deprecated and remove it in next major version with an appropriate change record, eg to show how code that leverages #states api can be reworked (if it is determined that this is a real use case)

    .

    According to comment #52 on the other issue #2575405-52: Is the

    with class "views-element-container" around views pages needed? → , @xjm (core maintainer) is not on board with deprecating this div+class for several reasons unrelated to the #states api use case also identified on that issue:

    Overall, speaking both as a release manager and a Views maintainer, I think this change is a massive disruption and probably wontfix.

    Views deliberately errs on the side of providing lots of wrappers for the content, so that it's easy for the implementation to theme it however they want. Sites that are concerned about divitis can always override the theming to not include the div.

    Emphasis in bold is my own and represents the solution on this issue.

    So maybe we just leave it at that, and remove the @todo completely.

  • last update over 1 year ago
    30,146 pass
  • 🇪🇨Ecuador jwilson3

    Patch update to address #67.

  • 🇪🇨Ecuador jwilson3

    Patch in #68 (incorrectly named as patch number 69) was to address #67. Leaving a comment here to justify it.

  • Status changed to Needs work over 1 year ago
  • 🇳🇱Netherlands Lendude Amsterdam
    +++ b/core/modules/views/tests/src/Functional/ViewElementTest.php
    @@ -40,8 +40,6 @@ public function testViewElement() {
    -    // Verify that the view container has been found on the form.
    -    $this->assertSession()->elementExists('xpath', '//div[@class="views-element-container js-form-wrapper form-wrapper"]');
    
    @@ -80,8 +78,6 @@ public function testViewElementEmbed() {
    -    // Verify that the view container has been found on the form.
    -    $this->assertSession()->elementExists('xpath', '//div[@class="views-element-container js-form-wrapper form-wrapper"]');
    

    If these changes to existing tests are needed, that seems to imply that we are changing the output with the current fix, while the whole idea of doing it this way was that existing sites would not be affected. Are these test changes really needed, and if so, I'm wondering why.

  • Status changed to Needs review over 1 year ago
  • last update over 1 year ago
    30,148 pass, 2 fail
  • 🇪🇨Ecuador jwilson3

    Excellent point.

    Let's find out.

  • Status changed to Needs work over 1 year ago
  • 🇪🇨Ecuador jwilson3

    1.- It looks like tests are not passing so this needs more work.

    2.-

    Are these test changes really needed, and if so, I'm wondering why.

    IMO, yes, as stated in issue summary:

    Since Drupal 8, classes and divs should preferentially be added in Twig templates when possible, but because this div and class are added in a preRender function, it makes things very difficult to override and/or remove.

    The only known fix for this is via a long-standing, somewhat hacky workaround from @idiaz.roncero:

    https://gist.github.com/idiazroncero/34425dbed9df73a44046252582ac1689

    Copy the following into YOURTHEME/templates/form/container.html.twig

    {%
      set classes = [
        has_parent ? 'js-form-wrapper',
        has_parent ? 'form-wrapper',
      ]
    %}
    
    {# Remove unnecessary <div class="views-element-container"> wrapper #}
    {% if attributes.hasClass('views-element-container') %}
      {{ children }}
    {% else %}
      <div{{ attributes.addClass(classes) }}></div>{{ children }}</div>
    {% endif %}
    

    Hacky because writing one-off exceptions in Twig like that is not really an intuitive approach.

  • last update over 1 year ago
    29,683 pass
Production build 0.71.5 2024