This is actually quite easy in JSF. The framework offers you the ability to plug yourself into the different rendering and build events of the life cycle. So you can, for example, easily use this functionality to shuffle components around before the view is rendered and sent to the browser.
The code
Here is an example of a XHTML page with five p:panel
components defined. Each time you reload the page, the components will be shuffled and shown in a different order. You can easily adapt this to show them in an order you prefer or according to some configuration settings;
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Shuffle test</title>
</h:head>
<h:body>
<h:panelGroup>
<p:panel header="First"/>
<p:panel header="Second"/>
<p:panel header="Third"/>
<p:panel header="Fourth"/>
<p:panel header="Fifth"/>
<f:event listener="#{shuffleBackingBean.onShuffle}" type="preRenderComponent" />
</h:panelGroup>
</h:body>
</html>
As you can see from the placement of the f:event
tag, we plug ourselves into the preRenderComponent
phase of the parent h:panelGroup
. This allows us to receive an event before it's time for the rendering phase of this component.
@Named
@ViewScoped
public class ShuffleBackingBean implements Serializable {
public void onShuffle(ComponentSystemEvent event) {
final List<UIComponent> components = new ArrayList<>(event.getComponent().getChildren());
Collections.shuffle(components);
event.getComponent().getChildren().clear();
event.getComponent().getChildren().addAll(components);
}
}
The backing bean above defines the onShuffle
method and just shuffles the components around when called. If you reload the page, the components get re-shuffled.
Side note
The reason for doing a shuffle()
on a copy of the component list is that JSF uses a custom ChildrenList
class that is based on ArrayList
. The implementation is botched and causes Collections.shuffle()
to crash with an IndexOutOfBoundsException
. This just works around that issue.
An alternate solution
Another way of dealing with this would be to rely on some component that offers built-in sorting or use a binding declared on the h:panelGroup
component. This would allow you to populate this component programmatically based on some setting. However, this moves much of the view definition out of the XHTML file and into a java class. It also complicates things slightly if you have a lot of sub-components in your panels and makes it even more complicated if they are very different from each other. Defining all that programmatically can be quite a hassle.
Conclusion
Personally, I prefer plugging into the event cycle and just moving or modifying components already defined in the XHTML page (as per the code above), as this moves more of the view definition into the XHTML file where it belongs.