Primefaces DataTable with SubTable and multiple selection - jsf

I would like to have a table of bookings grouped by clients. From this table the user shall be able to select multiple booking for billing. So I am trying to use a SubTable for the grouping, however, I am unsure how to realize to selection functionality. Apparently the subtable doesn't allow the selection attribute and if I set the selection attribute on the parent DataTable I don't know how to choose the rowKey.
That is my try:
<p:dataTable style="border: 0px;" value='#{clientController.allClients}'
var='client' rowKey="#{item.id}" selectionMode="multiple"
selection="#{bookingController.bookingsToBill}">
<p:subTable value='#{client.billableBookings}' var='item'>
<f:facet name="header">
<h:outputText style="font-weight:bold;" value="#{client.name}" />
</f:facet>
<p:column>
<f:facet name="header">
<h:outputText value="Booking"/>
</f:facet>
<h:outputText value="#{item.title}"/>
</p:column>
</p:subTable>
</p:dataTable>
Well, this results in the following error when submitting the selection:
java.lang.NullPointerException
java.lang.reflect.Array.newArray(Native Method)
java.lang.reflect.Array.newInstance(Array.java:52)
org.primefaces.component.datatable.DataHelper.decodeMultipleSelection(DataHelper.java:238)
org.primefaces.component.datatable.DataHelper.decodeSelection(DataHelper.java:224)
org.primefaces.component.datatable.DataTableRenderer.decode(DataTableRenderer.java:64)
javax.faces.component.UIComponentBase.decode(UIComponentBase.java:787)
javax.faces.component.UIData.processDecodes(UIData.java:1162)
org.primefaces.component.datatable.DataTable.processDecodes(DataTable.java:531)
javax.faces.component.UIForm.processDecodes(UIForm.java:225)
javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1176)
javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1176)
javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:933)
com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:409)
Is multiple selection supported for DataTables with SubTables? If so, how to do it right? If not, which way would you suggest to achieve a similar result?
I'm using: Primefaces 3.1.1 - Mojarra JSF 2.1 - Tomcat 6.0.14

Have you analyzed this solution described in Primefaces' showcase?
It basically boils down to this:
<p:dataTable style="border: 0px;" value='#{clientController.allClients}'
var='client' rowKey="#{item.id}"
selection="#{bookingController.bookingsToBill}" >
<p:subTable value='#{client.billableBookings}' var='item'>
<f:facet name="header">
<h:outputText style="font-weight:bold;" value="#{client.name}" />
</f:facet>
<p:column selectionMode="multiple" />
<p:column>
<f:facet name="header">
<h:outputText value="Booking"/>
</f:facet>
<h:outputText value="#{item.title}"/>
</p:column>
</p:subTable>
Or try with the ajax event listener, bound to your BookingController:
<p:ajax event="rowSelect" listener="#{bookingController.rowSelected}" />
<p:ajax event="rowUnselect" listener="#{bookingController.rowUnselected}" />
And you update your own list of selected items in these two functions:
List<Booking> selectedBookings = new ArrayList<>();
...
public void rowSelected(SelectEvent event) {
Booking book = (Booking) event.getObject();
selectedBookings.add(book);
}
public void rowUnselected(UnselectEvent event) {
Booking book = (Booking) event.getObject();
selectedBookings.remove(book);
}
It's not very elegant, but it brought my logic to work after getting this rather obscure NullPointerException.

Related

add Dynamic Columns in addition to Static columns

