Marco Bolis
Marco Bolis

Reputation: 1220

Freemarker - loop variables not surviving nested macro call

I have the following freemarker template:

[#local snippet ][#noparse]
        [#assign out ]value: ${v}
        [/#assign]
    [/#noparse]
[/#local]
[#assign hook = snippet?interpret ]
...
[#macro trigger ]
    [@hook /]
[/#macro]
...
[#list values as v ]
    [@trigger ]
[/#list]
${out}

What it essentially does is defining a hooks to execute at a certain later moment and a macro to trigger it's execution.

When I try to render this template, i get the following error:

The following has evaluated to null or missing:
==> v  [in template "xxx.ftl->anonymous_interpreted" at line 1, column 17]

It might be interesting to note that the following:

[#list values as v ]
    [@hook ]
[/#list]

is just working, i.e. rendering the template as I would expect, printing value: xxx for each value in the list.

EDIT

I just discovered that the error doesn't occur if instead of an interpreted snippet I pass a regular macro:

[#assign hook = myMacro ]
...

but not if the macro is defined in another namespace.

Upvotes: 1

Views: 1333

Answers (1)

ddekany
ddekany

Reputation: 31112

A loop variable (v) is basically a local variable of the #list block. So, with Java-ish pseudo-code what you try to do is like:

void main() {
   for (int v : values) {
      trigger();
   }
}

void trigger() {
   print(v);  // Error, v is not visible here!
}

As of calling a directive defined with ?interpret, that's special, as it tries to behave as if you have copy-pasted the code snippet at the place of invocation. That is, the directive that you call with @hook doesn't create its own local context. So that's why it sees v if you call it directly from the loop. But if you call it form trigger, then it will live in the local context of the trigger macro, which do have its own local context.

As of making it work... one possibility is assigning v to a namespace-scope variable via <#assign value = v>, and then refer to value in the interpreted fragment. Another solution is of course getting rid of the trigger indirection, if you can. Yet another solution is to define trigger with ?interpret instead of with #macro, because then trigger will see v, and so hook will too.

Upvotes: 3

Related Questions