Expand a boolean variable to the string "true" or "false" - freemarker

In a freemarker template I want to expand a boolean variable to a string like that:
<#assign booleanVar = "test".isEmpty() />
state: ${booleanVar} <#-- this throws an exception! -->
This is what I want to get as output:
state: false
The only way I found to reach this goal by now is:
state: <#if booleanVar>true<#else>false</#if>
Is there a easier way to do it?

booleanVar?string("true", "false")
Although true/false is default, so
booleanVar?string
should work fine.

Starting from FreeMarker 2.3.20, if you want to print true/false (because you are generating JavaScript or such), write ${booleanVar?c} (?c for "computer format", also used for numbers). ${booleanVar?string} is dangerous for that, since somebody can set the boolean_format setting to yes,no or something... (BTW, in that case ${booleanVar} will work too in 2.3.20, and you get yes and no.)
See: http://freemarker.org/docs/ref_builtins_boolean.html#ref_builtin_c_boolean

Related

Freemarker not entering if/else if values are null

<#if myVar = "test">
A
<#else>
B
<#/if>
If myVar is null / not defined, neither A nor B will be output. This is fixed by adding ! after the variable:
<#if myVar! = "test">
A
<#else>
B
<#/if>
Is this intended? Because if so, it is extremely confusing and I can't imagine any user would expect it to behave this way. It is suggesting that null = "test" is somehow neither true nor false. It seems clear that null = "test" should always be considered false -- what am I missing?
Note: we have configured Freemarker to replace unknown variables with an empty string:
config.setTemplateExceptionHandler((ex, environment, out) ->
{
if (ex instanceof InvalidReferenceException)
{
try
{
out.write("");
}
catch (IOException e)
{
throw new TemplateException("Error while handling template exception.", e, environment);
}
}
else
{
throw ex;
}
});
So this would (at least I thought it would) end up being:
<#if "" = "test">
A
<#else>
B
<#/if>
In which case, I'd expect the else to be entered for sure -- but it is not.
Given this example:
<#assign myVar = null/>
<#if myVar == "test">
A
<#else>
B
</#if>
This throws the following error:
The following has evaluated to null or missing:
==> null
myVar has been evaluated to null or missing -- this seems like the source of my confusion; Freemarker does not differentiate between missing and existing-but-null values, I guess?
It's irrelevant if it's #if/#else or any other directive call, because when an error occurs (any kind of TemplateException, not just "missing value"), the whole statement is skipped. Not the variable resolution, not even the whole expression, but the whole statement. That's what the Manual and the Javadoc says too, like see: https://freemarker.apache.org/docs/pgui_config_errorhandling.html. Therefore, templateExceptionHandler setting is not for providing defaults for missing values. It's for handling situations that are genuinely errors. Like, someone did a mistake, something is not operational.
As of why not take the #else still. When an error occurs while evaluating the condition, the template engine just can't tell which branch would have been taken if the developers (or whoever) did not make a mistake. Picking either branch of the if/else is a gamble. (It's not even a 50-50 gamble, as the positive branch tends to be the right one considering the real world meaning of the output, yet, if anything, people expect the template engine to bet on the else branch.) So, hoping that you will print some error indicator at least, it rather doesn't pick either branch. But it's really automatic, as the whole #if/#elseif/#else thing is one big statement.
<#assign myVar = "test"/>
<#if myVar?? && myVar == "test">
A
<#else>
B
</#if>
Yes it is intended. When a variable is not defined or null, freemarker will raise an error.
It is explained in the FAQ: Why is FreeMarker so picky about null-s and missing variables, and what to do with it?
This is a design choice from the initial author of the framework. If you are not confortable with this choice, you can use the Default value operator (use myVar! instead of myVar), or use another templating framework.
The custom exception handler you added doesn't replace the expression with an empty String, it replaces the full if/else 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.

How do you evaluate and compare the value of item.istaxable in Netsuite?

