Shahaf
Shahaf

Reputation: 443

Does jinja support multiple blocks in a macro?

I'm using flask with jinja.

I know that you can define a base page template with multiple placeholder blocks:

<html>
    <head>
        [ standard meta tags, etc go here ]
        {% block css %}{% endblock %}
    </head>
    <body>
        [ standard page header goes here ]
        {% block content %}{% endblock %}
        [ standard page footer goes here ]
        {% block javascript %}{% endblock %}
    </body>
</html>

And I know that you can define a macro with a single placeholder:

{% macro dialog() %}
    <div class="dialog">
        [ standard dialog header ]
        {{ caller() }}
    </div>
{% endmacro %}

{% call dialog() %}
    <div class="log-in">
        Log in or sign up! (etc.)
    </div>
{% endcall %}

But is it possible to define a macro with multiple placeholder blocks?

Upvotes: 6

Views: 2131

Answers (3)

Thanks to muffel, I modified his answer and figured out this

{# macro defined file #}

{% macro card_block() %}

    <div class="card block-use">
        <div class="card-header">
            {{ caller('header') }}
        </div>

        <div class="card-body">
            {{ caller('body') }}
        </div>
    </div>
{% endmacro %}

Now we can use it template

{# template page #}

{% from 'modules/components/card-block.html' import card_block %}

{% call(content) card_block() %}
                
    {% if content == 'header' %}
        <div>Header</div>
    {% endif %}

    {% if content == 'body' %}
        <div>Body</div>
    {% endif %}

{% endcall %}

Output like

<div class="card block-use">
    <div class="card-header">
        <div>Header</div>
    </div>

    <div class="card-body">
        <div>Body</div>
    </div>
</div>

Upvotes: 0

neuront
neuront

Reputation: 9622

@muffle 's answer is great. However if you don't want your callers to be too complicated since you have provided a standard header by default, you can pass another macro for the header part as its argument.

import jinja2


environment = jinja2.Environment()
template = environment.from_string('''
{% macro dialog(dialog_header) %}
    {% if dialog_header %}
        {{ dialog_header() }}
    {% else %}
        Default Header
    {% endif %}

    {{ caller() }}
{% endmacro %}

---- A
{% call dialog() %}
    Dialog body A.
{% endcall %}
---- END-OF-A

---- B
{% macro dialog_header_b() %}
    Header B
{% endmacro %}

{% call dialog(dialog_header_b) %}
    Dialog body B.
{% endcall %}
---- END-OF-B
''')
print(template.render())

Output (with blank lines trimmed)

---- A
    Default Header
    Dialog body A.
---- END-OF-A

---- B
    Header B
    Dialog body B.
---- END-OF-B

(Works with Jinja2==3.0.3)

Upvotes: 0

muffel
muffel

Reputation: 7370

No, you can't. While you can pass several parameters to the macro, only one caller can exist. You can nevertheless pass a parameter back from your macro to the calling context and simulate your desired behavior like this:

{% macro twoblocks()%}
    <div class="content-a">
        {{ caller(True) }}
    </div>
    <div class="content-b">
        {{ caller(False) }}
    </div>
{% endmacro %}

{% call(isA) twoblocks() %}
    {% if isA %}
        A content
    {% else %}
        B content
    {% endif %}
{% endcall %}

Upvotes: 5

Related Questions