What’s the best practice for rendering a different GSP template based on the type of object in a collection

grailsgroovyrenderingtemplates

Let's say I have a collection that is setup in my controller, named "things." In this collection is a heterogeneous assortment of objects. Let's say some are of type "Thing" and some others are of type "OtherThing". In the GSP
page that renders this list, I want to render each different object type
differently.

I know that the following code works, but I can't escape the feeling that there should be a more elegant way to do this. Does anybody have a better way?

        <div id="eventStream">

        <ul>
            <g:each in="${things}" var="thing">

                <g:if test="${thing.type.equals( 'Thing' )}">
                    <g:render template="/renderThing" bean="thing" />
                </g:if>

                <g:if test="${thing.type.equals( 'OtherThing')}">
                    <g:render template="/renderOtherThing" bean="thing" />
                </g:if>

            </g:each>       
        </ul>

    </div>

(Note: there is a "type" field defined on 'Thing' and 'OtherThing' that contains those string literals. That's just a shortcut).

EDIT: Just realized that there's another way to do this, that depends on the notion of "convention over configuration." I could just go with a convention that the templates are always named something like:

_renderXXXXTemplate.gsp where XXX is "Thing" or "OtherThing" or whatever. Then I just render with something like:

<g:render template="/render${thing.class.name}Template" bean="thing" />

Anybody care to share their thoughts on the merit of that approach?

Best Answer

I hate explicit type-checks. I would probably start by creating a private tag to render this part of the page.

<myApp:renderThing object="${thing}" />

Then I would probably add an abstract method to the base class to return the template name. Sure, it mixes presentation logic into the business classes but it will simplify maintenance considerably.

abstract class Thing {
    public abstract String getTemplateName();
    ...
}

public class ThingOne extends Thing {
    public String getTemplateName() { return "templateForThingOne" }
    ...
}

Another idea would be to derive the template name from the class name, and add a note in the base class to remind developers to create the appropriate template for any subclass.

Related Topic