Keats / tera

A template engine for Rust based on Jinja2/Django
http://keats.github.io/tera/
MIT License
3.36k stars 280 forks source link

Allow scoping context in block #888

Closed holly-hacker closed 6 months ago

holly-hacker commented 6 months ago

Given the following context:

{
    sub: {
        my_value: 5,
    }
    my_value: 1,
}

Given a new scope block, I want the following template to output 5:

{% scope sub -%}
{{ my_value }}
{% endscope %}

This would allow for including other templates and scoping a part of the context to it. As an example:

{
    user: "user",
    token_pane: { token: vec![] },
}
<!-- settings_complete.html.tera -->
<h1>Settings for {{user}}</h1>

<h2>Tokens</h2>
{% scope token_pane -%}
{% include "settings_tokens.html.tera" %}
{% endscope %}
<!-- settings_tokens.html.tera -->
<ul>
{% for token in tokens -%}
    <li>{{ token }}</li>
{% endfor %}
</ul>
Keats commented 6 months ago

That's not going to be added. If you really need it you can set new variables or, the preferred version to have isolated context, use a macro.

holly-hacker commented 6 months ago

The documentation also mentioned macros but I don't quite understand how they would solve this usecase. Can you give an example?

Keats commented 6 months ago

Well instead of using an include in the settings_complete.html.tera, you would have the content of settings_tokens.html.tera be a macro if you really want a clean scope.

holly-hacker commented 6 months ago

Yes, but that wouldn't allow me to output settings_tokens separately without having to create an additional nesting level in the context, would it? Ideally, I'd like to be able to output settings_complete and settings_tokens with the following contexts respectively:

{
    token_pane: {
        tokens: vec![]
    },
    // other panes here
}
{
    tokens: vec![]
}
Keats commented 6 months ago

I'm not entirely sure I get the problem. You want to include the same template but depending on the page it might not be at the same key in the context, is that it?

holly-hacker commented 6 months ago

I want one API endpoint to emit the whole page (settings_complete), and another to only emit a subset of the page (settings_tokens). This is what you would do using HTMX, for example. Currently, my settings_tokens needs to accept the following context:

{
    token_pane: {
        tokens: vec![]
    }
}

The entire context for settings_tokens needs to be wrapped in token_pane even if I want to use it on its own (without emitting settings_complete). For this example usecase that's fine-ish, but it leads to problems when there is deeper nesting or when you want to use a child template multiple times or in different places.

If I understand correctly, the proposed solution involving macros would result in 3 files:

Is that correct? It feels a bit unergonomic to handle it this way. The documentation didn't elaborate on how this should go either.

Keats commented 6 months ago

I believe it would be better if there was a way to render a specific macro from the Tera instance without going through the templates. Someone else wanted a method to render a specific block for htmx but blocks are not really safe to render that way.

holly-hacker commented 6 months ago

That would be a viable alternative, yes. Should I re-open this issue or will you create a new one?

Keats commented 6 months ago

I've created an issue on the tera2 repo