Problem: The Primefaces dataTable sort functionality doesn't work!
While constructing this site I needed to list all articles in tabular form to gain access to hidden and draft articles. The Primefaces dataTable is a perfect fit so a page was constructed:
<p:dataTable value="#{articleController.allArticles}" var="article">
<p:column headerText="Title" sortBy="#{article.title}">
<a href="#{request.contextPath}/showArticle.faces?article=#{article.id}">#{article.title}</a>
</p:column>
...
</p:dataTable>
This code grabs a collection of articles and outputs each article in a row in the table, so far so good - there is a problem!
The articleController class is RequestScoped so no state is maintained between requests. Every time the page is refreshed the list of articles is fetched from the database which lead to a problem when it came to sorting the columns within the dataTable. To sort the columns the sortBy attribute was added to the p:column tag. When the page is rendered a sort column icon appears next to each column. Clicking on the column header did nothing, I could see an ajax request was being posted but the order of the rows stayed the same!? A little strange I thought, surely this dataTable and sort should work out of the box!?
After some research I discovered the collection the dataTable references needs to maintain state therefore the request scope of the allArticles method was not going to work! A possible solution - change the scope of articleController class to SessionScoped but we all know that is not good practice right! A better solution is to keep the articleController in request scope but move the collection of articles out of the articleController class and into a SessionScoped class! A perfect home is the applicationController class which is SessionScoped.
This approach solves the column sorting issue but has an unintended side effect! The list of articles are grabbed from the applicationController, if this list is null the list is read from the database and returned. If the list is not null then the database read is skipped and the in memory list is returned. The problem occurs when there is a modification to the list of articles ie. an add/edit or delete of an article. Because the list of articles is not being read from the database every time there was a potential that the data could become out of sync. Step in the Observer pattern!
I needed a way of resetting the session scoped article list if there were any modifications to the list of articles. Of course I could grab my session scoped bean from the context and reset the list that way but this would break the loosely coupled rule! A neater solution that results in completely decoupled code is to broadcast a "data changed" event. When ever I need to reset this session scoped articles list I include this
@Inject
private Event<String> articlesEvent;
And when the actual data changes I fire the event
articlesEvent.fire("Reload list (edit article)");
In my application controller I simply include a method that "listens out" for events with a String parameter and that is all there is too it
public void resetArticles(@Observes String type) {
LOGGER.debug("Observer event received: " + type);
articles = null;
}
References
https://examples.javacodegeeks.com/java-ee-observer-design-pattern-example/