JSF datatable with paging and sorting and browser-back - jsf-2

We are using Primefaces datatable component with sorting, pagination and a LazyDataModel in order to display search results. The backing bean of the search page is ViewScoped.
From the search result list a user can select a result item in order to view the details. This is implemented as a new page request, e.g. <h:link value="Show Details" outcome="showDetails?id=11234"/>. By clicking the browser back button the user will navigate back to the search result list.
The problem is that the datatable will be in initial state again, so it wont display the last selected sort order and page. This is the case in most browsers, only Firefox 11 seems to keep those DOM changes in cache. Neither IE nor Chrome.
Does anybody has a good approach how to handle this? We actually don't need "ajaxified" sorting and paging. We would prefer handling everything through ViewParams, but this seems not to be supported by primefaces.

I ran into this as well, and I developed a coding pattern that might help. It isn't really a PrimeFaces thing (which I am using as well) -- more of a technique for using book-markable links with JSF 2.
In the markup, I include all the parameters for the page as follows:
<f:metadata>
<f:viewParam name="orderID" value="#{bean.orderID}" />
<f:viewParam name="sortMode" value="#{bean.sortMode}" />
<f:viewParam name="firstRecord" value="#{bean.firstRecord}" />
<f:viewParam name="pageSize" value="#{bean.pageSize}" />
</f:metadata>
Each of those values is backed in the backing bean such that, if they are not set, they produce a suitable default value. For example:
public int getPageSize() {
if (pageSize < MIN_PAGE_SIZE) pageSize = DEF_PAGE_SIZE;
if (pageSize > MAX_PAGE_SIZE) pageSize = DEF_PAGE_SIZE;
return pageSize;
}
A link to this view can be generated as follows:
Now you implement user controls to change those values, and regenerate the table each time they are changed. You should still use AJAX for that -- for example:
<p:spinner value=#{bean.pageSize} >
<p:ajax update="tableID" />
</p:spinner>
In the setter methods, you have to trigger the re-generation of the table, but that's the gist of it. I hope this helps.
UPDATE:
To handle pagination, you simply need to add links for first page, prior page, next page, etc. The links will look something like this:
<h:link value="First Page" outcome="thisPage">
<f:param name="orderID" value="#{bean.orderID}" />
<f:param name="sortMode" value="#{bean.sortMode}" />
<f:param name="firstRecord" value="0" />
<f:param name="pageSize" value="#{bean.pageSize}" />
</h:link>
<h:link value="Next Page" outcome="thisPage">
<f:param name="orderID" value="#{bean.orderID}" />
<f:param name="sortMode" value="#{bean.sortMode}" />
<f:param name="firstRecord" value="#{bean.nextRecord}" />
<f:param name="pageSize" value="#{bean.pageSize}" />
</h:link>
etc. You need to implement properties in the backing bean to calculate what the firstRecord param ought to be.

Related

Primefaces commandButton inside dialog not firing backing bean's method

I've just started learning JSF and PrimeFaces, and as soon as I solve a problem (with your help), another one arises. I have a datatable showing some data about my application's users; in the last column, a commandButton invokes a dialog allowing the corresponding data to be edited. The dialog actually interacts with the backing bean, since the fields are correctly precompiled with the existing data, but the "Submit changes" commandButton doesn't fire the proper editUser() method!
I've searched everywhere for a solution to my problem, but none of the threads on the PrimeFaces forums nor any question here on Stack Overflow helped me: I tried all combinations of action, actionListener, inner <h:form>, outer <h:form>, even the dreaded nested <h:form>, but the underlying method is still not called.
Thank you all, people!
EDIT: I included some more xhtml. Just to be clear: in the datatable I'm implementing both single and multiple selection mechanisms. The single selection is performed by the editButton in the last column and triggers the editDialog that's giving me pain, while multiple selection is enabled by the checkboxes in the first column and is targeted by a commandButton at the bottom of the table that deletes all selected users; of course they store the selections in different fields in the backing bean (selectedUser and selectedUsers[], respectively).
xhtml file
<h:form id="tableForm">
<p:dataTable id="userList" var="user" value="#{userListBean.userList}"
selection="#{userListBean.selectedUsers}" rowKey="#{user.username}">
<!-- this is a checkbox column I use for multiple selection -->
<p:column selectionMode="multiple" style="width:2%"/>
<!-- other datatable columns -->
<!-- this is the button column that triggers the dialog -->
<p:column style="width:4%">
<p:commandButton id="editButton" update=":tableForm:editUserData"
oncomplete="PF('editDialog').show()" title="Edit" icon="ui-icon-pencil">
<f:setPropertyActionListener target="#{userListBean.selectedUser}"
value="#{user}" />
</p:commandButton>
</p:column>
</p:datatable>
<p:dialog id="editDlg" widgetVar="editDialog" header="Edit User"
showEffect="fade" hideEffect="fade" modal="true" dynamic="true">
<h:panelGrid columns="6" id="editUserData">
<p:outputLabel for="editUsername">Username:</p:outputLabel>
<p:inputText disabled="true" id="editUsername" value="#{userListBean.selectedUser.username}" />
<p:message for="editUsername" />
<!-- and other fields like that -->
</h:panelGrid>
<p:commandButton id="submitChanges" action="#{userListBean.editUser()}"
value="Submit changes" oncomplete="PF('editDialog').hide();" />
</p:dialog>
</h:form>
Backing bean
#ManagedBean(name="userListBean")
#ViewScoped
public class UserListBean {
private UserDTO selectedUser;
public UserListBean() {
}
//some methods...
public String editUser() {
System.out.println("------------------ EDIT TRIGGERED! -------------------");
System.out.println(selectedUser.getUsername());
//this stuff never gets printed, so the method is never called!
}
//getters and setters
}
Actually, the only thing didn't come to my mind turned out to be the one that worked.
I sorted out my issue by using THREE forms (as I mentioned in my question, I had already tried out all possible combinations of one and two forms, even nested ones), like this:
<h:form id="tableForm">
<!-- here lies the p:dataTable -->
</h:form>
<p:dialog>
<h:form id="dialogForm">
<!-- here lies the h:panelGrid with the editable fields -->
</h:form>
<h:form id="buttonForm">
<!-- and HERE goes the commandButton, alone -->
</h:form>
</p:dialog>
It looks like everyone solves this problem in ways that don't work for others :) .

