Is this normal block behavior in my template?

I have a question about blocks in templates and how they’re implemented:

I have a template that does not have an extends statement. It does have several block statements, such as {% block stylesheets %} and {% block javascripts %}. Let’s call it pagebuild.html.twig

Because it’s part of one module on a modular page, pagebuild.html.twig gets called with {{ module.content|raw }}.

The modular page template contains no block statements, but it extends Quark’s modular.html.twig which does contain block statements.

The block statements in pagebuild.html.twig get successfully implemented. Is that expected behavior?

@pwr, This seems to be a question that’s best resolved by getting a foundation on Twig itself.

According to Twig:

block
Blocks are used for inheritance and act as placeholders and replacements at the same time. They are documented in detail in the documentation for the extends tag.

I would suggest to read the following documentation:

@pamtbaau There’s nothing I can see in the documentation that explains the behavior I outlined.

Documentation as far as I can see refers only to blocks in child templates that include extends.

In the case above, the child template is called directly with {{ module.content|raw }}, and does not extend, yet its blocks are still rendered successfully. I’m wondering why, and if that’s expected or unexpected behavior.

base.html.twig (contains {% block stylesheets %}, {% block javascripts %}, {% block body %} among others)

=> modular.html.twig (extends base.html.twig, contains {% block javascripts %}, {% block body %} and others)

