I have a ftl freemarker template file, I pass some object (that contains the list of needed elements) to it and trying to process html output page. The problem is that I need to implement some complex logic using this object, so if I do it using freemarkers if's
list
and e.t.c. it will become too complex and uneccesarry big (since I will copy same logic in multiple places on the page).
I want to render table rows depending of the size of needed elements (by key), but obviously sometimes there’s no elements so I need to render an empty row too if that happens, so it’s easier for me to do in java code. I know how to call a method from ftl file, my question is how do I correctly return html string for a row from my java code and put it in template so it will be rendered as html string?
For example, the row looked something like this when I was putting all of the needed entries by key in one table row:
<tr>
<td>${object.getNumbers("key1")}</td> // multiple names, but all in one <td> element
<td>${object.getNames("key1")}</td> // same, all names for all entries by "key1"
<td>${object.getDescriptions("key1")}</td>
</tr>
<tr>
<td>${object.getNumbers("key2")}</td>
<td>${object.getNames("key2")}</td>
<td>${object.getDescriptions("key2")}</td>
</tr>
So basically it’s an object that contains a List of needed objects, but now I need to print every entry from every key each in a separate row (in the example above all the entries infos were printed in one row), but I don’ want to write complex logic in ftl file, because it will look something like this and I’m not even sure it will work:
<#if(object.isPresent("key")>
<#list object.getEntries("key") as entry>
<tr> // printing every entry
<td>...
...
<tr>
<#else>
<tr> // empty tr if there's nothing
...
<#if>
It already looks big and it’s just for one key, so I was thinking maybe it will be easier to write something like this using java code:
...
public String renderRow(String key){
StringBuilder sb = new StringBuilder();
if(elements.contains(key)){
for(var element: elements.get(key)){
sb.append("<tr>");
sb.append("<td>");
sb.append(element.getNumber());
...
}
else{
sb.append(makeEmtpyRow());
}
return sb.toString(); // escape?
}
...
So I can put together needed table rows using java code, because it will be easier to implement, and then use returned html code in the page. How can I do that? I’ve read that the returned value will be printed as it is instead of the html. How should I call it from ftl file so it will be rendered correctly as html string?
... // other part of html page
<table>
#{object.renderRow("key1")} // I guess this won't work because it will be a literal string instead of html?
#{object.renderRow("key2")}
...
</table>
...
Also, do I need to do any special escapes before returning the string from java code? If yes, than what and how to do it correctly? I tried to search for some examples but unfrotunately didn’t find anything like this case and the guides on official site are too sophisticated
UPD: I’m not going to create the whole html page out of returned java string, only the part with the table, so I will still have a .ftl file and call a method where I need to render these table rows
UPD2: Just tested it out, seems like calling ${object.renderRow("key")} works as it is and actually takes the returned string as an html code. But from java code I’m returning sb.toString()
and not doing any escapes, is it enough and ftl does something for me with escaping or I need to manually escape returned html string, if so, how to do it?
2
Answers
So, the solution was easier than I expected. You can make a call from .ftl file to java method which will return a rendered html code like this:
Just make sure that returned string is escaped so the data won't be lost, in my example I escaped the data in the
td
tags, i.e:At the end I got a page with the needed amount of
<tr>
s and<td>
s and the data was properly displayed (even if there are special html symbols like<
in the data).You should always use auto-escaping, so that
String
-s by default are HTML-escaped. See: https://freemarker.apache.org/docs/dgui_misc_autoescaping.htmlThen, where you return a
String
that’s actually HTML, like"This is <em>HTML</em>!"
, useHTMLOutputFormat.INSTANCE.fromMarkup("This is <em>HTML</em>!")
instead of aString
. Then when it’s printed with${}
, FreeMarker will know that it needs no escaping.