flosch / pongo2

Django-syntax like template-engine for Go
https://www.schlachter.tech/pongo2
MIT License
2.88k stars 270 forks source link

Accessing parent variable scope #163

Open CaptKelley opened 7 years ago

CaptKelley commented 7 years ago

In the code below, I declare a in the top most context I can, then I have a loop, within which I wish to manipulate that higher-level a and have the value 'stick' for later use in the template. Is there a way to access the parent scope and/or block evaluation until the loop completes and retain the value?

I realize this is likely a race-condition sort of problem, and I'm looking for a good workaround.

<html>
<head>
<title>pongo2 demo</title>
</head>
<body>
Welcome to pongo2 {{ pongo2.version }}! :-)

{% set a = 0 %}
{% for b in "wxyz" %}
  {% set a = a + 1 %}
  {{ a }} Counting up nicely...
{% endfor %}

{{ a }} Is 0, but I want it to be 4...

</body>
</html>
gunnsth commented 5 years ago

I ran into a similar issue:

{% set canonicalUrl = "https://myurl.com" %}
{% block meta_tags %}
<link rel="canonical" href="{{ canonicalUrl }}" />
{% endblock %}

This results in an output href="" as the variable set at the top is not accessible within the block. I have used a similar approach in Jinja2 in the past that worked, but seems like it is not working in pongo2. Is this intended or is it similar to the bug reported above?

der-lyse commented 1 year ago

I also wanted the modification of my template variable in the loop to be visible to the outside of the loop. So I came up with this ugly hack (better don't use it unless you really have to). Create a map as a data holder and define two accessor functions to set and get arbitrary fields. Both functions can be passed in the context to the template:

tmp := map[string]any{}
ctx := pongo2.Context{
    "setvar": func(name string, value any) string {
        tmp[name] = value
        return ""
    },
    "getvar": func(name string) any {
        return tmp[name]
    },
    …
}
…
template.ExecuteWriterUnbuffered(ctx, w)

They can be used in the template to make @CaptKelley's example work:

{{ setvar("a", 0) }}
{% for b in "wxyz" %}
    {{ setvar("a", getvar("a") + 1) }}
    {{ getvar("a") }} Counting up nicely...
{% endfor %}

{{ getvar("a") }} Is now actually 4!

Please note the use of {{ instead of {% around setvar(…). Maybe there is an even better way, I don't know. I just came across pongo2 the other day and I'm loving it so far. Thank you very much! :-)