==>modular-extension.html.twig (extends modular.html.twig, contains only {% block content %} and uses {{ module.content|raw }} to call pagebuild.html.twig

===>pagebuild.html.twig (does not extend anything. contains {% block stylesheets %} and {% block javascripts %} which successfully render.

Is that abnormal behavior because pagebuild.html.twig doesn’t extend anything and modular-extension.html.twig doesn’t itself reference the javascript and stylesheets blocks?

Or is it normal behavior because the chain of extended templates, from base thru modular thru modular-extension creates (in a matter of speaking) an extended template that does contain all referenced blocks? That’s what I’m suspecting, but it’s not explicitly outlined in the docs that I can see

@prw, I have been pondering over your question both yesterday and today quite a bit. And I am still confused…

In order to help the reader help you and increase the chance on a response, I would kindly ask you to consider the following:

  • Use an informative title
  • Try to formulate the question as clear as possible.
  • Mention the theme/plugins being used.
  • Use properly formatted folder structures of relevant folders.
    Tip: On Windows 10 you can use $ tree | clip and on Unix $ clip | clip.exe to get a nice folder tree copied to the clipboard.
    templates
    ├── modular
    │   └── pagebuild.html.twig
    ├── ...
    ├── modular.html.twig
    ├── modular-extension.html.twig
    └── partials
        └── base.html.twig
    
    pages
    ├── 01.home
    ├── 02.typography
    └── 03.modular-page
        ├── 01.child-module
        │   └── pagebuild.md
        └── modular-extension.md
    
  • Use code samples properly formatted enclosed by triple back-ticks (```)
    {# modular-extension.html.twig #}
    
    {% extends 'modular.html.twig' %}
    
    {% block content %}
      {{ module.content|raw }}
    {% endblock %}
    
    {# pagebuild.html.twig #}
    
    {% block javascripts %}
      ...
    {% endblock %}
    
  • Show the results of your efforts.
    <script src="/system/assets/jquery/jquery-2.x.min.js"></script>
    
  • In short: I should be able to reproduce your issue if necessary

The examples above express my confused understanding of your question. Did I understand the question correctly?

@pamtbaau, thanks very much for taking the time to look into this with me. You’ve pretty much understood my setup. I’ll produce below the exact relevant trees and code (abbreviated to only what’s necessary) just for clarity:

templates
    ├── modular
    │   └── pagebuild.html.twig
    └── modular-extension.html.twig

but that is the templates folder of a child theme of Quark, so your templates tree was in essence correct.

pages
    └── 01.page
        ├── _01.aside
        │   └── text.md
        ├── _02.main
        │   ├── 01
        │   │   └── text.md
        │   ├── 02
        │   │   └── text.md
        │   ├── 03
        │   │   └── text.md
        │   └── pagebuild.md
        └── modular-extension.md

modular-extension.html.twig:

{% extends 'modular.html.twig' %}

{% block body %}
    <div id="flex-row">
    {% for module in page.collection() %}
        <div class="flex-item {{ module.menu|hyphenize }}"> 
            {% block content %}
                {{ module.content|raw }}
            {% endblock %}
        </div>
    {% endfor %}
    </div>
{% endblock %}

pagebuild.html.twig:

{% block stylesheets %}
    <style>
        body * {color: red !important;}
    </style>
{% endblock %}

{% block javascripts %}
    <script>
        alert( 'Hello, world!' );
    </script>
{% endblock %}

{% block content %}
<div>content goes here</div>
{% endblock %}

{% block bottom %}
    <script>
        alert( 'Hello again, world!' );
    </script>
{% endblock %}

The question is whether the javascript, stylesheet, and bottom blocks in pagebuild.html.twig should be rendering—which they do. And if that is correct, what is the explanation?

Thanks again.

@prw, Thanks for rewriting your setup and filling in the blanks. Much clearer this way!

TL;DR: Yes, blocks ‘javascript’, ‘stylesheet’, and ‘bottom’ render as expected.

As quoted before:

Blocks are used for inheritance and act as placeholders and replacements at the same time.

Blocks are only used for inheritance. Code embedded in ‘block’ tags can (but don’t have to) be overridden by inheriting (child) templates. If overridden, the content of the block tag is replaced (‘replacements’) , else the existing content (‘placeholder’) will be rendered.

Blocks are not needed if the page is not inherited. This means, the ‘block’ tags in template ‘pagebuild.html.twig’ serve no use and could be left out.
The content of the blocks are rendered normally even if not inherited.

So in answer to your question: Yes, blocks ‘javascript’, ‘stylesheet’, and ‘bottom’ render as expected.

Note: Because ‘pagebuild.html.twig’ is not inherited by ‘modular-extension.html.twig’, the ‘content’ block in ‘modular-extension.html.twig’ will override nothing. Twig is very forgiving and will not raise an error.

As a side note: Your folder structure below ‘02._main’ looks odd, both with respect to naming and existance of subfolders. Since ‘pagebuild.md’ is not a modular page I wonder what function they serve…

@pamtbaau, thanks!

This makes sense, however a couple of things still mystify me:

If the javascript and stylesheet blocks serve no use in pagebuild.html.twig, I would expect their content to show up within the page body content, not in the page header. Instead, the content from the javascript block ends up in <script> ... </script> and content from the css block shows up in <style> ... </style>. Is that some kind of forgiving code-cleanup that grav is executing when it finds random style and javascript in the page content?

pagebuild.md is actually a sort-of-modular page (collection?), based on this template by @paulhibbitts from his Quark Open Publishing theme .

@pwr, Did you inspect the source code of the generated page? Everything in ‘pagebuild.html.twig’ ends up in the <body> tag.

I’m a dummy.

In the example I gave you, the javascript and css within their respective blocks actually stood for {% do assets.add('theme://js/bricklayer.min.js') %} and {% do assets.addCss('theme://css/bricklayer.css') %}.

So obviously do.assets.add was responsible for placing the javascript and css correctly in the page. It had nothing to do with blocks.

Would have been easy for you to spot if I hadn’t overlooked it as an overly specific part of my setup “not worth sharing”.

@prw, Not quite…

{% do assets.add('theme://js/bricklayer.min.js') %} does not write the javascript to the page. It only adds the javascript file to the assets manager.

It is {{ assets.js()|raw }} that outputs the <script> tags to the page.

Not sure if I should be pleased with your question skills… :innocent:

Thank you for clarifying that. I understand better.

btw, @pamtbaau sorry for wasting your time with a poorly thought out question!