0
votes

I'm pretty new to JSF / Facelets and trying to understand where best placed to put logic when deciding whether to render content within a ui:repeat component.

In my backing bean I have a list of messages each one containing a date. I want to print all messages but only render the date when it changes.

Currently I have something like below where the backing bean getter method determines whether or not to render the message date based on logic and setting state within the backing bean.

<ui:repeat value="#{bean.orderedMessages}" var="message" varStatus="messageLoop">

    <h:panelGroup rendered="#{bean.isDateRendered(messageLoop.index)}">
       #{message.formattedDate}
    </h:panelGroup>

    // some other stuff for each message

</ui:repeat>

This is buggy and horrible since JSF calls the getter method many times before actually rendering which adds further complexity to the getter method, the code has nothing to do with the anything other than whether something should be rendered or not which should really be in the UI and the getter method has a whole bunch of nasty logic in it whereas I would prefer a simple property to be returned.

I believe the UI has to somehow store the state of the last rendered date and compare that against the one it is about to render but I'm not sure whether that's correct.

I don't believe this is an uncommon problem. Does anyone know if there is a more elegant solution to solving this?

1
message in this instance is an entity populated from a database. It will always be rendered but has a property (date) that may or may not be rendered purely in relation to the other messages in the List at runtime. I don't think the logic should exist in the message entity since this is a UI thing and the message also has no knowledge of the other messages. This question is relating to the problem of managing state of objects within the repeat, and how to manage that state.Simon Kent
Possibly JSF should call a <:c:set... > on the backing bean setting the last rendered date. Not sure if this is a good practice or not as it doesn't feel like it.Simon Kent

1 Answers

0
votes

From the thoughts raised by BalusC in the comments of the question, I have implemented the following solution.

My Message object from the entity/domain layer is returned to the backing bean when the instance is created @PostConstuct, I have created another model in the UI layer called UIAugmentedMessage which takes the Message in the constructor as well as a boolean property for isRendered and a String for the formatted date.

The backing bean builds the list of UIAugmentedMessage objects when the Message list is returned from the server just after the bean is constructed and this contains the correct state for the isRendered and formattedDate fields.

The UI layer is now simplified as follows

<ui:repeat value="#{bean.orderedMessages}" var="message">

    <h:panelGroup rendered="#{message.rendered}">
       #{message.formattedDate}
    </h:panelGroup>

</ui:repeat>