Showing posts with label freemarker. Show all posts
Showing posts with label freemarker. Show all posts

Thursday, 14 May 2009

FreeMarker an iterating a map

We use FreeMarker to render our views in our web tier. It is a really powerful, fairly extensible (would love to easily add/override builtins!!!), but there are a couple of pain points.

The first is iterating maps. Without going into too much detail, you may expect to be able to do the following:
<#list map as entry>
  Hi there ${entry.key}, this is your value ${entry.value}
</#list>
but you can't.

Neither can you do:
<#list map.entrySet() as entry>
  ...
</#list>


Neither can you do:
<#list map.keySet() as key>
  ${map[key]} or ${map.key} or ${map.(key)} or ${map.[key]}
</#list>

Instead, you need to do the following:

Neither can you do:
<#list map?keys as key>
  ${map.values[key_index]}
</#list>
Nice.

Yeah.

Thursday, 7 May 2009

Convenient directive for iterating lists in freemarker

We are seeing the same pattern of iterating lists within freemarker appearing:
<#list items as item>
  <#if item_has_next>,</#if>
<#list>

this is quite verbose, and error prone so I have added a new directive called 'iterate', which automatically appends a separator (default is comma) if required.
<@iterate items ; item>
  whatever I want to do with the ${item}.
<@iterate>

The directive:
<#macro iterate items separator=','>
<#list items as item>
  <#nested item/>
  <#if item_has_next>${separator}<!--#if-->
<#list>
<#macro>

To use, this you need to put it into a file (i.e. mystuff.ftl) and then include it within all the templates from which you want to use the new directive (i.e. all of them ;)).
My freemarker fu sucks a bit at this point. We have a web app so I included it in /WEB-INF/views/includes/mystuff.ftl (of course I didn't call it that!). Unfortunately I needed to specify the relative path as I couldn't figure out the fully qualified one - /WEB-INF/views/includes/mystuff.ftl and /views/includes/mystuff.ftl don't work.
So, if my template is /WEB-INF/views/people/list.ftl then I need to do the following:
<#include ../includes/mystuff.ftl>

<@iterate people ; person> Person ${person_index}: ${person.name} <@iterate>

We are using Spring to configure Freemarker, which means using FreeMarkerConfigurer. Unfortunately, I don't think this supports the ability to specify automatic includes (similar to JSP preludes and codas), so I might need to write my own. No biggies.