How can I set a FreeMarker variable with an interpolated value? error: "You can't use "${" here as you are already in FreeMarker-expression-mode."? - spring

I am absolutly new in FreeMarker and I have the following problem working on a Spring MVC application that use this template engine.
So into a controller method I put an int representing the current year (2016) into the model, in this way:
model.addAttribute("annoCorrente", annoCorrente);
Then, into my FreeMarker page I have to assign this value to a variable, so I write the following expression:
<#assign a = ${annoCorrente}>
But in this way I obtain the following error message:
[col. 86] You can't use "${" here as you are already in FreeMarker-expression-mode. Thus, instead of ${myExpression}, just write myExpression. (${...} is only needed where otherwise static text is expected, i.e, outside FreeMarker tags and ${...}-s.)
Why? How can I correctly initizialize a FreeMarker variable with the value obtained from the model associated to this view?

Change <#assign a = ${annoCorrente}> to <#assign a = annoCorrente>
(or you can do <#assign a = "${annoCorrente}"> but this is not recommended)

Related

Freemarker assigning escaped ${expression} to a variable

I need to assign an escaped ${expression} to a variable in Freemarker
From the question here, it's clear that we can escape the $ sign in this way
${r"${expression}"}
This works perfectly outside Freemarker context, but doesnot working inside. I am trying to do
<#assign x = "${r"${expression}"}">
But getting the following error:
Template inclusion failed:
You can't use "${" here as you are already in FreeMarker-expression-mode. Thus, instead of ${myExpression}, just write myExpression. (${...} is only needed where otherwise static text is expected, i.e, outside FreeMarker tags and ${...}-s.)
What is the way to achieve this? Thanks in advance.
I had to spent some time to figure out the following scenarios to escape ${expression} -
In Freemarker assignment:
<#assign var = r"${expression}">
In html attribute:
Some link
In Freemarker concatenation:
<#assign x = "something&"+r"${expression}"/>
Like this:
<#assign x = r"${expression}">

Assigning empty string if XML node doesn't exist in Freemarker

I have an XML document passed as root to a Freemarker template. I want some values from this XML to be assigned to variables as a string and later concatenate/print them out.
<#assign MyVar = root.child1.child2.child3.mynode>
The issue here is that even when a path doesn't exist MyVar gets assigned with a sequence+hash which cannot be printed out or converted to string. This variable although returns false for ?has_content, it needs an extra step for these checks and I have this same issue with many variables and across template files and modules.
The only solution I have been able to find was
<#assign MyVar = root.child1.child2.child3.mynode>
<#assign MyVar = MyVar ?has_content?then(MyVar , "")>
I am looking for something like the Default Value Operator which also checks for nulls like ?has_content.
Does Freemarker provide any simpler one line function to check if a variable has no content and assign it with a default?
In short:
<#assign myVar = root.child1.child2.child3.mynode[0]!''>
Or just <#assign myVar = root.child1.child2.child3.mynode[0]!> if the implicit multi-typed default value doesn't cause problems (like when you just print it with ${}).
Why: XML queries (just like XPath queries) always return a sequence of matching nodes. There are maybe 0 such nodes (or 1, or multiple). An empty sequence is not a "missing value" according the template language. It's an inconvenient mismatch with the XML data-model. But while the sequence always exists, its 1st element ([0]) doesn't, so you can use all the missing value handler operators with it as usual.

Assigning a variable from another variable

I am trying to assign a variable from another variable. My code looks like this
<#macro ctglink c rhs x y z m e b>
<#assign ctg>
<#if ctgroutes["${y}..${x}-${m}"]??>ctgroutes['${y}..${x}-${m}']
<#elseif ctgroutes["${x}..${y}-${m}"]??>ctgroutes['${x}..${y}-${m}']
<#else>{}</#if>
</#assign>
However, this ctg variable is evaluating to just ctgroutes['227..257-TPPMD04X02'] its not actually evaluating the string itself.
I have tried ?eval, and ?interpret and a bunch of other very hacky things to get this to work, no go. Even the {} is a string
Basically, I need the assign function to work like the old PHP eval() function or something. I am trying to access values in a Map whose keys are derived from the state of the data, so I don't see any easy way to query my Map without evaluating keys.
Update:
I forgot to include the elseif in there
Either way, I tried <#assign ctg = ctgroutes["${y}..${x}-${m}"]!ctgroutes["${x}..${y}-${m}"]> but I get the following error:
Caused by: freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> ctgroutes["${y}..${x}-${m}"]!ctgroutes["${x}..${y}-${m}"] [in template "RouteCompare-WptTable.ftlh" at line 5, column 24]
I would like a null result to just return an empty map, however that doesn't seem possible:
Caused by: java.lang.RuntimeException: freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> ctgroutes["${y}..${x}-${m}"]!ctgroutes["${x}..${y}-${m}"] [in template "RouteCompare-WptTable.ftlh" at line 5, column 24]
So basically, my goal is I need to assign a variable that can take 1 of 3 values:
ctgroutes["${y}..${x}-${m}"] // Assuming it is not null
ctgroutes["${x}..${y}-${m}"] // Assuming it is not null
{} // An empty map
What is the best way to do that?
If I understand well what you want to achieve, you can write it like this:
<#assign ctg = ctgroutes["${y}..${x}-${m}"]!ctgroutes["${x}..${y}-${m}"]!{}>
Also note that <#assign target>...</#assign> is for capturing the output printed between the two tags into the target variable (instead of actually printing it). So target will always store a string or markup value. Also things outside FreeMarker tags and ${} are just static text, and won't be parsed. So, the naive but working approach is just using #if/#elseif/#else and have a separate #assign ctg = ... inside each of them, but you can make this much sorter with the ! operator, as it was shown.

FreeMarker Java 8 Optional value support

Does FreeMarker support Optional value in Java 8?
e.g. I have String id, and its getter method is like:
public Optional<String> getId() {
return Optional.ofNullable(Id);
}
How am I going to reference it in the .ftl file. It seems that {data.id} can not find the correct Optional value. But gives me Optional[1334586]
Well, Freemarker is not supposed to be aware of Optional or it is better to say that its dynamically typed so it works for any object.
Since you calling ${data.id} it's just calls toString on Optional which is totally expected behavior.
If you want to handle null values in your template and for that you want to use Optional, you may choose to set a default value if null, so Optional usage won't be needed:
Synopsis: unsafe_expr!default_expr or unsafe_expr! or (unsafe_expr)!default_expr or (unsafe_expr)!
Example: ${data.id!"No id."}
Or check if it's exists:
<#if data?? && data.id??>
Id found
<#else>
Id not found
</#if>
For more info check out the Freemarker docs. Specifically parts: Handling missing values and Missing value test operator.
If you just want to get the value from Optional in your template:
${data.id.orElse('')}
or
${data.id.get()!''}

Access variable of the returning object in Freemarker

I have an addNewRow() method that returns a object which gets displayed through the Freemarker template. There is a scenario where I need to check if one of the fields of the returning object has the value of -1. I tried the following syntax unsuccessfully.
<#if ${Object.field} ==-1>
<#if Object.field ==-1>
How can I access the object variable in this case?

Resources