how to change parameter variable value in a recursive macro? - freemarker

I want to show a number sequence in black color, and show nested sequence number in red. Here is my marco code:
<#macro test datas isNested>
<#list datas as d>
<#if d?is_enumerable>
<#local isNested = true>
<#if isNested>
<#-- become red if it is nested -->
Hi, I am going to be red!
<#-- some business code -->
</#if>
<#test d isNested/>
<#else>
<#if isNested>
<span style="color:red;">${d}</span>
<#else>
${d}
</#if>
</#if>
</#list>
</#macro>
call it like this: <#test [1,2,3,[4,5],6,7] false/>
I want it to be: 1 2 3 Hi, I am going to be red! 4 5 6 7
only 4 and 5 show in red.
But now I get this:
1 2 3 Hi, I am going to be red! 4 5 6 7
all of 4 5 6 7 are show in red.
I think this line of code <#local isNested = true> play the trick. I also try to use assign, global, all of 1 2 3 4 5 6 7 are black.
so how should I do?
Thx in advance.
PS:
I don't know how to make code show in red in stackoverflow, if any one knows plz helps me to edit my code.

Your problem is not FreeMarker-specific. You flip the isNested local variable (which also holds the parameter value) to true before the recursive #test call, and you leave if so after the recursive macro call has returned. So when the iteration of 6 and 7 comes, isNested is already true. You should not set the isNested at all, just pass true to the recursive call. Here:
<#macro test datas isNested=false>
<#list datas as d>
<#if d?is_enumerable>
Hi, I am going to be red!
<#test d true />
<#else>
<#if isNested>
<span style="color:red;">${d}</span>
<#else>
${d}
</#if>
</#if>
</#list>
</#macro>
<#test [1,2,3,[4,5],6,7] />

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 != ''>.

How to properly group records when executing a <#list> for a Saved Search

New to the forum but have been working with NetSuite for about 3 years and using this site as a resource. I was hoping someone could help me understand the answer to the following question, How to properly group records when executing a <#list>, so that I could adapt it to be used when pulling a saved search for items?
Thanks for the quick response! I've been trying to use the code you provided as a template and hoped you could take a quick look. For the macro, I modified as follows.
<#macro listGroups results groupField>
<#if results?size == 0><#return></#if>
<#local sortedResults = results?sort_by(groupField)>
<#local groupStart = 0>
<#list sortedResults as result>
<#if !result?is_first && result[groupField] != lastResult[groupField]>
<#local groupEnd = result?index>
<#nested lastResult[groupField], sortedResults[groupStart ..< groupEnd]>
<#local groupStart = groupEnd>
</#if>
<#local lastResult = result>
</#list>
<#local groupEnd = sortedResults?size>
<#nested lastResult[groupField], sortedResults[groupStart ..< groupEnd]>
</#macro>
When I used the macro later, I modified as follows.
<#listGroups record.result "result.class"; groupName, groupResults>
<p>${groupName}</p>
<table>
<#list groupResults as groupResult>
<tr>
<td>${groupResult.itemid}</td>
<td>${groupResult.salesdescription}</td>
<td>${groupResult.othervendor}</td>
<td>${groupResult.vendorcost}</td>
</tr>
</#list>
</table>
</#listGroups>
This is the error I'm getting, but I feel like I'm very close to what I'd like to accomplish.
*The following has evaluated to null or missing:
==> record [in template "template" at line 58, column 14]
Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
FTL stack trace ("~" means nesting-related):
Failed at: #macro listGroups results groupField [in template "template" in macro "listGroups" at line 3, column 1]
Reached through: #listGroups record.result, "result.cl... [in template "template" at line 58, column 1]
----*

Summation of values in freemarker

I am new to programming and this is my first time using freemarker. I was wondering if there is a way to get the summation of the values.
My current code is this:
<td>Total Boxes:</td>
<td><#if invtransfer?has_content>
<#if record.custbody_hdr_sort_by == "Item Category">
<#list invtransfer?sort_by("x.item.class?split(':')[0]?trim") as x>
<#assign currCategory>${x.item.class?split(':')[0]?trim}</#assign>
<#if currCategory == cat>
<#if x.unit == "PIECE">
${(x.item.unitstype?split("/")[0]?trim?number) * (x.quantityuom?replace("-", "")?number)}
<#elseif x.unit == "BOX">
${x.quantityuom?replace("-", "")}
<#elseif x.unit == "PALLET">
${(x.item.unitstype?split("/")[1]?trim?number) * (x.quantityuom?replace("-", "")?number)}
</#if>
</#if>
</#list>
</#if>
</#if>
</td>
and my current output is this:
Total Boxes: 3 4 5
How do I add them all to get Total Boxes: 12?
Please help, Thanks.

