What does the ! (exclamation point) in FreeMarker do? - freemarker

I keep seeing exclamation points at the end of FreeMarker code in Magnolia code examples. For example:
${content.header!}
What is the exclamation point called and what does it do?

The exclamation point is called a default value operator. It's used to set a default value when an interpolation (${...}) returns null. If no default value is set, it returns an empty string ("").
${content.header!}
<#-- Returns "" if content.header is null -->
${content.header!"Example Header"}
<#-- Returns "Example Header" if content.header is null -->
See Dealing with missing variables for more info.

Related

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.

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()!''}

Freemarker - default value for variable that may be missing or blank?

In Freemarker, I want to treat something that may be missing (not defined on given object or in given Map), have a null value, be an empty string "" or a string with whitespace only (" \t\n"), AKA blank string. Or a real value like "foo".
In case it's anything besides a non-blank string, I want the expression to return a default value.
How can I handle this in Freemarker?
So far, this is what I use:
${ obj.propery???then(obj.property?trim!"default") }
But I can imagine something more ellegant, like
${ obj.property!!?trim!"default" }
and even with trimming (which is quite common operation for templates):
${ obj.property!!!"default" }
Is there something such in Freemarker? (Besides coding my own method or macro.)
There's nothing in FreeMarker for that, at least not in 2.3.24. I think the best way to address that is wiriting a trimToNull function, and then you can write trimToNull(obj.property)!"default" and trimToNull(obj.property)??, etc.
I had the same challenge and came to this solution:
${ (obj.property!"")?trim }
It also trims the empty string but is easier to read than writing an function or a if.

Check if the node value exist

I want to check in a xml if there is a node with the value "Hotel Hafen Hamburg".
But I get the error.
SimpleXMLElement::xpath(): Invalid predicate on line 25
You can view the xml here.
http://de.sourcepod.com/dkdtrb22-19748
Until now I have written the following code.
$apiUmgebungUrl = "xml.xml";
$xml_umgebung = simplexml_load_file($apiUmgebungUrl);
echo $nameexist = $xml_umgebung->xpath('boolean(//result/name[#Hotel Hafen Hamburg');
It seems that your parantheses and brackets do not close properly at the end of your XPath expression - it should end on ]).
Also, what is Hotel Hafen Hamburg? If it is an attribute called value, your value check should look like this:
[#value="Hotel Hafen Hamburg"]
You cannot just write # and then a value, without specifying where that value is supposed to be.
EDIT: Looking at the Xml document, it seems that Hotel Hafen Hamburg is supposed to be the text content of the <name> element. Therefore, try looking for a text node with that value rather than an attribute:
boolean(//result/name[text() = "Hotel Hafen Hamburg"])

Freemarker: assign interpreted variable to other variable

I would like to assign the result of an interpreted variable to another varialbe.
Freemarker provides the built-in ?interpret to interpret a variable holding an ftl expression. See http://freemarker.sourceforge.net/docs/ref_builtins_expert.html#ref_builtin_interpret
If I do
[#if var1?has_content && var1?starts_with(r"${")]
[#assign interpretedValue = var1?interpret!""]
[#interpretedValue/]
[/#if]
The [#interpretedValue/] will output the interpreted value.
However, I'd like to assign the value of the interpreted value to a variable (in order to do some things such as ?has_content in the rest of my code). I tried [#assign varInterpretedValue = #interpretedValue] but this does not work.
Is this possible?
Yes, like this:
[#assign capturedOutput][#(var1!'')?interpret /][/#assign]
${capturedOutput} [#-- Attention! Put this into #noescape if you are inside #escape! --]
Note that the !'' suff has to before the ?interpret, otherwise it doesn't do anything (since the result of ?interpret is always non-null).

Resources