Surfaces Datatable / sorting / wrong item
I have a simple Databean:
@Model
Class DataBean{
private List<Elements> elements;
@PostConstruct
private void loadElements(){
//fetch data from database.
}
}
im using Primables datatable to display data like:
<h:form>
<p:dataTable
value="#{dataBean.elements}"
var="element" >
<p:column sortBy="#{element.id}"
sortFunction="#{sortingHelper.sortNumericCallback}">
<f:facet name="header">ID</f:facet>
<p:commandLink action="#{pageController.navigateToDetailView(element)}"
value="#{element.id}">
</p:commandLink>
</p:column>
</p:datatable>
</h:form>
pageController.navigateToDetailView(element)
simple sets the selected item on the next databean page so detailView has its data, and then returns detail-Navigation-Outcome.
Now: Problem: If I click on one of the cmdlets without any sorting, everything is fine. If I sort by id and click on the details link, the following happens:
- Request started
- Databean (postconstruct) loaded (no sorting)
Now - in the second request - the page is rebuilt again (in order to run navigateToDetailView-Action) AND the data that "datatable" knows I clicked on line 5. But without sorting it again, line 5 is now a different entry, so how the bean is restored.
Console output for different points.
First, I click on the page showing the Datatable. "." are one of the comparisons of my custom sort function, just to indicate the collection is sorted.
13:47:56,046 INFO [stdout] (http--0.0.0.0-8090-1) -- Started Request --
13:47:56,047 INFO [stdout] (http--0.0.0.0-8090-1) ---- Started RESTORE_VIEW 1 ----
13:47:56,048 INFO [stdout] (http--0.0.0.0-8090-1) ---- Started RENDER_RESPONSE 6 ----
13:47:56,087 INFO [stdout] (http--0.0.0.0-8090-1) PostConstruct DataBean
13:47:56,566 INFO [stdout] (http--0.0.0.0-8090-1) -- Finished Request --
It's good. Now im sorting by clicking the title id
13:48:15,008 INFO [stdout] (http--0.0.0.0-8090-2) -- Started Request --
13:48:15,009 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started RESTORE_VIEW 1 ----
13:48:15,051 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started APPLY_REQUEST_VALUES 2 ----
13:48:15,052 INFO [stdout] (http--0.0.0.0-8090-2) PostConstruct DataBean
13:48:15,124 INFO [stdout] (http--0.0.0.0-8090-2) ..............................................................
13:48:15,124 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started PROCESS_VALIDATIONS 3 ----
13:48:15,126 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started UPDATE_MODEL_VALUES 4 ----
13:48:15,127 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started INVOKE_APPLICATION 5 ----
13:48:15,127 INFO [stdout] (http--0.0.0.0-8090-2) ---- Started RENDER_RESPONSE 6 ----
13:48:15,387 INFO [stdout] (http--0.0.0.0-8090-2) -- Finished Request --
It's also good. The table is now sorted as it should. Now I click on the 10th line to select the item with id 53;
13:48:28,295 INFO [stdout] (http--0.0.0.0-8090-4) -- Started Request --
13:48:28,296 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started RESTORE_VIEW 1 ----
13:48:28,361 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started APPLY_REQUEST_VALUES 2 ----
13:48:28,363 INFO [stdout] (http--0.0.0.0-8090-4) PostConstruct DataBean
13:48:28,487 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started PROCESS_VALIDATIONS 3 ----
13:48:28,501 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started UPDATE_MODEL_VALUES 4 ----
13:48:28,514 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started INVOKE_APPLICATION 5 ----
13:48:28,514 INFO [stdout] (http--0.0.0.0-8090-4) navigateToDetail() called
13:48:28,516 INFO [stdout] (http--0.0.0.0-8090-4) Constructing ElementEditDataBean
13:48:28,517 INFO [stdout] (http--0.0.0.0-8090-4) Setting ActiveElement to 42
13:48:28,518 INFO [stdout] (http--0.0.0.0-8090-4) ---- Started RENDER_RESPONSE 6 ----
13:48:28,748 INFO [stdout] (http--0.0.0.0-8090-4) -- Finished Request --
Please note that there is PostConstruct DataBean
no sorting AFTER . (I am assuming, since I am using a form on a table, the datatable does not know that the sort may have changed.)
As a result, the element with id 42 is passed. (Element 42 is in the 10th position for the unsorted case)
As a result, navigateToDetailView (element) is now launched with a different element expected ...
The problem consists of c. that the sorted collection gets reset by the postconstruct method. I also know that it can be solved with Conversation.
But I wonder if there is such a way of statelessness? (I don't want to run conversations for every sort / page navigation2)
Any ideas?
Edit 1: SortingHelper is a native class that looks like this:
@Named
public class SortingHelper {
/**
* Sorts two integers correctly.
* @param o1 integer 1
* @param o2 integer 2
* @return negative value if o1 is less, 0 if equal, or positive value if greater
*/
public int sortNumericCallback(Object o1, Object o2) {
System.out.print(".");
int i1 = Integer.parseInt((String) o1);
int i2 = Integer.parseInt((String) o2);
return (i1 == i2) ? 0 : (i1 > i2) ? 1 : -1;
}
}
(Datatable surface fails when sorting integers, or lets say it sorts numbers, lexicographic: 11 <5, etc.)
But even if I don't mind sorting and don't use the NO arbitrary sort function, the result is the same.
source to share
According to feedback, I have now modified the Databean to start a conversation after loading.
@ConversationScoped
Class DataBean{
private List<Elements> elements;
@Inject
private Conversation conversation;
@PostConstruct
private void loadElements(){
if (this.conversation.isTransient())
this.conversation.begin();
//fetch data from database.
}
}
in the navigation functions of my pageController I will stop this conversation again:
public String navigateToDetailView(Element element) {
//pass element to next databean.
conversation.end();
//...
return "detailView";
}
This, however, caused an issue that switching from the browser leads to the wrong targets (they end up).
To solve this problem, I created a custom filter that basically disables caching for browsers, so they refresh the page to history.back()
and therefore have a completely new, valid conversation ID.
The filter looks like this:
public class NoCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse hsr = (HttpServletResponse) res;
hsr.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
hsr.setHeader("Pragma", "no-cache"); // HTTP 1.0.
hsr.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(req, res);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
}
with this part in web.xml:
<filter>
<filter-name>noCacheFilter</filter-name>
<filter-class>com.example.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>noCacheFilter</filter-name>
<url-pattern>*.xhtml</url-pattern>
</filter-mapping>
Now I just need to figure out how to stop conversations if the user does not choose to go from side, instead of calling showDetail () - Action.
source to share