10

When dealing with a renderable array in PHP, I can use Element::children() to access the elements that are not # properties but subordinated renderable elements (form items inside a fieldset, items inside a field widget, etc). For instance, this snippet from file.module:

<?php
if ($element['#multiple']) {
  foreach (Element::children($element) as $name) {
    // ...
  }
}
?>

How can I do the same in a Twig template? If I do {% for child in element %}, it will include also #type, #cache etc.

18
{% for key, child in element if key|first != '#' %}
  <div>{{ child }}</div>
{% endfor %}
  • 2
    Please avoid code-only answers. – Mołot Sep 17 '15 at 11:59
2

I have created a Twig filter that returns back with the children as an ArrayIterator.

mymodule/mymodule.services.yml

services:
  mymodule.twig_extension:
    arguments: ['@renderer']
    class: Drupal\mymodule\TwigExtension\Children
    tags:
      - { name: twig.extension }


mymodule/src/TwigExtension/Children.php

<?php

namespace Drupal\mymodule\TwigExtension;


class Children extends \Twig_Extension
{

  /**
   * Generates a list of all Twig filters that this extension defines.
   */
  public function getFilters()
  {
    return [
      new \Twig_SimpleFilter('children', array($this, 'children')),
    ];
  }


  /**
   * Gets a unique identifier for this Twig extension.
   */
  public function getName()
  {
    return 'mymodule.twig_extension';
  }


  /**
   * Get the children of a field (FieldItemList)
   */
  public static function Children($variable)
  {
    if (!empty($variable['#items'])
      && $variable['#items']->count() > 0
    ) {
      return $variable['#items']->getIterator();
    }

    return null;
  }

}


in the Twig template:

{% for headline in entity.field_headline|children %}
  {{ headline.get('value').getValue() }}
{% endfor %}
2

Use the Twig Tweak module, which, among other wonderful features, has a "children" filter:

{% for item in content.field_name | children(true) %}
  {# loop.length, loop.revindex, loop.revindex0, and loop.last are now available #}
{% endfor %}
1

Here's a modification of https://drupal.stackexchange.com/a/236408/67965 that loops through the render children instead of the field #items.

The twig extension:

/**
 * Generates a list of all Twig filters that this extension defines.
 */
public function getFilters() {
  return [
    new \Twig_SimpleFilter('children', array($this, 'children')),
  ];
}

/**
 * Get the render children of a field
 */
public static function children($variable) {
  return array_filter(
    $variable, 
    function($k) { return (is_numeric($k) || (strpos($k, '#') !== 0)); },
    ARRAY_FILTER_USE_KEY
  );
}

In twig, you can then pass through rendered children directly, which helps in atomic design patterns. Define an entity template, for instance:

{% include '@molecules/grid.html.twig' with { 
   head : content.field_title,
   grid_columns: content.field_collection_items|children
} %}

where grid.html.twig is something like:

{% if head %}
<div class="slab__wrapper">
  {{ head }}
</div>
{% endif %}
<div class="grid">          
  {% for col in grid_columns %}
  <div class="grid__column">
    {{ col }}
  </div>
  {% endfor %}
</div>

This is usually more useful than having to render a field template for {{ content.field_collection_items }} because the layout of the children may be controlled in the context of the parent design element.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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