0

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?

1 Answer 1

1

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.

2
  • OK, that's the other idea I had in mind as well. And, as you mention, it has the issue of mixing things across layers. That has a bit of a "code smell" to me, but I guess it's still cleaner than explicit type checks (which are exactly what I want to avoid).
    – mindcrime
    Commented Jan 29, 2012 at 7:45
  • 1
    I don't mind adding code in the models to support presentation. Just don't add code in the models that does presentation. Commented Jan 30, 2012 at 2:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.