Freemarker nested macros - freemarker

How can I call nested macros as such?
<#replaceA-sToB-s>
<#replaceB-sToC-s Text/>
</#replaceA-sToB-s>
replaceB-sToC-s simply replaces any "B" with "C"
replaceA-sToB-s simply replaces any "A" with "B"

If you want to pass the result of replaceB-sToC-s macro to the replaceA-sToB-s then you have to use something like this:
<#assign str><#replaceB-sToC-s "abc" /></#assign>
<#replaceA-sToB-s str />

Like this:
<#macro replaceAsToBs>
<#local captured><#nested></#local>
${captured?replace('a', 'b')}<#t>
</#macro>
<#macro replaceBsToCs text>
${text?replace('b', 'c')}<#t>
</#macro>
(The #t-s are only there to remove the extra whitespace around the ${...} parts.) And then you call it like:
<#replaceAsToBs>
<#replaceBsToCs "abcd"/>
</#replaceAsToBs>

Related

Check if FreeMarker #nested directive is empty

I want to output tags around a <#nested> directive in a macro, but only if it would actually output something. The actual use case is more complicated, this is just the broken down version.
How do I check for existence of <#nested> content?
<#macro opt tagname>
<#if (#nested)??> <-- what do I need to put here
<${tagname}>
<#nested>
</${tagname}>
</#if>
</#macro>
Example 1
Template: <#opt hello />
Output: (empty)
Example 2
Template: <#opt hello>goodbye</#opt>
Output: <hello>goodbye</hello>
You have to capture the nested content, and then print it if necessary. Like this (this assumes auto-escaping on):
<#macro opt tagname>
<#local nestedContent><#nested></#local>
<#if nestedContent?has_content>
<${tagname}>${nestedContent}</${tagname}>
</#if>
</#macro>
Without auto-escaping the #if changes to just <#if nestedContent != ''>.

Trying to generate a string that becomes a variable name in Freemarker or a "derived variable name"

I'm using a data-directive or list to pull in several values, of which, I don't know how many there will be, and I want to try and list through them, and create several variables in the list if you will.
<#list 1..10 as x>
<#-- the next line doesn't work, but what i'm trying to fix -->
<#assign .vars['VAR'+x?string] = rand(100) />
</#list>
But I can list them back out that way.
<#list 1..10 as x>
${.vars['VAR'+x?string]}
</#list>
The documentation for assign, says:
name: name of the variable. It is not expression. However, it can be
written as a string literal, which is useful if the variable name
contains reserved characters, for example <#assign "foo-bar" = 1>.
Note that this string literal does not expand interpolations (as
"${foo}").
Is there no way around this? Am I trying to do the impossible? Is there some way I can insert a derived name into the .vars... Hash is it?
A little more research that was close, but didn't get me there:
This prevoius question gives how to READ the derived variable, but I need to WRITE/CREATE the derived variable.
FreeMarker get variable value by concatenating another variable value
This prevoius question shows that I can use a string to assign a variable and re-iterates what we saw in the first link.
Variable name in Freemarker Template Language
As FreeMarker can't assign to elements of collections (but you can ?map(it -> ...) a collection to another), the only way is via ?interpret:
<#list 1..10 as x>
<#-- the next line doesn't work, but what i'm trying to fix -->
<#'<#assign VAR${x?c} = rand(100)>'?interpret />
</#list>
I wonder why do you need to assign to a dynamically named variables though, since reading dynamically named variables is also a pain, even if a lesser one.
Ultimately, I believe the correct way to phrase my solution was a sequence of hashes:
<#list 1..z_Coupon_Pet.NUM_COUPONS as x>
<#assign INSTORE_COUPON=call_coupon_from_table1() />
<#assign ONLINE_COUPON=call_coupon_from_table2() />
<#assign coupon_string_row= '{
"COUPON_NUM" : ${x},
"INSTORE" : "${INSTORE_COUPON?js_string}",
"ONLINE" : "${ONLINE_COUPON?js_string}"
}' />
<#if x==1>
<#assign coupon_hash_string = coupon_string_row />
<#else>
<#assign coupon_hash_string = coupon_hash_string + ',' + coupon_string_row />
</#if>
</#list>
</#if>
<#if coupon_hash_string?has_content>
<#assign coupon_hash=parsejson('[' + coupon_hash_string + ']') />
</#if>
We specifically avoid <#assign my_hash = my_hash + element /> because of this note in the documentation:
Note that hash concatenation is not to be used for many repeated concatenations, like for adding items to a hash inside a loop. While adding together hashes is fast and is constant time (independent of the size of the hashes added), the resulting hash is a bit slower to read than the hashes added together. Thus after tens... of additions the result can be impractically slow to read.

How do you call a function within a FreeMarker macro?

Is there a way to call a function within a Freemarker macro?
Example of what I am trying to do:
<#function foo>
<return "hello">
</#function>
<#macro fooMacro>
${foo()}
</#macro>
<#fooMacro> <#-- This doesn't work for me
"The "foo()" has evaluated to null or missing" -->
Similarly, is there a way to pass the output of a function into a param of a macro? Like:
<#macro fooMacro p1>
${p1}
</#macro>
<#fooMacro p1=foo()/> <!-- Also doesn't seem to work for me -->
Answer: both are possible, just change
<return "hello"> ==> <#return "hello">

Creating a freemarker macro with optional nested content?

What is the proper way to create a macro that can be used with or without nested content? e.g.
<#myMacro/>
<#myMacro>my nested content</#myMacro>
Is there something like this? Or something else?
<#macro myMacro>
<#if ??????>
Before nested content
<#nested/>
After nested content
<#else/>
Nothing nested here
</#if>
</#macro>
The nested content is assigned to a variable and then this variable is checked if it has any content. The variable is then sent to the output instead of using another nested directive to avoid the content being processed twice.
<#macro myMacro>
<#assign nested><#nested/></#assign>
<#if nested?has_content>
Before nested content
${nested}
After nested content
<#else/>
Nothing nested here
</#if>
</#macro>

freemarker how to use split in string which is readed with include

<#assign reasonValue="xxx.ftl">
and I call it like:
<#include "${reasonValue}">
and I get output like:
Rejected - Something
how can I now use split on this ouput because I would like to get just Something as output
I tried:
<#list "${reasonValue}"?split("-") as sValue>
${sValue}
</#list>
but problem is that instead of real value i get name of ftl file...
Assign output of include to some variable and then use split on this variable.
<#assign xx>
<#include reasonValue>
</#assign>
<#list xx?split("-") as sValue>
${sValue}
</#list>
If you need to show only part of the string after "-" then use substring and index_of.
${xx?substring(xx?index_of("-") + 2)}

Resources