Primefaces' commandLink works only on the first page of a dataTable - jsf

I have a column with p:commandLink inside p:dataTable, which has a paginator. When user clicks on a commandLink, a dialog opens, displaying data of that row.
datatable html code:
<p:dataTable id="dtSample" value="#{sessionBean.sampleList}"
binding="#{requestBean.dtSampleList}"
paginator="true" paginatorPosition="bottom"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="#{10,25,50}"
currentPageReportTemplate="({startRecord} of {totalRecords})"
var="item" emptyMessage="No entries." rows="10" rowKey="#{item.id}">
<p:column headerText="Id">
<p:commandLink id="lnkSample"
action="#{requestBean.onLinkClick(item)}"
oncomplete="PF('dlgSample').show();"
update="#(.dialogClass)">
<h:outputText value="#{item.id}" />
</p:commandLink>
</p:column>
<p:column headerText="Code">
<h:outputText value="#{item.code}" />
</p:column>
<p:column headerText="Description">
<h:outputText value="#{item.descr}" />
</p:column>
</p:dataTable>
Request bean:
public class RequestBean {
private SessionBean sessionBean;
private DataTable dtSampleList;
public void init() {
// load samle list
loadSampleList();
}
public String onLinkClick(Sample sample) {
getSessionBean().setSelectedSample(sample);
return "success";
}
private void loadSampleList() {
List<Sample> list = new ArrayList<Sample>();
for (int i = 0; i < 100; i++) {
Sample tmp = new Sample();
tmp.setId(new BigDecimal(i + 1));
tmp.setCode("code" + (i + 1));
tmp.setDescr("desc" + (i + 1));
list.add(tmp);
}
getSessionBean().setSampleList(list);
}
// getters and setters
}
Session bean:
public class SessionBean implements Serializable {
private static final long serialVersionUID = 1L;
private List<Sample> sampleList;
private Sample selectedSample;
// getters and setters
}
dialog html code:
<p:dialog id="dlgSample" closeOnEscape="true" widgetVar="dlgSample"
styleClass="dialogClass" modal="true">
<p:panelGrid columns="1">
<h:outputText value="Id: #{sessionBean.selectedSample.id}" />
<h:outputText value="Code: #{sessionBean.selectedSample.code}" />
<h:outputText value="Description: #{sessionBean.selectedSample.descr}" />
</p:panelGrid>
</p:dialog>
When I click on a link on the first page of a datatable, link action is executed and a dialog, displaying row data, is properly refreshed. But when I move to any of the following pages of a datatable, clicking a link doesn't refresh the data in a dialog (the link action is not called, so the data in a dialog is wrong - selectedSample variable has old value). Of course when I go back to the first page of the datatable, command link works again (action method is called and data is refreshed).
What am I doing wrong? Why is action method not called on any datatable page?
I'm using Primefaces 5.2.

Looks like the problem was in a PF dataTable component. There were first and rows attributes missing and after adding them, commandLinks on all pages work as expected.

What am I doing wrong? Why is action method not called on any datatable page?
A common misstake leading to that behaviour is that your state is not consistent: To process the form submit, JSF will recreate the exact same view as in the prior request, and then apply the changes (execute the action)
If this new view is now different than the view before the submit was, every action is aborted and not invoked. (Different in terms of involved elements. I.e. if your commandLink was rendered="true" before submitting, it needs to be rendered="true" during the APPLY_REQUEST_VALUES-Phase).
So, from your description I would assume, that your table is falling back to page 1, which will remove any link on page 2 from the view and abort it's view action, because the element is no longer rendered.
For the same reason, links on page 1 are working, because even if your table looses track of the current page - it will render page 1, so you have the same view as before, so the submission works.
You could easily verify this, by increasing the number of elements per page and see that every link moved from page 2 to page 1 is now working.
But without seeing the whole bean I can only assume this. For easy testing, set your bean to #SessionScoped and see if this resolves it.
For more ideas, see this post: commandButton/commandLink/ajax action/listener method not invoked or input value not updated

When putting components such as p:commandLink in a p:dataTable row, you should always specify
process="#this"
So, in your case, I would try :
<p:commandLink id="lnkSample"
action="#{bean.onLinkClick(item)}"
oncomplete="PF('dlgSample').show();"
update="#(.dialogClass)">
<h:outputText value="#{item.id}" />
</p:commandLink>
Doing so, you are sure that only the click on the link will be processed, and no other submission will be performed.

Related

Primefaces Lazyloading renders rows as html on pagination

