How tu use a variable in if in ftl? - freemarker

I am learning ftl and have problem with variables.
I have an element a which has its own subelement b.
I access the subelement with the following method:
a.getChild("b")
Now, I check whether a subelement has content in the following way:
<#if a.getChild("b").getData()?has_content>
and this works as expected.
Now, I wanted to shorten this if's syntax by doing the following:
<#assign b>${a.getChild("b")}</#assign>
<#if b.getData()?has_content>
However, this produces error:
For "." left-hand operand: Expected a hash, but this has evaluated to a string
What am I doing wrong and how do I eliminate the need for calling the getChild in ifs?
I have hundreds of ifs in my production environment which all use getChild, sometimes even multiple times if subelements are nested so it would be great if I could shorten the syntax by assigning a child element to a variable.
I just started learning ftl yesterday, so this might be stupid question, sorry.

Simply do <#assign b = a.getChild("b")>.
With ${a.getChild("b")} you've outputted a string which then was assigned.
See the documentation also: https://freemarker.apache.org/docs/ref_directive_assign.html

Related

In Freemarker is it possible to use the value of a hash item as a method? i.e., ${key}: ${val?val}

I'm trying to make a function or macro to test a variable against all Freemarker is_xxxx types (https://freemarker.apache.org/docs/ref_builtins_expert.html#ref_builtin_isType), so that I can just run a variable through that function/macro to see what it is.
I could obviously just run each one at a time and return that, i.e.,
is_string? ${var?is_string?c} <br/>
is_hash? ${var?is_hash?c} <br/>
I was hoping to avoid that by <#list> ing through a hash, but I couldn't figure it out ... then I just got curious if this sort of thing were even possible.
At a loss how to do this, or if I can.
For example
<#assign builtIns = {"is_string":"is_string","is_number":"is_number","is_boolean":"is_boolean","is_date_like":"is_date_like","is_date_only":"is_date_only","is_time time":"is_time time",
"is_datetime":"is_datetime","is_unknown_date_like":"is_unknown_date_like","is_method":"is_method","is_transform":"is_transform","is_macro":"is_macro","is_hash hash":"is_hash hash","is_hash_ex":"is_hash_ex","is_sequence
sequence":"is_sequence sequence","is_collection":"is_collection","is_collection_ex":"is_collection_ex","is_enumerable":"is_enumerable","is_indexable":"is_indexable","is_directive":"is_directive","is_node
node":"is_node node","is_markup_output":"is_markup_output"}
/>
<#list builtIns as key,val>
${key},${val?val} <br/> // or something like this.
</#list>
Make sense? Possible? A different way to do this entirely?
Thanks!
No, the so called built-ins (things after the ?) has no value, so you can't put them into variables. That's unlike normal methods, FTL macros, and FTL functions, which can be passed around as a value. The difference is because some built-ins affect template parsing (kind of like compilation), so they are not purely runtime.
Well, you could get around that with generating the expression as a string then do myExpressionString?eval, but that's awkward and somewhat slow.

Freemarker expected extended hash error

I've added an objectwrapper in freemarker that wraps a type of object as a templateHashModel. I don't seem to be able to iterate over it - if I try to iterate as a hash, (ie <#list blah as x,y> I get this:
Expected an extended hash, but this has evaluated to a hash
(au.com.amp.common.json.JObjMap wrapped into au.com.amp.blue.templates.FreemarkerAdapterJobj
And if I iterate as a list (ie <#list blah as x>) I get
Expected a sequence or collection, but this has evaluated to a hash
any ideas?
found the solution - apparently freemarker wants you now to implement TemplateHashModelEx rather than TemplateHashModel in your object wrapper. When you do that it works great.

Freemarker - Evaluating an expression one after another

the issue here is that i have both index and c as the evaluated expressions, after index[1] is evaluated, it will serve as a parameter to the userDetails. Hence, an e.g. like c.firstname.
<#ftl encoding='UTF-8'>
<#list param?chunk(3) as index>
<#list userDetails as c>
${c.index[1]}
</#list>
</#list>
However, i am assuming that Freemarker is evaluating all the expressions at one time, hence when this piece of code is being run, index[1] is not evaluated yet. Thus, i'm getting this error instead.
FreeMarker template error:
The following has evaluated to null or missing:
==> c.index [in template "src/template_fixedlength.ftl" at line 5, column 19]
Does anyone of you know of any workaround for this problem?
Thanks in advance!
The evaluation happens left to right in this case, like if it was (c.index)[1]. This is like so in pretty much all other languages too. If I understand well, index[1] would evaluate to the string "firstname", and you want c.firstname in effect. Then, the syntax is c[index[1]].

"Expression Too Complex" error on simple property assignment

I'm getting (fairly reguarly) an "Error 16: Expression too complex" runtime error on a simple assigment to a property from a class.
public property PropertyName() as double
PropertyName = mvarPropertyName
end property
The debug window points to the crash being on the assignment line in the above code.
Some inital reading here and elsewhere suggested that it was related to the line calling the property. However, that now looks like this:
variableName = ObjectName.PropertyName
And all arithmetic is done with variableName.
Even more oddly, if I just hit debug, then resume/F5 immediatley, everything is fine.
Trying to use the error handling code to do this doesn't seem to have worked however.
Any ideas what is causing this error?
Stop using Not (Not MyArray) to test for uninitialized arrays. This uses a bug in the compiler that has a known side effect of destabilizing the run-time leading to "Expression too complex" on random places.
VB6 - Returning/Detecting Empty Arrays is fairly complete thread on different ways to test for empty and uninitialized arrays.
A string expression is too complicated. Strings not assigned to variables (such as those returned by functions) are assigned to temporary locations during string expression evaluation. Having a large number of these strings can cause this error. Try assigning these strings to variables and use the variables in the expression instead.

Ruby if vs end of the line if behave differently?

Why doesn't this code work?
b if b = true
Error: undefined local variable or method `b'
But this does:
if b = true
b
end
Shouldn't they be the same?
This is a very good question. It has to do with the scoping of variables in Ruby.
Here is a post by Matz on the Ruby bug tracker about this:
local variable scope determined up to down, left to right. So a local variable first assigned in the condition of if modifier is not effective in the left side if body. It's a spec.
In the first version as soon as k is hit, the parser pukes because it hasn't been seen yet.
In the second version, k is part of an assignment expression, and is parsed differently.
I don't know the reason but the problem that the interpreter tries to lookup the variable k before evaluating the condition.
If you write it like this, there won't be any error and works as you expected:
k = nil
h = {k: 1}
v = k if k = h.delete(:k)
you have put only one '='
Try with '=='
Then you will get error
In second example, you are assigning 'true' to b.
Because the Ruby interpreter creates a local variable when it sees an assignment
In the second case, it hasn't yet seen the assignment, so the variable doesn't exist when the expression is parsed.
To be more precise, a method is first parsed into an internal representation, and then, perhaps, the code will eventually be called and actually executed.
Local variables are created in that parsing pass. It's a matter of declaration, it just means that the interpreter becomes aware of them. They won't be created in the sense of being given space or a value until the surrounding method is called by someone.

Resources