I have a dataTable with some columns, which are not supposed to change.
<p:dataTable id="datatable" value="#{phase.tasks}" var="task">
<p:commandButton id="Testcycles"
action="#{testprojectProducer.doAddTestCycle(phase)}"
update=":formID:Tasks" title="add Test "
icon="ui-icon-plus fa fa-plus">
<!-- <p:ajax oncomplete="#{testprojectProducer.doAddTest(phase)}" update=":formID:Tasks"></p:ajax> -->
</p:commandButton>
<f:facet name="header">
<h:panelGrid>
<h:panelGroup columns="2"
style="display:block; text-align:right">
<p:column>
<f:facet name="header">
Task
</f:facet>
<h:outputText value="#{task.name}" id="description"
title="#{task.description}" />
<pe:tooltip for="description" value="#{task.description}" />
</p:column>
<p:column>
<f:facet name="header">
Tool
</f:facet>
<p:commandLink value="#{task.tool}" ajax="false"
action="#{fileLinkController.openFile()}" />
</p:column>
<p:column>
<f:facet name="header">
Result
</f:facet>
<h:outputText value="#{task.resultDocuments}" />
</p:column>
In addition to that, the user should have the possibility, to add new columns.
<p:columns value="#{task.listTest}"
var="sometest">
<f:facet name="header">
Test
</f:facet>
<p:inputText id="comment"
value="#{sometest.description}">
</p:inputText>
</p:columns>
</p:dataTable>
Here is the method where I add my new Tests.
public void doAddTest(Phase phase) {
for (Task task : phase.getTasks()) {
task.getListTest().add(new Test(task));
}
}
I'm aware of the fact that it is not possible to control the columns on a per-row basis, but I really need some advice, how I can keep my static columns and add the dynamic ones.
doAddTest() is invoked with a button, you can find under the Tag.
It is first of all important that the extra columns are displayed in the table and the user has the option to leave a comment. with this method, the content of the columns will be written into the database.
public String doSave() {
if (testprojectProducer.isAddMode()) {
testprojectAddEvent.fire(testprojectProducer.getTestproject());
}
} else {
testprojectUpdateEvent.fire(testprojectProducer.getTestproject());
}
return Pages.LIST_TESTPROJECT;
}

p:dataTable unselect row without using meta key (selecting works)

I'm using:
Server: Wildfly 8.2
JSF: 2.2
Primefaces: 5.2
JDK:8
I have a datatable with multiple selection. I use rowSelectMode='add' so clicking on different rows without modifier keys adds new rows instead of unselecting all selected ones.
<p:dataTable id="table"
value="#{ixController.files}"
var="item" paginatorTemplate="{FirstPageLink} {PreviousPageLink}
{PageLinks}
{NextPageLink} {LastPageLink}"
selection="#{ixController.selecteds}"
rowKey="#{item.id}"
rowSelectMode="add"
selectionMode="multiple">
<p:column headerText="Files">
#{item.fileName}
</p:column>
As stated it works fine for adding rows, however I need to press Ctrl to unselect rows, and the customers wish to just click again on the row and unselect it.
Is that possible somehow?
Use check box instead that way they can select and un-select just by clicking it, follow Prime Faces example:
<p:dataTable id="checkboxDT" var="car" value="#{dtSelectionView.cars6}" selection="#{dtSelectionView.selectedCars}" rowKey="#{car.id}" style="margin-bottom:0">
<f:facet name="header">
Checkbox
</f:facet>
<p:column selectionMode="multiple" style="width:16px;text-align:center"/>
<p:column headerText="Id">
<h:outputText value="#{car.id}" />
</p:column>
<p:column headerText="Year">
<h:outputText value="#{car.year}" />
</p:column>
<p:column headerText="Brand">
<h:outputText value="#{car.brand}" />
</p:column>
<p:column headerText="Color">
<h:outputText value="#{car.color}" />
</p:column>
<f:facet name="footer">
<p:commandButton process="checkboxDT" update=":form:multiCarDetail" icon="ui-icon-search" value="View" oncomplete="PF('multiCarDialog').show()" />
</f:facet>
</p:dataTable>
I cant find an attiribute for this, however, there is a way:
<p:dataTable value="#{groupBean.groups}" var="grp" rowKey="#{grp.id}"
selectionMode="single" selection="#{groupBean.group}">
<p:ajax event="rowSelect" listener="#{groupBean.onRowSelect}" update="#all" />
As you see, when a row clicked, selectedItem setted to groupBen.goup object.
public void onRowSelect(SelectEvent event) {
if (prevGroupId == group.getId())
group = new PersonelGroup();
else
prevGroupId = group.getId();
}
In onRowSelect method, you can check if user click same row for the second time by comparing previous selected item id. And by instanciating group object to new, DataTable has been updated to no selection state.
It is important to not forget to add update="#all" attiribute of DataTable.

Dataexporter returns empty rows after filtering