I am building a single page application with Primefaces 6.1, JSF 2.2 and running it on Wildfly, which has an option to search for users. Initial view renders only search panel with various filters and on click of search, user table will be queried and set of data is returned and then a block is rendered in the view which contains datatable. Below is the lazyloading code for search users.
public void getUsers(ActionEvent event) {
//few lines to get filterData
this.lazyUsers = new LazyDataModel<UserModel>() {
private static final long serialVersionUID = 1L;
#Override
public List<UserModel> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
filterObject.setStartCount(new Long(first));
filterObject.setCountPerPage(new Long(pageSize));
List<UserModel> list = userService.getUsers(filterObject);
if (list != null)
this.setRowCount(list.get(0).getTotalNumberOfRecords().intValue());
RequestContext context = RequestContext.getCurrentInstance();
if(context!=null)
context.addCallbackParam("totalRecords", this.getRowCount());
return list;
}
};
}
and here's my table view.
<p:outputPanel id="users" autoUpdate="true">
<p:outputPanel rendered="#{users.displayusers}">
<h5 class="title">
<h:outputText value="#{msg.header_title}"></h:outputText>
</h5>
<p:dataTable id="userTable" lazy="true" paginator="true"
rows="2"
emptyMessage="#{msg.emtpymsg}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="2,4,5"
rendered="#{null != users.lazyUsers && not empty users.lazyUsers }"
value="#{users.lazyUsers}" var="user"
rowIndexVar="userRow">
<p:column headerText="#{msg.Name}">
<h:outputLabel class="tdRow" value="#{user.name}" />
</p:column>
<p:column headerText="#{msg.Phone}">
<h:outputLabel class="tdRow" value="#{user.phone}" />
</p:commandLink>
</p:column>
<p:column headerText="#{msg.Address}">
<h:outputLabel class="tdRow" value="#{user.address}" />
</p:column>
<p:column headerText="#{msg.Email}">
<h:outputLabel class="tdRow" value="#{user.email}" />
</p:column>
</p:dataTable>
</p:outputPanel>
</p:outputPanel>
Everything renders when search happens but then when I paginate it searches for the next page data, and gets the next page data, but it renders it as plain html removing the datatable. On inspection of html, I can only see tr,td as response from server in network tab, and only contents within it are rendered directly into div. The datatable is not preserverd. Could anyone please let me know what's happening here.
I also tried to capture page event within datatable and added update value as
<p:ajax event="page" update="userContainer:userTable"></p:ajax>
but again it did not help. Hope to find some help.
Update
I noticed, this happens for any ajax operation through datatables, like, filtering, sorting, rowsperpage changed etc., Is there any way to identify on exactly what's wrong with this?
You change the value of rendered attribute. Try to encapsulate the dataTable ino another component, and re-render this outer component.
Also could be helpful if you place a dummy table in the template, that is always rendered, EG:
<p:dataTable disabled="true" />
This will make sure all the dataTable needed libraries are loaded at first load of your page...
The solution for this problem was very simple infact.
I had to keep <p:ajax event="page" update="userContainer:userTable"></p:ajax> and rather than update, I had to use process and also with ignoreAutoUpdate="true"
<p:ajax event="page" ignoreAutoUpdate="true" process="userContainer:userTable"></p:ajax>
That fixed the issue. Hope this will be useful to someone in future.

Primefaces datatable update in f:facet name="header" not working

I want to update a button in the header facet of a primefaces datatable and it does not work. I copied the button outside the datatable everything works fine.
The update should happen when the filter event of the datatable gets fired. I explicitly update the datatable, the outside- and the inside-button.
The intention is to display a button with an icon when no filter is set and another icon when a filter is used. In this example I simplyfied the use case: when no filter is used there is a open-lock icon, if I type something in a filter a closed-lock icon should be displayed. To release the lock one has to click the button (I did not implement the deletion of the filter in the datatable).
From what I understand I use the correct ID of the button inside the header. So I do not know why this does not work?
I am using mojarra 2.2 and primefaces 6.
<h:form id="id_form">
<p:dataTable
id="id_table"
value="#{stateController.names}"
var="currentName">
<p:ajax
event="filter"
listener="#{stateController.markLocked()}"
update="id_table id_form:id_table:id_button_inside id_form:id_button_outside"/>
<p:column
filterBy="#{currentName}"
filterMatchMode="contains">
<f:facet name="header">
<p:commandButton
id="id_button_inside"
action="#{stateController.markUnlocked()}"
icon="#{stateController.locked ? 'ui-icon-locked' : 'ui-icon-unlocked'}"
update="id_form"/>
</f:facet>
<h:outputText value="#{currentName}" />
</p:column>
</p:dataTable>
<p:commandButton
id="id_button_outside"
action="#{stateController.markUnlocked()}"
icon="#{stateController.locked ? 'ui-icon-locked' : 'ui-icon-unlocked'}"
update="id_form"
/>
</h:form>
#Named(value = "stateController")
#SessionScoped
public class StateController implements Serializable
{
private boolean locked;
private List<String> names;
#PostConstruct
private void init()
{
locked = false;
names = new ArrayList<>();
names.add("peter");
}
public void markLocked()
{
locked = true;
}
public void markUnlocked()
{
locked = false;
}
// getter + setter omitted
}
I also tried to put a button in a separate column. With this button (which is displayed in every row of the datatable) everything works fine as well.
It's a bit late, but maybe someone will find it useful some day.
To solve Filou's problem, you need to define remoteCommand outside dataTable and make it update dataTable's header facet.
<p:remoteCommand name="rmtCommand" update="id_form:id_table:id_button_inside"/>
<p:dataTable
id="id_table"
value="#{stateController.names}"
var="currentName">
<p:ajax
event="filter"
listener="#{stateController.markLocked()}"
oncomplete="rmtCommand()"/>