can't refresh a selectOneMenu

first excuse my english if it's not correct ...
I've a probleme with a primeface's component, I'm trying to refresh a p:selectOneMenu from a p:commandButton, but it's doesn't work (it 's work on another xhtml page, but not here and I can't understand why ...)
First I select an item from an p:autocomplete which update a backingbean's attribute ( for example : userChoose ).
then the p:commandButton is able to call his listener and add userChoose to a list, but i can't refresh the selectOneMenu that display the list. I have to use another p:commandButton to refresh the list.
My form is included into a p:tabMenu in another xhtml page.
<p:autoComplete id="acPojo" value="#{forumBean.user}"
completeMethod="#{autoCompleteBean.completeUser}"
converter="#{userConverter}" forceSelection="true"
var="usr" itemLabel="#{usr.loginUtilisateur}" itemValue="#{usr}">
<p:column>
<h:outputText value="#{usr.loginUtilisateur}"/>
</p:column>
</p:autoComplete>
<p:commandButton value="ajouter" process="acPojo #this "
udpate=":tabView:formSujet:listeUser" actionListener="#{forumBean.addUser}"/>
<p:selectOneMenu value="#{forumBean.user}" converter="#{userConverter}" var="us" id="listeUser"
itemValue="#{us}" itemLabel="#{us.loginUtilisateur}">
<f:selectItems value="#{forumBean.newSujet.listeUserAllowed}" var="User"
itemValue="#{User}" itemLabel="#{User.loginUtilisateur}" />
<p:column>
<h:outputText value="#{us.loginUtilisateur}"/>
</p:column>
<p:ajax process="#this" />
</p:selectOneMenu>
<p:commandButton id="refreshAdmin" icon="ui-icon-arrowrefresh-1-w"
update=":tabView:formSujet:listeUser" />
Thanks for help.
From your code:
udpate=":tabView:formSujet:listeUser"
This has at least 2 (potential) mistakes. The right attribute name is update, not udpate. The one on your other button has however the right attribute name.
It that still doesn't work, then the other potential mistake is that the client ID tabView:formSujet:listeUser does not exist in HTML DOM tree (and thus JavaScript/jQuery is unable to find and replace it). That can happen if the <p:tabView> is dynamic (i.e. you're using <p:tabView value="#{bean.tabs}" var="tab">, because it prepents the tab index number in the final client ID like so tabView:0:formSujet:listeUser if it's the 1st tab.
But, after all, as both the dropdownlist and the commandbutton are in the same NamingContainer parent, you don't need an absolute client ID at all. Just the relative client ID should suffice:
update="listeUser"
Fix that on your both buttons.
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

Authorization in JSF2 components

In our project we have JSF2 pages used in internal network of a company. But we are going to open those pages to Internet. So pages will be available to everybody. But some fields will not be rendered if a user comes from Internet.
I know it is possible to write a rendered attribute for each component such as "userIsInRole". But additional "rendered" control doesn't seem to be efficient and elegant method. So I plan to mark the components which are going to be rendered online by using a custom attribute as shown below:
<h:inputText context="internet" />
...
In the renderer of the inputText or component code:
if(user is from Internet && context = "internet") {
return true; // or render... whatever
}
If a component is not marked as Internet, then it means it will be available (rendered) only from inside of the company.
Is it possible implement authorization by using JSF2 components according to a given attribute? Is there any better options? Or should I design separate pages for internet users?
We use: PrimeFaces + Spring in our project.
Thank you
This is ridiculous. What's the difference in effort of ultimately using
<h:inputText context="internet" />
versus
<h:inputText rendered="#{intranet}" />
?
If your sole problem is that you need to repeat the whole condition everytime ending up in ugly code like so
<h:inputText rendered="#{not (user.hasRole('internet') and context eq 'internet')}" />
<h:inputText rendered="#{not (user.hasRole('internet') and context eq 'internet')}" />
<h:inputText rendered="#{not (user.hasRole('internet') and context eq 'internet')}" />
...
then just refactor the condition to a single request scoped variable
<c:set var="intranet" value="#{not (user.hasRole('internet') and context eq 'internet')}" scope="request" />
...
<h:inputText rendered="#{intranet}" />
<h:inputText rendered="#{intranet}" />
<h:inputText rendered="#{intranet}" />
...

