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

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]
----*

Related

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>

String replace in FTL with a specific word

I need to replace a specific string in a URL in FTL.
Code 1:
<#assign pageUrlWithParams= "https://sample.com/category?filter=low&navParam=Appliances&skrLocale=en_US&t=1"/>
<#if pageUrlWithParams?? && pageUrlWithParams != '' && pageUrlWithParams?contains("skrLocale")>
<#assign pageUrlWithParams = pageUrlWithParams?replace('skrLocale','')/>
</#if>
${pageUrlWithParams}
Code 2 :
<#assign pageUrlWithParams= "https://sample.com/category?filter=low&navParam=Appliances&skrLocale=en_FR&t=2"/>
<#if pageUrlWithParams?? && pageUrlWithParams != '' && pageUrlWithParams?contains("skrLocale")>
<#assign pageUrlWithParams = pageUrlWithParams?replace('skrLocale','')/>
</#if>
${pageUrlWithParams}
I need to remove "skrLocale=en_US" and "skrLocale=en_FR" from pageUrlWithParams.
Known text is "skrLocale", using this I need to remove "skrLocale=en_US" and "skrLocale=en_FR".
Because of en_US and en_FR, i dont know how to take that that part after equals.
Some suggest an answer please. Thanks in advance
Using regular expressions ('r' as 3rd parameter to ?replace) is the key. Note that due to the complexity of URL syntax, we need to handle two edge cases (skrLocale is the first parameter, and skrLocale is the only parameter), which the below function fulfills. However, it doesn't handle %xx escapes in the parameter name (which you might don't care about):
<#function removeSkrLocale url>
<#return url?replace(r'([&\?])skrLocale=[^&]*&?', '$1', 'r')?remove_ending('?')>
</#function>
${removeSkrLocale(pageUrlWithParams)}
Of course you can do this without #function as well, directly inside the ${}, but it's reusable and more self documenting this way.
I would use a Freemarker function (this is a simple version, can be improved)
<#function remove_param_if_value_equals pageUrlWithParams paramName, paramValues >
<#if pageUrlWithParams?has_content && pageUrlWithParams?contains(paramName)>
<#list paramValues as paramValue>
<#local paramNameAndValue = paramName + "=" + paramValue />
<#local pageUrlWithParams = pageUrlWithParams?replace(paramNameAndValue,'')/>
</#list>
</#if>
<#return pageUrlWithParams />
</#function>
<#assign pageUrlWithParams= "https://sample.com/category?filter=low&navParam=Appliances&skrLocale=en_US&t=1"/>
<#assign pageUrlWithParams = remove_param_if_value_equals(pageUrlWithParams, "skrLocale", ["en_US", "en_FR"]) />
${pageUrlWithParams}
The code that does the business remains isolated and can be altered/improved later on.
(For example there is that double "&" that could be cleaned)
I don't know about FTL. But if you need to replace parameter skrLocale in your URL, I think below is good pattern you can use:
(skrLocale(.*?)&)
With (.*?) is called lazy, it match from skrLocale to nearest &. Therefore, with your case, it will match skrLocale=en_US& and skrLocale=en_FR& .

What's the meaning of !"" within a variable expression in freemarker?

I have the following code from the apache ofbiz development book:
<#macro displayData data>
<#if data?is_sequence>
<#assign keys = data?first?keys/>
<#else>
<#assign keys = data?keys/>
</#if>
<#-- Header -->
<tr>
<#list keys as key>
<td class="dark-grid"><b>${key}</b></td>
</#list>
</tr>
<#-- Data -->
<#if data?is_sequence>
<#list data as record>
<tr>
<#list keys as key>
<td class="light-grid">${record[key]!""}</td>
</#list>
</tr>
</#list>
<#else>
<tr>
<#list keys as key>
<td class="light-grid">${data[key]!""}</td>
</#list>
 <h1>Processed script: "${parameters.scriptName}"</h1>
<#if data?has_content && (data?is_sequence || data?is_hash)>
</tr> </#if>
</#if>
</#macro>
when I try to display something in the front end I get the following error:
Error on line 31, column 38 in component://crmsfa/webapp/crmsfa/tests/displayData.ftl Expecting a string, date or number here, Expression record[key]!"" is instead a freemarker.ext.beans.SimpleMethodModel The problematic instruction: ---------- ==> ${record[key]!""} [on line 31, column 36 in component://crmsfa/webapp/crmsfa/tests/displayData.ftl] in user-directive displayData [on line 7, column 9 in component://crmsfa/webapp/crmsfa/tests/displayData.ftl] ---------- Java backtrace for programmers: ---------- freemarker.core.NonStringException: Error on line 31, column 38 in component://crmsfa/webapp/crmsfa/tests/displayData.ftl Expecting a string, date or number here, Expression record[key]!"" is instead a freemarker.ext.beans.SimpleMethodModel at freemarker.core.Expression.getStringValue(Expression.java:126) at
freemarker.core.Expression.getStringValue(Expression.java:93)
I would like to know two things in order that I may be able to debug this first I've never come across a ${record[key]!""} (the !"" inside the variable) or <#assign keys = data?first?keys/> could someone please explain the semantic meaning of these two expressions.
The reason of the error is that the type of data[key] is not appropriate. The !'' part has no role in the failing case, as that only kicks in if data[key] is null or missing.
Otherwise please use the manual, it describes the meaning of those operators as well as anybody here could: http://freemarker.org/docs/dgui_template_exp.html#exp_cheatsheet

Filtering list of beans with expression

I have a list of beans and I would like to get a sub list matching a criteria on one or several properties with an expression as below:
${data[propertyName=='my value']}
data is a list of beans that have a property called propertyName.
Is such approach possible? If not, what is the best approach to do that.
Thanks very much for your answers.
Thierry
You could write a FTL function which selects the list items that match your criteria and collect them in a new sequence via sequence concatenation. Your filter function can be very simple or very sophisticated, it depends on your actual use case. Here is an example how it might work:
<#function filter things name value>
<#local result = []>
<#list things as thing>
<#if thing[name] == value>
<#local result = result + [thing]>
</#if>
</#list>
<#return result>
</#function>
<#-- some test data -->
<#assign data = [ {"propertyName":"my value", "foo":150},
{"propertyName":"other value", "foo":250},
{"propertyName":"my value", "foo":120}] >
<#assign filteredData = filter(data, "propertyName", "my value") >
<#list filteredData as item>
${item.foo}
</#list>
But keep into account that using sequence concatenation might be "suboptimal" for your performance.
You could do it by creating your own filter method variable and exposing it to the template. Then it would just be a matter of calling it with the list of beans and property value you want to filter on:
<#assign filteredData = filter(data, "my value") />
<#list filteredData as item>
// do something fancy
</#list>

Resources