Primefaces datatable onEdit() edits the wrong entry if an entry has been added before refreshing the view

i've got a question concerning Primefaces (version 5.0) and their datatable.
If I have a p:datatable and I want to edit a cell or row (doesn't matter - behaviour is the same in that case) AND someone added a new entry to that database, I edit the new entry instead of the old.
Well. Here are some code-snippets:
my index.xhtml:
<h:form id="viewChanges">
<p:dataTable id="changeTable"
widgetVar="changeTable"
value="#{changeController.list}" var="item"
selection="#{changeController.selectedChangeEntry}"
selectionMode="single" rowKey="#{item.id}"
sortBy="#{item.id}" sortOrder="descending"
editable="#{lDAPUserController.loggedIn}"
style="width: auto">
<p:ajax event="rowEdit"
listener="#{changeController.onRowEdit}"
update=":growlInfo, :growlError, :viewChanges:changeTable" />
..output of columns..
<p:column style="width:32px">
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
my changeController.java
#ManagedBean(name = "changeController")
#ViewScoped
public class ChangeController implements java.io.Serializable {
attributes, getter, setter, other methods etc..
public void onRowEdit(RowEditEvent event) {
FacesMessage msg = new FacesMessage("Change Edited", ((Change) event.getObject()).getId());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
Well with this it's already possible to replicate the error.
Little Example: We have a dataset with 3 entries. - id=1,2,3
We open a browser[1] and see the 3 datasets. We now open a private tab[2] and go to the same site and add a new entry. Without refreshing [1] we edit the entry with id=3. All of the sudden we edited entry with id=4.
Could someone explain me why this is happening? Is it me or is it bug in primefaces? Wrong Scope?

Get value of a randomly generated jsf Table

I am new to this , i am using primefaces and generating a dynamic table, this datatable gets its vaules from a list created by a managed bean.
I need to set the property of another managed bean with the value of the column generated by the datatable.
Eg: Col1, has a particular value, i want to click on that value and a dialog box should appear displaying that col1 value
<p:dataTable id="dataTable" var="c" value="#{databaseSearch.customerList}"
paginator="true" rows="10" paginatorAlwaysVisible="false"
paginatorTemplate="Page {CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} Rows per page {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15,30">
<p:column>
<f:facet name="header">
<h:outputText value="Machine" />
</f:facet>
<p:commandButton id="xxx" value="#{c.machine}" action="#{updateEntry.setMachine(c.machine)}" ajax="true" onclick="dlg1.show();" styleClass="ui-Machinebutton"/>
<!-- styleClass="ui-Machinebutton" -->
</p:column>
The managed bean 'updateEntry' has getter and setter methods.
Bean
#ManagedBean
#SessionScoped
public class UpdateEntry implements Serializable {
public Long Machine;
public Long getMachine() {
return Machine;
}
public void setMachine(Long Machine) {
this.Machine = Machine;
}
}
Dialog Box
<p:dialog id="modalDialog" header="Modal Dialog" widgetVar="dlg1" modal="true" height="100" dynamic="True">
<h:outputText value="#{updateEntry.machine}" />
</p:dialog>
I do understand that in order to pass a parameter to the dialog box i would need the help of a managed bean. However, the datatable is generated randomly and i am not able to pass that particular random value. So how do i pass the random value of a command button and set the managed bean when it is clicked ?
Objective
When the command button is clicked, i want its value to be passed to a managed bean, since this is randomly generated dataTable, i am not sure how i can achieve that.
Update
It Works ! The same code updates updateEntry.machine but the problem is since clicking on the button opens the dialog box first before the updateEntry.machine is updated to a new value, the dialog box displays the previously clicked option. Does the 'Action' happen before the 'Onclick' ? or is it the other way around ? I need to update the bean first and then open the dialog box for it to show the newer value. I have tried both True & False of the dynamic property of the dialog box, not sure what is that for.
Also i need to refresh the page to get the new value loaded in dialog box, else no matter which command button i click it shows me the value of the command button i first clicked, post the page refresh, it shows the value of the command button last clicked.
u can declare a new object for updateEntry inside your current class, c
then in your action button, call a method in c to change updateEntry machine value such as:
<p:commandButton id="xxx" value="#{c.machine}" action="#{c.change()}" ajax="true" onclick="dlg1.show();" styleClass="ui-Machinebutton"/>
while in c class
private void change(){
//set updateEntry.machine value here
}
so your dialog box should look like
<p:dialog id="modalDialog" header="Modal Dialog" widgetVar="dlg1" modal="true" height="100" dynamic="False">
<h:outputText value="#{c.updateEntry.machine}" />
</p:dialog>
I found the problem ...
I changed the code to below and it worked !
<p:commandButton id="basic" value="#{c.machine}" action="#{updateEntry.setMachine(c.machine)}" ajax="true" oncomplete="dlg1.show();" styleClass="ui-Machinebutton"/>
The trick here was to first set the 'Machine' variable and upon completion load the dialog box which reads back the same 'Machine' variable

Add a new row to a p:datatable then submit content

I'm trying to add a new row in a Primefaces datatable, then I would like to submit the content of this table and do some business logic. The datatable model is a collection that is maintained in a ViewScoped managed bean.
I'm using JSF 2.1 with Primefaces 3.3.
Short example:
<h:form id="my-form">
<p:dataTable value="#{testBean.list}" var="s" id="datatable">
<p:column>
<h:inputText value="#{s}"/>
</p:column>
<f:facet name="footer">
<p:commandButton value="Add row" action="#{testBean.addRow()}" process="#form" update="#form" immediate="true" />
<p:commandButton value="Do stuff" action="#{testBean.doSomeLogic()}" process="#form" update="#form"/>
</f:facet>
</p:dataTable>
</h:form>
Managed Bean:
#ManagedBean
#ViewScoped
public class TestBean implements Serializable {
private List<String> list;
public TestBean() {
}
#PostConstruct
public void init() {
list = new ArrayList<String>();
list.add("one");
list.add("two");
}
public void addRow(){
list.add(new String());
}
public void doSomeLogic(){
for (String string : list) {
System.out.println(string);
}
}
// getters and setters
}
What actually happens:
the user clicks on "add row" button, a new row is added (I need immediate to be true so no validation is done, those fields are part of a bigger form).
the user clicks on "do stuff", the collection has the right size (with new rows) but the user's input in not taken into account (neither modification to pre exiting rows, nor new values in freshly added rows).
What can I do to submit the new values too? I'm only beginning JSF and I'm not sure I'm already 100% getting it.
Thanks for your help.
Possible duplicates:
Add a row to h:dataTable via AJAX with request-scoped bean without losing the row data
How to Dynamically add a row in a table in JSF?
JSF datatable: adding and removing rows clear rows values
Edit: problem is solved thanks to Jitesh, a working example can be found here: JSF2, can I add JSF components dynamically?
The only problem is you are using immutable object in inputText. To understatnd this check out BaluC's Answer
According to it "As being an immutable object, the String doesn't have a setter method. The will never be able to set the entered value."
Try to remove immediate attribute from the commandButton you will find that on insertion of each row the data will be cleared.
If I understand correctly, there are some validations elsewhere in the form that are failing. When any of the submitted form values fail validation then none of the submitted values are applied to the managed bean unless immediate is used. This is why it seems that you are able to add a new row but not with the doStuff method. You did not add immediate to doStuff.
But stating that there are a few things you could do much more cleanly and efficiently.
First, the action attribute should really be used for navigation actions. JSF expects that methods bound to an action have a return value that represents the navigation result. For void methods it is better to use actionListener. For more information on the difference between action and actionListener read here: Differences between action and actionListener
Secondly, why not just set process and update to only the data table component and then you don't have to worry about other form validations? Here is an example:
<h:form id="my-form">
<p:dataTable value="#{testBean.list}" var="s" id="datatable">
<p:column>
<h:inputText value="#{s}"/>
</p:column>
<f:facet name="footer">
<p:commandButton value="Add row" actionListener="#{testBean.addRow}"
process=":my-form:datatable" update=":my-form:datatable" />
<p:commandButton value="Do stuff" actionListener="#{testBean.doSomeLogic}"
process=":my-form:datatable" update=":my-form:datatable" />
</f:facet>
</p:dataTable>
</h:form>

Resources