JSF 2.0 commandLink: how to pass a the page number as a parameter in the browser?

I want to pass the number each of clicks page of search results from tables in the browser with JSF commandLink Tag. But it does not work. I always get the following URL: http://localhost:myport/kundenVerwaltungWebClient/searchPerson.jsf
The URL in the browser should look something like this:
http://localhost:myport/kundenVerwaltungWebClient/searchPerson.jsf?pageNum=6
Here is the view (searchPerson.xhtml):
... <!-- The paging links -->
<t:dataList value="#{controller.pages}" var="page">
<h:commandLink value="#{page}" actionListener="#{controller.page}"
rendered="#{page != controller.currentPage}" >
<f:param name="pageNum" value="#{page}" />
</h:commandLink>
<b><h:outputText value="#{page}" escape="false"
rendered="#{page == controller.currentPage}" /></b>
</t:dataList> ...
Here is the managed bean:
#ManagedBean #SessionScoped public class Controller { private String pageNum; ... //Getter and Setter }
Can someone please tell me what I do wrong here?
I thank you in advance.
The <h:commandLink> sends a POST request, but you apparently want to send a GET request. You need to use <h:link> instead of <h:commandLink> if you want to send a GET request.
<h:link value="#{page}" rendered="#{page != controller.currentPage}" >
<f:param name="pageNum" value="#{page}" />
</h:link>
(this doesn't require a <h:form> by the way, so you can safely remove it if you don't have any other command links/buttons in the view)
To replace the actionListener job, put this in the top of your view:
<f:metadata>
<f:viewParam name="pageNum" value="#{controller.currentPage}" />
<f:event type="preRenderView" listener="#{controller.page}" />
</f:metadata>
See also:
Communication in JSF 2.0 - Processing GET request parameters

preRenderView-Event triggered before an action (commandbutton)

I am in a little bit of trouble with my preRenderView-Event. My page also uses a commandbutton to submit data but unfortunately the preRenderView-Event is always called before the buttons action is invoked.
The preRenderView-Event is responsible for loading data from a service. Since the page is simple and doesnt have to store any data I dont need any Session or ViewScoped-Beans.
As far as I understood the event is called in the Render-Response-Phase and the action in the button should happen somewhat earlier.
<h:body>
<f:metadata>
<f:viewParam name="id" value="#{bean.id}" converter="converter.SetOfLongConverter"/>
<f:event type="preRenderView" listener="#{bean.initializeData}" />
</f:metadata>
<ui:composition template="/META-INF/templates/myTemplate.xhtml">
<ui:param name="title" value="#{bean.title}"/>
<ui:define name="content">
<h:outputText value="#{bean.description}" />
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Model" for="model" />
<h:inputText id="model" value="#{bean.model}" />
<h:outputLabel value="Manufacturer" for="manufacturer" />
<h:inputText id="manufacturer"
value="#{bean.manufacturer}" />
<h:outputLabel value="Year" for="year" />
<h:inputText id="year" value="#{bean.year}" />
</h:panelGrid>
<h:commandButton value="Create" type="submit" action="#{bean.create}" rendered="#{bean.createMode}"/>
<h:commandButton value="Save" type="submit" action="#{bean.save}" rendered="#{!bean.createMode}" />
</h:form>
</ui:define>
</ui:composition>
</h:body>
I added some Console-Messages to verify when the method in the bean is called and when I have the preRenderView-event it never happens.
Any advice what I am doing wrong here?
The <f:metadata> has to go inside <ui:define> of an <ui:composition>.
See also <f:metadata> tag documentation (emphasis mine):
Declare the metadata facet for this view. This must be a child of the <f:view>. This tag must reside within the top level XHTML file for the given viewId, or in a template client, but not in a template. The implementation must insure that the direct child of the facet is a UIPanel, even if there is only one child of the facet. The implementation must set the id of the UIPanel to be the value of the UIViewRoot.METADATA_FACET_NAME symbolic constant.
The simple reason is because the metadata is supposed to be view-specific, not template-specific.
Update: it turns out that the action method is not invoked at all. You've there a rendered attribute on the command button which seems according the symptoms to be dependent on a request based variable instead of a view based variable. Putting the managed bean in the view scope should fix this problem. See also commandButton/commandLink/ajax action/listener method not invoked or input value not updated

Resources