3

I have an object (Ticket), which has a list of other objects (Message). Message is abstract, and has several subclasses - like EditMessage, CreationMessage, and so on. So that Ticket object contains a mix of that messages, and they are ordered by their creation time.

Now I want to display all those messages in a Facelets page, and I need to output values of fields, specific for that message type: i.e., editedField in EditMessage, userName in CreationMessage, ...

The most obvious way seems to use h:dataTable:

<h:dataTable value="#{ticketController.ticket.messages}" var="msg" >
    // determine type of message, cast, and use <c:if> to output needed values
</h:dataTable>

The problem is that Facelets expression language does not have "instanceof" and casts. As far as I can see, this can be solved using some ugly round-tripping to managed bean, determining type of message in standard Java, return message of needed type, ... and so on.

Is there a better, more understandable and concise way of doing this?


Solution

My main problem was with <c:if> tag. It turned out that it is a JSTL tag, so it has slightly different rendering life cycle. Instead of it, I now use <h:panelGroup> and its "rendered" attribute.

Some code:

<h:dataTable value="#{ticketController.ticket.messages}" var="msg" >
    <h:column>
        <h:panelGroup rendered="#{msg.class.name == 'org.rogach.tsnt.TextMessage'}" >
            <h:outputText value="msg.text" />
        </h:panelGroup>
        <h:outputText value="#{msg.creationTime}" />
    </h:column>
</h:dataTable>

And no cast is ever needed.

Rogach
  • 26,050
  • 21
  • 93
  • 172
  • 1
    this is exactly what I'm working on right now. so i'm interested in the answer too. – Cosmin Cosmin Jun 11 '11 at 21:15
  • 1
    JSTL runs during JSF view build time. Basically, JSTL produces JSF. It's not just "slightly different". During render response it's one and all JSF which produces HTML. Similar question here by the way: http://stackoverflow.com/questions/4166247/how-to-handle-polymorphism-with-jsf2 – BalusC Jun 12 '11 at 03:52
  • 1
    you can put rendered directly on outputText – Cosmin Cosmin Jun 12 '11 at 09:19
  • @Cosmin Vacaroiu - Yes. But h:panelGroup is better, since it allows me to put any number of components inside it and control their rendering all at one time. – Rogach Jun 12 '11 at 11:31
  • 1
    yes, but i think it renders you an extra div in html which might screw your styles up. – Cosmin Cosmin Jun 12 '11 at 11:34
  • @BalusC - yes, a very similar question. By the way, in your answer there you were saying that "xxx.class.name" would not work somewhere. Is it a restriction in some newer version of EL, or it is just Tomcat's problems? I'm deploying on GlassFish 3.1, everything works for now. – Rogach Jun 12 '11 at 11:35
  • @Cosmin Vacaroiu - no, it does not. I specially checked documentation about it, and it says that if `layout="block"` is not present, it renderes only tag. – Rogach Jun 12 '11 at 11:37
  • yeah, span. Anyway in Seam 2 you had `s:fragment` which didn't rendered nothing in html. – Cosmin Cosmin Jun 12 '11 at 11:54

1 Answers1

3

Instead of instanceof, compare the name of the object's class.
Say: <c:if test="${xxx.class.name == 'CreationMessage'}"> or c:choose
And you won't need any cast with EL. If the object doesn't have some property you specified it will give an exception, if it does have it's OK.

Cosmin Cosmin
  • 1,526
  • 1
  • 16
  • 34
  • Thanks! But there is problem with the class name - it refuses to agree, that it is that class. Funnily, I outputted the name of class a line earlier, triple checked it, but #{msg.class.name == 'org.rogach.tsnt.CreationMessage'} still returns false. – Rogach Jun 11 '11 at 21:36
  • eq ? instead of == Try to output #{msg.class.name} to see what it exactly is. – Cosmin Cosmin Jun 11 '11 at 21:40
  • Wait, now that is funny! #{msg.class.name == 'org.rogach.tsnt.CreationMessage'} alone prints true, but is not printing. – Rogach Jun 11 '11 at 21:40
  • maybe it's a problem mixing jstl - jsf. See http://onjava.com/pub/a/onjava/2004/06/09/jsf.html – Cosmin Cosmin Jun 11 '11 at 21:43
  • Yes, looks like that. The article says " and may contain JSF component custom actions, but only if the id attributes are set for the nested JSF component actions". – Rogach Jun 11 '11 at 21:52
  • But how can I avoid nesting output tags in ? – Rogach Jun 11 '11 at 21:53
  • And by the way, it may not be a problem with JSTL - prints! – Rogach Jun 11 '11 at 21:56
  • Finally, I found the solution! If you are interested, I have put it into question. – Rogach Jun 11 '11 at 23:25
  • Every JSF UI component has a `rendered` attribute. Just put it on the `h:outputText` instead of wrapping it in another component. – BalusC Jun 12 '11 at 03:49
  • @BalusC - Thanks! But in fact, not every component has it. For example, ui:fragment does not allow `rendered` attribute. – Rogach Jun 12 '11 at 11:30
  • @Rogach: you're apparently using Netbeans which incorrectly gives a IDE warning on that. Just ignore and run it. You'll see that it works. – BalusC Jun 12 '11 at 11:55