I'm using the Advanced PDF/HTML Templates in Netsuite to create a custom output template. In this Template I want to evaluate an item to see if it is taxable.
NetSuite's Schema Defines a Sales Order with a Sub List Item that has a field .istaxable (source)
Field: istaxable
Type: checkbox
Label: Tax
Required: false
When I try to evaluate an expression such as:
<#if item.istaxable == true>
By printing the template I get the following error.
Left hand operand is a com.netledger.templates.model.StringModel
Right hand operand is a freemarker.template.TemplateBooleanModel$2
When I try to evaluate .istaxable as a String:
<#if item.istaxable == "true">
or
<#if item.istaxable == 'T'>
*EDIT: Updated in response to suggested answer
I am unable to save the template in the editor as it throws an error:
The only legal comparisons are between two numbers, two strings, or
two dates. Left hand operand is a
com.netledger.templates.model.BooleanModel Right hand operand is a
freemarker.template.SimpleScalar
So is item.istaxable a StringModel or a BooleanModel?
Netsuite is notoriously inconsistent with how it treats boolean values, and in fact I have come across cases where the same field is treated differently depending on the transaction (in my case it was the isclosed field). I ended up using the syntax below:
<#if (item.isclosed?is_boolean && item.isclosed) || (item.isclosed?is_string && item.isclosed == 'T')
Have you tried item.istaxable = 'T'? That is what I use in SuiteScript 1.0
<#if item.istaxable>.....
Should function as a true/false evaluation.
If Netsuite has set the boolean_format setting correctly, then the following should work:
<#if "${item.istaxable}" == "T">
If istaxable is a string, then its evaluation produces the Netsuite 'T'. If istaxable is a boolean, then the boolean_format setting is used to convert it to a string; if true maps to 'T' and false maps to 'F', then it produces the same result as the string version.
YMMV - If I remember, I'll try to update this answer.

Show default content in a template if an object is nil otherwise show based on the set property

In my template, I would like to include some default meta tags (90% of the time). However, when a specific property is set, I would like to show a different set of text.
I know I can set an anonymous struct and set a property with either "default" or "some-x". However, this means, I need to add an anonymous struct to 90% of my handlers that just currently pass nil.
Is there way to do something like
{{if eq . nil}}
// default meta tag
{{else if eq .MetaValue "some-x"}}
//other
{{end}}
If I try something like my above code, it compiles but doesn't do what I want. Appreciate any suggestions on how to handle it properly without adding a lot of boiler plate.
Thanks!
{{if not .}}
output when . is nil or otherwise empty including
false, 0, and any array, slice, map, or string of length zero
{{else if eq .MetaValue "some-x"}}
// some-x case
{{else}}
// other case
{{end}}
If you want to ensure you're only checking against nil and not 0, false, the empty string, or any other falsey type, you can use the kindIs function to accomplish this.
{{ if kindIs "invalid" . }}
// only if variable is literally nil. falsey values will fallthrough.
{{ else if eq .MetaValue "some-x" }}
// other
{{ else }}
// final case, if any
{{ end }}
I've been recently facing an issue with identifying nil vs 0 values in a Helm Chart (which uses Go templates, including sprig) and haven't found any solutions posted, so I thought I'd add mine here.
I came up with a kind of ugly solution which is to quote the value and then check for a string that matches "<nil>" (with quotes, so you'd actually be checking (quote .Values.thing | eq "\"<nil>\"")). This allows differentiating tests against empty values vs defined 0 values. In my case, I was trying to build a config file where some default options were non-0, so when 0 was explicitly set, I wanted to know that 0 was set instead of just omitted.
Hopefully this can be a help to someone else.
It would be nice to have a better way to do this, but so far I haven't found anything that doesn't require creating and adding my own template functions.

How to set null to a variable in freemarker

I can't find anything related to this on any question, and it is something really basic, but I can't figure it out.
So my problem is that I don't know how to set null to a variable in freemarker.
Example:
${hi!"bye"} <#-- Prints "bye" because hi is undefined -->
<#assign hi="hi"> <#-- Sets a value to the var hi -->
${hi!"bye"} <#-- Prints "hi" because hi has a value -->
<#assign hi=null> <#-- This does not work but is what I am looking for -->
${hi!"bye"} <#-- I want it to print "bye" because hi should be undefined -->
I have this problem because I iterate over a list and set a var if some logic to the specific item validates, and then check if the var exists, but if the first item creates the var, then I will have the var set for the rest of the items in the list.
No, there's no "unassign", nor the concept of null exists in FreeMarker (until 2.4.0 at least, but that's far away anyway). It only have missing variables (maybe technically a null, maybe doesn't exist at all) and those that are there. I don't really get why is that needed in your case. Can you show a simplified example of the situation?
You could assign an empty string to your variable and check with the buit-in ?has_content if it is set:
${hi?has_content?then(hi, "bye")}
<#assign hi="hi">
${hi?has_content?then(hi, "bye")}
<#assign hi="">
${hi?has_content?then(hi, "bye")}
This will render:
bye
hi
bye
Depending on what you need it for, you can use a different type to indicate a "missing" value.
For instance, if you have myVariable that is normally a number, assign false to it, and then instead of checking myVariable??, check myVariable!false?is_number. This will cover both cases (non-existent and "unset").
${ (myVariable!false?is_number)?c }
<#assign myVariable = 12 >
${ (myVariable!false?is_number)?c }
<#assign myVariable = false >
${ (myVariable!false?is_number)?c }
Result:
false
12
false
Go try.

Resources