2

I have what is probably a common question ... How do render arrays get parsed into the the twig template - If we have a controller class which outputs a render array, such as:

$output['element1']=array ("#type"=>"markup","#markup"=>"Hello World!");
$output['element2']=array ("#type"=>"markup","#markup"=>"Today is a nice day!");

Now, lets say that we're using the bootstrap framework, hence using row and columns for layout, if we want the array elements to be output in the following manner:

<div class="row">
    <div class="col-md-12">
    Hello World!
    </div>
</div>
<div class="row">
    <div class="col-md-12">
    Today is a nice day!
    </div>
</div>    

How can this be done? It is clear that Drupal will initially use the page.html.twig template, which will call the region.html.twig template, which in turn will call the block.html.twig template in order to display the render array. But, the block.html.twig will output the render array as:

 Hello World! Today is a nice day!

Namely,it will not output the div tags - mostly because the block.html.twig doesn't know about them. And looking at the block.html.twig it appears that the output is only available as 'content':

 {{ content}}

And there is no way of templating the content in the following way:

 <div class="row">
     <div class="col-md-12">
        {{ content['element1'] }}
     </div>
 </div>
 <div class="row">
     <div class="col-md-12">
        {{ content['element2'] }}
     </div>
 </div>

Secondary related query:

What would also be useful, is being able to ascertain which class called the page.html.twig so that one could use the {% if calling_page="page name" %} in the block.html.twig template

Hopefully, you get what I am outining here. Thanks in advance and please operate upon the premise I'm totally new to Drupal when outlining solutions.

4

I can answer the first part of your question...

Drupal uses render arrays for the different outputs. Since you only specify two single dimension arrays with #markup they are rendered as 2 strings.

There are multiple ways to achieve what you want, I will highlight 2;

  • Use #prefix and #suffix:

    $output['element1']=array (
      "#type"=>"markup",
      "#markup"=>"Hello World!"
      "#prefix"=>'<div class="row"><div class="col-md-12">',
      "#suffix"=>'</div></div>',
    );
    
  • Use nested elements:

    $output['element1']=array (
      'row' => array(
        '#prefix' => '<div class="row">',
        '#suffix' => '</div>',
        'cols' => array( // This has no #, so is the next level of elements
          '#markup' => t('Hello World!'),
          '#prefix' => '<div class="col-md-12">',
          '#suffix' => '</div>',
        ),
      ),
    );
    

See this related issue: How to nest elements in a render array.

However, there might be cases where it is not possible to alter the output of modules. Instead you want to do this in the theme. Your suggestion was not that bad. It is just that in twig arrays are noted with a dot symbol. {{ content['element1'] }} should be {{ content.element1 }}

<div class="row">
     <div class="col-md-12">
        {{ content.element1 }}
     </div>
 </div>
 <div class="row">
     <div class="col-md-12">
        {{ content.element2 }}
     </div>
 </div>
1

there is no way of templating the content in the following way

Sure there is.

You can create a block--block-type.html.twig template that corresponds to this particular block type.

<div class="row">
   <div class="col-md-12">
     {{ content.field_one }}
   </div>
</div>
<div class="row">
  <div class="col-md-12">
     {{ content.field_two }}
  </div>
</div>

Where field_one etc is the name of a field attached to the block type. Another common thing I have seen with this (Bootstrap) is that some people add fields to the block type denoting the column width. IE a select field with Small, Medium, Large, who's keys are col-sm-12, col, md-12, col-lg-12 and what have you.

<div class="row">
   <div class="{{ content.field_col_one_size }}">
     {{ content.field_one }}
   </div>
</div>
<div class="row">
  <div class="{{ content.field_col_two_size }}">
     {{ content.field_two }}
  </div>
</div>

This would mean you need to display the field with its key value, and override its template:

field--field-col-one-size.html.twig

{% for item in items %}
  {{ item.content }}
{% endfor %}

I don't know what you mean by 'calling page' though, or why that is necessary.

I haven't seen the rest of the code, but you can also render using your own theme function and twig template too in a custom controller.

To be honest, a lot of this is upfront strategy and planning when architecting content types, block types, paragraph types or other content entities. It helps you roadmap templates and how to handle how and what will render their output.

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.