Convert html string with anchor tags into plain text in freemarker

Is there a way in freemarker to convert the below html string into plain-text?
Challange: data assign in below tag is dynamic including links and their placement.
<#assign data = '<li>List item 1 with some link</li><li>List item 2 with some link1 and some link2</li>' />
Expected: Need data in plain text format like below:
- List item 1 with some link <https://some-link.com>
- List item 2 with some link1 <https://some-link1.com> and some link2 <https://some-link2.com>
What I have achieved till now:
I am able to remove the complete html code using regex freemarker but it also removes the anchor tag urls which cause unexpected results.
${data?replace("<[^>]*>", "", "r")}
// result
- List item 1 with some link
- List item 2 with some link1 and some link2
Any help would really appreciate!!
Please find the solution below.
<#function htmlListToPlainText param>
<#assign str = param?replace('<ol>','')?replace('</ol>','')?split('</li>')/>
<#assign result>
<#list str as item>
<#if item?contains('<a')>
<#assign anchors = item?matches(r"href='(.*?)'") />
<#if anchors?size == 0>
<#assign anchors = item?matches(r'href="(.*?)"') />
</#if>
<#assign mitem = item?replace('</a>','[0]','f')?replace('</a>','[1]','f')?replace('</a>','[2]','f')?replace("<[^>]*>", "", "r") />
<#list anchors as m>
<#assign mitem = mitem?replace('[${m?index}]',' <${m?groups[1]}>','f') />
</#list>
-${mitem}
<#elseif item?has_next>
-${item?replace("<[^>]*>", "", "r")}
</#if>
</#list>
</#assign>
<#return result>
</#function>
How to Use
${htmlListToPlainText(YoursignalName)}
Limitation:
Handling upto 3 dynamic links in one <li> tag.

Freemarker: convert sting to list with split then iterate

I need to iterate through a list made from the "split" built-in yet I haven't succeeded.
I have a two lists of dates that share some of the same dates. using "seq_index_of" as a "Vlookup" i'm able to find the correlation and get the index of the dates that are in both lists. however, "seq_index_of" gives a string, number, boolean or date/time values output and not a sequence (for me to iterate).
I use "split" to turn the string into a sequence. split wont truly do the job, though.
first i use seq_index_of for the two lists:
this will give me the index of correlated dates.
<#assign result>
<#list list1 as L1>
${list2?seq_index_of(L1)}
</#list>
</#assign>
---------------------------------------
next I iterate the result. for this, imagine "array" is the result from above:
ex 1. this does not work. cant iterate
<#assign array = "2020-10-02,2021-10-04,2022-10-04,2023-10-04" />
<#assign mappedArray_string = []>
<#list array?split(",") as item>
<#assign mappedArray_string += [item]>
</#list>
${"is_sequence: " + mappedArray_string?is_sequence?c}
<#--print the list-->
<#list mappedArray_string as m>
<#--i cant do ?string("MM/dd/yyyy") or ant other iteration
${m?string("MM/dd/yyyy")}
<#if m?has_next>${";"}<#else>${"and"}</#if>-->
<#with no iteration, this will work. not what i need-->
${m}
</#list>
+++++++++++++++++++++++++++++++++++++++++++++
ex 2. - this works with a regular numerical list
<#assign array = [100, 200, 300, 400, 500] />
<#assign mappedArray_numbers = []>
<#list array as item>
<#assign mappedArray_numbers += [item]>
</#list>
${"is_sequence: " + mappedArray_numbers?is_sequence?c}
<#--print the list-->
<#list mappedArray_numbers as m>
${m?string("##0.0")}<#-- able to iterate-->
</#list>```
expected:
date1 ; date2 ; date3 and lastdate
error:
For "...(...)" callee: Expected a method, but this has evaluated to a string (wrapper: f.t.SimpleScalar):
==> m?string [in template "preview-template" at line 27, column 3]
----
FTL stack trace ("~" means nesting-related):
 - Failed at: ${m?string("MM/dd/yyyy")}
If you want to list the items of list2 that also occur in list1, then do this (requires FreeMarker 2.3.29 because of ?filter):
<#list list2?filter(it -> list1?seq_contains(it)) as d>
${d?string('MM/dd/yyyy')}
</#list>
Before 2.3.29:
<#list list2 as d>
<#if list1?seq_contains(d)>
${d?string('MM/dd/yyyy')}
</#if>
</#list>

Resources