I have a datatable in my xhtml view with filtering enabled. Additionally, there's the Primefaces export (for Excel) function in the context menu. When I use this function without filtering the datatable it works fine, but when I filter first and den export the data I get a file with empty rows.
This is my code:
<p:panel header="#{msg['prs.list']}">
<p:contextMenu for="persons">
<p:menuitem value="#{msg['com.view']}" icon="#{msg['icon.view']}"
action="#{personBean.redirectToEditPerson}"/>
<p:menuitem value="#{msg['student.new']}" icon="#{msg['icon.new']}"
action="#{personBean.redirectToNewStudent}"/>
<p:menuitem value="#{msg['prs.new']}" icon="#{msg['icon.new']}"
url="edit.xhtml"/>
<p:menuitem value="#{msg['report.export.excel']}" ajax="false" icon="#{msg['icon.export']}">
<p:dataExporter type="xls" target="persons" fileName="export" />
</p:menuitem>
</p:contextMenu>
<p:dataTable id="persons" var="person" value="#{personBean.personList}"
rowKey="#{person.id}" selection="#{personBean.selectedPerson}" selectionMode="single"
emptyMessage="#{msg['com.noEntries']}" paginator="true" rows="15">
<p:column headerText="Id">
<h:outputText value="#{person.id}"/>
</p:column>
<p:column headerText="#{msg['prs.name']}" filterBy="name" filterMatchMode="contains">
<h:outputText value="#{person.name}"/>
</p:column>
<p:column headerText="#{msg['prs.surname']}" filterBy="surname" filterMatchMode="contains">
<h:outputText value="#{person.surname}"/>
</p:column>
<p:column headerText="#{msg['prs.email']}" filterBy="email" filterMatchMode="contains">
<h:outputText value="#{person.email}"/>
</p:column>
</p:dataTable>
<f:facet name="footer">
<p:button value="#{msg['prs.new']}" icon="#{msg['icon.new']}"
outcome="edit"/>
</f:facet>
</p:panel>
I'm using Primefaces 4, JSF 2 and Java 7 on Wildfly 8
Solved. I found a warning in my log regarding the filteredValue property of the datatable.
[0m[33m17:26:45,701 WARNING [org.primefaces.component.datatable.DataTable] (default task-4) DataTable form:persons has filtering enabled but no filteredValue model reference is defined, for backward compatibility falling back to page viewstate method to keep filteredValue. It is highly suggested to use filtering with a filteredValue model reference as viewstate method is deprecated and will be removed in future.
I therefore added this property which then solved the problem
<p:dataTable id="persons" var="person" value="#{personBean.personList}"
rowKey="#{person.id}" selection="#{personBean.selectedPerson}"
selectionMode="single" emptyMessage="#{msg['com.noEntries']}"
paginator="true" rows="15" filteredValue="#{personBean.filtered}">
And added the following property in PersonBean as well
private List<PersonEntity> filtered;
public List<PersonEntity> getFiltered() { return filtered; }
public void setFiltered(List<PersonEntity> filtered) { this.filtered = filtered; }

Row Editor does not update value object's fields displaying via "columns" tag

I try to devise flexible table containing dynamic columns for CRUD operations.
I use p:columns component to make dynamic columns, but those columns do not change value when I try to update it.
<h:form id="form">
<p:dataTable id="dataTable"
var="item"
value="#{tableEditorController.items}"
editable="true"
paginator="true"
rows="10"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15">
<p:ajax event="rowEdit" listener="#{tableEditorController.onEdit}" />
<f:facet name="header">
#{tableEditorController.tableName}
</f:facet>
<p:columns value="#{tableEditorController.columns}"
var="column"
columnIndexVar="colIndex">
<f:facet name="header">#{column.header}</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{item[column.property]}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item[column.property]}" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:columns>
<p:column>
<f:facet name="header">test</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{item['activityName']}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item['activityName']}" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Edit" style="width:6%">
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
When I click on link in "Edit" column all the cells in selected row turn into text input fields.
If I edit a value in the "test" column and save, it saves properly as I expect.
But if I edit value using an input field in column which was generated by p:columns tag and try to save, it does not save changes.
I monitored http requests by Chrome Tools. The requests contain changed value in every case.
I ran my code under debugger and a RowEditEvent contains new data in first case and unchanged data object in second.
I cannot realize my mistake and only thing left I doubt is square braces notation.
I checked square braces notation using in "test" column and it works fine.
I use JSF 2.1.3 and Primefaces 3.3
Controller class is following:
#ManagedBean
#RequestScoped
public class TableEditorController {
private static List activityList;
static{
activityList = new ArrayList();
ActivityType at = new ActivityTypeImpl();
at.setActivityId(1L);
at.setActivityName("FirstActivity");
activityList.add(at);
at = new ActivityTypeImpl();
at.setActivityId(2L);
at.setActivityName("Second Activity");
activityList.add(at);
}
public void onEdit(RowEditEvent event){
FacesMessage msg = new FacesMessage("Row Edited");
msg.setDetail(event.toString() + event.getObject());
FacesContext.getCurrentInstance().addMessage(null, msg);
}
public void delete(Object id){
}
public List getItems(){
return activityList;
}
public String getTableName(){
return "Name of the Table";
}
public List<ColumnModel> getColumns(){
ColumnModel m1 = new ColumnModel("ID", "activityId");
ColumnModel m2 = new ColumnModel("Activity", "activityName");
return Arrays.asList(m1, m2);
}
}
I will appreciate any suggestion and/or idea.
I have found a way around p:columns. I replaced it with following code.
<c:forEach items="#{tableEditorController.columns}" var="column">
<p:column>
<f:facet name="header">#{column.header}</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{item[column.property]}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item[column.property]}" style="width:100%" disabled="#{not column.editable}"/>
</f:facet>
</p:cellEditor>
</p:column>
</c:forEach>
It works but I had to add JSTL library to project dependencies.

Exception thrown while accessing HTML DataTable in backing bean

I am building a simple application in JSF with the CrUD functionality. I am trying to implement edit functionality using the tomahawk component .I am unable to retrieve the selected row in my backing bean.
Here's my JSP file snip:
<t:dataTable id="data"
binding="#{selectOneRowList.dataTable}"
styleClass="scrollerTable"
headerClass="standardTable_Header"
footerClass="standardTable_Header"
rowClasses="standardTable_Row1,standardTable_Row2"
columnClasses="standardTable_Column,standardTable_ColumnCentered,standardTable_Column"
var="car"
value="#{selectOneRowList.list}"
sortColumn="#{selectOneRowList.sortColumn}"
sortAscending="#{selectOneRowList.sortAscending}"
preserveDataModel="false"
preserveSort="true"
preserveRowStates="true"
rows="10"
>
<h:column>
<f:facet name="header">
<h:outputText value="Select"/>
</f:facet>
<t:selectOneRow groupName="selection" id="hugo" value="#{selectOneRowList.selectedRowIndex}"
onchange="submit();" immediate="true"
valueChangeListener="#{selectOneRowList.processRowSelection}"/>
</h:column>
<h:column>
<f:facet name="header">
</f:facet>
<h:outputText value="#{car.id}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Cars" />
</f:facet>
<h:outputText value="#{car.type}" />
</h:column>
<t:column sortable="true">
<f:facet name="header">
<h:outputText value="Color" />
</f:facet>
<h:outputText value="#{car.color}" />
</t:column>
</t:dataTable>
Here's my backing bean SelectOneRowList.java :
public void editCar(ActionEvent event) {
System.out.println("Row number ## " + _selectedRowIndex.toString() + " selected!");
System.out.println("Datatable ::"+ dataTable);
System.out.println("Row Count ::" + dataTable.getRowCount());
dataItem = (SimpleCar) getDataTable().getRowData();
//selectedCar = (SimpleCar) _list.get(Integer.parseInt(_selectedRowIndex.toString()));
selectedCar = (SimpleCar) dataTable.getRowData();
System.out.println(dataTable.getRowData());
}
My DTO{Data Transfer Object} which is SimpleCar.java contains the variables ID, type, color and their respective setters/getters.
The dataItem variable is of type "SimpleCar". The dataTable is of type HTMLDataTable. I am able to get the the first 3 SOP's but the 4th SOP isn't printed. I receive the following exception on the server :
javax.faces.el.EvaluationException: Exception while invoking expression #{selectOneRowList.editCar}
org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:156)
javax.faces.component.UICommand.broadcast(UICommand.java:89)
javax.faces.component.UIViewRoot._broadcastForPhase(UIViewRoot.java:97)
javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:171)
org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:32)
org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:95)
org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:70)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:139)
org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:341)
On click of the edit button the editCar method in my backing bean is invoked. I need to get the data of the selected row in my backing bean. Why is the exception occurring ?
The above example is taken from the tomawhawk examples WAR distributed on the website. I have gone through many links including the ones on BalusC but none of them have helped.
Regards,
To use DataTable.getRowData() you would have to put the button tied to editCar() within the data table. So, on your JSP would need to add another column, such as:
...
<t:column sortable="true">
<f:facet name="header">
<h:outputText value="Color" />
</f:facet>
<h:outputText value="#{car.color}" />
</t:column>
<t:column sortable="false">
<h:commandButton value="Edit" action="#{selectOneRowList.editCar}" />
</t:column>
</t:dataTable>

Resources