Writing Generic Templates
Introduction
Goal
Write a generic template that can be used to render any content type.
Use Case
Sometimes functional requirements dictate that the same generic template can render any content bean that is passed on to it. As an example, consider a search results page. Typically the controller performs the search query and compiles a list of results. Those results can be of any content type used in the site. You want to render all possible types in a consistent way, say using the title and date of the item. However some content types might not have a date property.
Freemarker Templates
Freemarker provides a missing value test operator ( expression??) which can be used to handle such use cases:
<#list searchResults.items as bean> <@hst.link var="link" hippobean=bean.item /> <ul> <li> <a href="${link}">${bean.title?html}</a> <#if bean.date??> <p><@fmt.formatDate value=bean.date.time type="Date" pattern="MMMM d, yyyy h:mm a" /></p> </#if> </li> </ul> </#list>
Or even safer:
<#if bean.date?? & bean.date.time??> <p><@fmt.formatDate value=bean.date.time type="Date" pattern="MMMM d, yyyy h:mm a" /></p> </#if>
JSP Templates
JSP does not provide the same convenience operator as Freemarker, the HST Tag Library includes a function isReadable which can be used to achieve the same:
<c:forEach var="bean" items="${requestScope.searchResults.items}" > <hst:link var="link" hippobean="${bean.item}"/> <ul> <li> <a href="${link}"><c:out value="${bean.title}"/></a> <c:if test="${hst:isReadable(bean, 'date')}"> <p><fmt:formatDate value="${bean.date.time}" type="Date" pattern="MMMM d, yyyy h:mm a" /></p> </c:if> </li> </ul> </c:forEach>
The isReadable function also supports nested properties, so the example above can be made even safer:
<c:if test="${hst:isReadable(bean, 'date.time')}"> <fmt:formatDate value="${bean.date.time}" type="Date" pattern="MMMM d, yyyy h:mm a" /> </c:if>