Binding a subtable inside a datatable with JSF - jsf

My product is using JSF with Primefaces 2.2. What I need is to process a sub-datatable inside another datatable. The code is as below.
<h:form>
<p:dataTable id="myInvoiceTable"
value="#{globalBean.invoices}" var="invoice">
<p:column>
<f:facet name="header">
<h:outputText value="Bill ID" />
</f:facet>
<h:outputText value="#{invoice.billId}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Item" />
</f:facet>
<h:outputText value="#{invoice.item}" />
</p:column>
<p:rowExpansion>
<h:selectOneMenu id="reasons" value="#{invoiceAction.reason}">
<f:selectItem itemLabel="Reason 1" itemValue="Reason 1"></f:selectItem>
<f:selectItem itemLabel="Reason 2" itemValue="Reason 2"></f:selectItem>
</h:selectOneMenu>
<p:inputText id="activity_amount" value="#{invoiceAction.activity_amount}"/>
<p:commandButton value="PROCESS" action="invoice_process" update="actions_table">
</p:commandButton>
<p:dataTable id="actions_table" value="#{invoice.actions}" var="invAction">
<p:column>
<h:outputText value="#{invAction.reason}" />
</p:column>
<p:column>
<h:outputText value="#{invAction.activity_amount}" />
</p:column>
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
Here's my bean.
public class Invoice {
private int billId;
private String item;
private List<InvoiceAction> actions;
// Getter & setter here
}
public class InvoiceAction {
private String reason;
private double activity_amount;
private int billId;
// Getter & setter here;
}
But when the HTML is generated, it's not processed as desired. Only the select menu in the last row of datatable can receive the data chosen by user, others can't. Because all select menus in all rows are using the same object (#{invoiceAction}), and the bean just gets the value of the last component in the page. I knew the root cause but I don't know how to solve it. How to let they submit in the specified row?

First of all, I think your Invoice and InvoiceAction classes look just like normal Java objects. I feel that they should not be Managed Bean.
Besides, you're right that your #{invoiceAction} is not correctly binded to one of the actions in List<InvoiceAction> actions. Since you are using nested <p:dataTable>, I think you can take advantage of <p:cellEditor>. It would be something like this:
<p:rowExpansion>
<p:dataTable id="actions_table" value="#{invoice.actions}" var="invAction">
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{invAction.reason}" />
</f:facet>
<f:facet name="input">
<h:selectOneMenu id="reasons" value="#{invoiceAction.reason}">
<f:selectItem itemLabel="Reason 1" itemValue="Reason 1" />
<f:selectItem itemLabel="Reason 2" itemValue="Reason 2" />
</h:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{invAction.activity_amount}" />
</f:facet>
<f:facet name="input">
<p:inputText id="activity_amount" value="#{invoiceAction.activity_amount}" />
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
<p:commandButton value="PROCESS" action="invoice_process" update="actions_table" />
</p:rowExpansion>

Related

using <p:dataTable> cellEdit event is never fired in Primefaces

I am having issue in firing an ajax call on the cellEdit event of a Data Table. The table shows up just fine on the UI but nothing happens when I click any of the cell.
xhtml
<h:form>
<p:dataTable id="decisionTree" var="tree"
value="#{treeBean.content}" editable="true" editMode="cell"
styleClass="smallGrid">
<f:facet name="header">
Notes Decision Tree
</f:facet>
<p:ajax event="cellEdit" listener="#{treeBean.onCellEdit}"
immediate="true" update=":#{p:component('notesTextArea')}" />
<p:column headerText="Comment Type">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.commentType}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.commentType}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="MTCNs">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.mtcns}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.mtcns}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Call Type">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.callType}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.callType}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Phone">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.phone}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.phone}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Dispute Reason">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.disputeReason}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.disputeReason}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Placement Decision">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{tree.placementDescision}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{tree.placementDescision}" disabled="true" />
</f:facet>
</p:cellEditor>
</p:column>
<!--
<p:column>
<p:rowEditor />
</p:column>
-->
</p:dataTable>
</h:form>
Here is the Bean.
#Component("treeBean")
#Scope(value = "view", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TreeBean {
private List<TreeDto> content;
private String result="";
public List<TreeDto> getContent() {
return content;
}
public void setContent(List<TreeDto> content) {
this.content = content;
}
#PostConstruct
public void init() {
content=new ArrayList<TreeDto>();
TreeDto dto1=new TreeDto();
dto1.setCommentType("First Attempt");
dto1.setMtcns("mtcn1");
dto1.setCallType("OBC");
dto1.setPhone("8975730838");
dto1.setDisputeReason("Fraud");
dto1.setPlacementDescision("Write Off");
content.add(dto1);
}
public void onCellEdit(CellEditEvent event) {
RequestContext.getCurrentInstance().showMessageInDialog(new FacesMessage(FacesMessage.SEVERITY_INFO, "Status","something clicked"));
}
}
My intention to capture the value of the cells clicked. But I am not able to get an event fired in the first place on cell edit. Please give me some suggestions on how to resolve this.
Try to add a widgetVar to your dataTable and then edit your cell with a contextMenu for your data table, which call onclick="PF('yourWidgetVar').showCellEditor();return false;"
Somenthing like this
<p:contextMenu for="decisionTree" widgetVar="cMenu">
<p:menuitem value="Edit Cell" icon="ui-icon-search" onclick="PF('yourWidgetVar').showCellEditor();return false;"/>
<p:menuitem value="Hide Menu" icon="ui-icon-close" onclick="PF('cMenu').hide()"/>
</p:contextMenu>
You can take a look to Primefaces documentation too:
https://www.primefaces.org/showcase/ui/data/datatable/edit.xhtml

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:rowEditor display new values, even update db was not performed successfully

My web app is built on the Spring boot + JSF and Primefaces.
To update my dataTable I am using roweditor primefaces element.
Here is my dataTable:
<p:dataTable id="table" var="categoryLevel"
value="#{cardCategoryLevelController.categoryLevels}"
editable="true">
<p:ajax event="rowEdit"
listener="#{cardCategoryLevelController.onRowEdit}"
update=":categoryLevelForm:msgs" />
<p:ajax event="rowEditCancel"
listener="#{cardCategoryLevelController.onRowCancel}"
update=":categoryLevelForm:msgs" />
<p:column headerText="Id">
<h:outputText value="#{categoryLevel.id}" />
</p:column>
<p:column headerText="Category Name">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{categoryLevel.cardCategory.name}" />
</f:facet>
<f:facet name="input">
<h:selectOneMenu id="category"
value="#{categoryLevel.cardCategory}"
style="width:125px">
<f:selectItems value="#{cardCategoryLevelController.categories}"
var="category" itemLabel="#{category.name}"
itemValue="#{category}" />
<f:converter binding="#{categoryConverter}"/>
</h:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Level Name">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{categoryLevel.level.name}" />
</f:facet>
<f:facet name="input">
<h:selectOneMenu id="level"
value="#{categoryLevel.level}"
style="width:125px">
<f:selectItems value="#{cardCategoryLevelController.levels}"
var="level" itemLabel="#{level.name}" itemValue="#{level}" />
<f:converter binding="#{levelConverter}"/>
</h:selectOneMenu>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Card Drop Rate">
<h:outputText value="#{categoryLevel.cardDropRate}" />
</p:column>
<p:column headerText="Created Date">
<h:outputText value="#{categoryLevel.createdDate}">
<f:convertDateTime type="time" pattern="MM-dd-yyyy HH:mm:ss" />
</h:outputText>
</p:column>
<p:column headerText="Updated Date">
<h:outputText value="#{categoryLevel.updatedDate}">
<f:convertDateTime type="time" pattern="MM-dd-yyyy HH:mm:ss" />
</h:outputText>
</p:column>
<p:column headerText="Actions">
<p:rowEditor style="float:left; padding:5px;"/>
<p:commandButton styleClass="no-btn" icon="ui-icon-trash"
rendered="#{not empty categoryLevel}"
action="#{cardCategoryLevelController.delete(categoryLevel.id)}"
update="table">
<p:confirm header="Confirmation"
message="Are you sure you want to delete this card category level"
icon="ui-icon-alert" />
</p:commandButton>
</p:column>
</p:dataTable>
And roweditor method:
public void onRowEdit(RowEditEvent event) {
CardCategoryLevel cardCategoryLevel = (CardCategoryLevel) event.getObject();
cardCategoryLevel = cardCategoryLevelService.update(cardCategoryLevel);
if (cardCategoryLevel != null) {
FacesMessage msg = new FacesMessage("Card Category Level Edited", cardCategoryLevel.getId().toString());
FacesContext.getCurrentInstance().addMessage(null, msg);
} else {
FacesMessage msg = new FacesMessage("Such card category level already exists");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
The problem is when I edit some dataTable row, and the update on the my db was not performed successfully, wrong values (instead of old ones) are displayed on the updated row.
How can I solved that?
Nice work around is to implement custom validator, which is called before back end logic. In case validation failed, the data table row becomes red, and appropriate message is displayed.

Editor Calendar in Primefaces 3.5 cell value is lost?

is that I have a grid that dynamically genre, I mean I add empty rows to the grid, first thought to use primefaces in Cell Editor, so that the value is stored in the list of beans
List, works but the problem that I have is on the calendar, when I select one and is stored, it is stored in the list, I checked it does not execute the ajax event, you lose the value of the date.
<p:commandButton value="AƱadir dia" update="dataTable" actionListener="#{regDPNL.registrarNuevoDia}" />
<p:dataTable id="dataTable" var="dpnl" value="#{regDPNL.listdpnl}" paginatorPosition="top"
rows="20" rowIndexVar="rowIndex" editable="true" editMode="cell"
<p:ajax event="cellEdit" listener="#{regDPNL.onCellEdit}" />
<p:column width="25%">
<f:facet name="header">
<h:outputText value="NOMBRE" />
</f:facet>
<p:cellEditor>
<f:facet name="output"><p:inputText value="#{dpnl.nombre}" /></f:facet>
<f:facet name="input"><p:inputText value="#{dpnl.nombre}" /></f:facet>
</p:cellEditor>
</p:column>
<p:column width="20%">
<f:facet name="header">
<h:outputText value="DESDE" />
</f:facet>
<p:cellEditor>
<f:facet name="output"><p:inputText value="#{dpnl.fechaDesde}" /></f:facet>
<f:facet name="input"><p:calendar value="#{dpnl.fechaDesde}" pattern="dd/MM/yyyy" /></f:facet>
</p:cellEditor>
</p:column>
<p:column width="20%">
<f:facet name="header">
<h:outputText value="HASTA" />
</f:facet>
<p:cellEditor>
<f:facet name="output"><p:inputText value="#{dpnl.fechaHasta}" /></f:facet>
<f:facet name="input"><p:calendar value="#{dpnl.fechaHasta}" pattern="dd/MM/yyyy" /></f:facet>
</p:cellEditor>
</p:column>
<p:column width="5%">
<f:facet name="header">
<h:outputText value="ELIMINAR" />
</f:facet>
<p:commandButton icon="ui-icon-editar" title="Delete Periodo" style="background:#fff">
</p:commandButton>
</p:column>
</p:dataTable>
Java code:
private List<DetallePeriodoBean> listdpnl = new PeriodoNoLaborable();
private DetallePeriodoBean detallePeriodoNoLaborable = new ArrayList<DetallePeriodoBean>();
public void registrarNuevoDia() {
detallePeriodoNoLaborable = new DetallePeriodoBean();
this.listdpnl.add(detallePeriodoNoLaborable);
}
public void onCellEdit(CellEditEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
if(newValue != null && !newValue.equals(oldValue)) {
System.out.println("==> Old: " + oldValue + ", New:" + newValue);
}
}
http://s2.subirimagenes.com/imagen/previo/thump_8690487captura.png
I have faced this problem.
The solution is very simple (after a lot of testing and error)
You must add id attribute to your tag,
for instance:
<p:calendar id="date_selector" value="#{dpnl.fechaHasta}" pattern="dd/MM/yyyy" />
At the facet named output use the tag h:outputText instead of p:inputText
make sure you use java.util.date for your date fields(i.e fechaDesde etc) in your bean.

p:dialog shows empty values when when I go to the next page of the p:datatable using paginator

I have been using onEdit in the p:datatable and it works fine.My data table has a pagintor and shows 5 records per page.so when I click on the onEdit in the first page event.getObject gets the changed value in the bean.Now when I go the next page the event.getObject does not work and returns the old value only.
Same with the dialog box.In the table I have a link when clicked opens a dialog box in whcih I am populating few fields with the values from the row selected.It works fine in the first page and When I navigate to other pages gives empty values.
Here is my jsf code:
<p:dataTable value="#{mybean.userList}"
var="item"
id="dataTab"
widgetVar="usersTable"
tableStyleClass="data" paginator="true" rows="5"
filteredValue="#{userController.filteredUsers}"
editable="true"
rowKey="#{item}"
>
<p:ajax event="rowEdit" listener="#{mybean.onEdit}" update=":userForm:growl" />
<p:ajax event="rowEditCancel" listener="#{mybean.onCancel}" update=":userForm:growl" />
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:" />
<p:inputText id="globalFilter" onkeyup="('usersTable').filter()" style="width:150px" />
</p:outputPanel>
</f:facet>
<p:column sortBy="#{item.firstName}" filterBy="#{item.firstName}"
filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="First Name" />
</f:facet>
<f:facet name="output">
<h:outputText value="#{item.firstName}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{item.firstName}" style="width:100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{item.lastName}" filterBy="#{item.lastName}" filterMatchMode="startsWith">
<p:cellEditor>
<f:facet name="header">
<h:outputText value="Last Name" />
</f:facet>
<p:column headerText="Update" style="width:6%">
<p:rowEditor />
</p:column>
</p:dataTable>
<p:dialog id="modalDialog"
header="Delete User?"
modal="true"
resizable="false"
draggable="false"
widgetVar="delUserConf"
>
<h:panelGrid id="display" columns="2" style="width: 150px;">
<h:outputLabel value="Firat Name" style="font-weight: bold"/>
<h:outputText value="Last Name" style="border: none"/>
</h:panelGrid>
</p:dialog>
Here is the code in my bean for the edit functionality:
public String onEdit(RowEditEvent event)
{
User user=(User)event.getObject());
user.getFirstName();
}
I did not add the link that I have in my form which pops up the dialog.Also I read that this might be because of Lazy loading and I am not sure about it.I am populating my table with a list and not the model.
Could you let me know what should I do to make it work?
maybe you should switch to SessionBean scope because the scope view is released when there are changes in the view. Try SessionBean. Also you can call from view scope any sessionbean like this, where controladorMB is a sessionbean
public void detallesPersona() {
try {
FacesContext ctx = FacesContext.getCurrentInstance();
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{controladorMB}", ControladorMB.class);
ControladorMB gMB = (ControladorMB) vex.getValue(ctx.getELContext());
this.setSelectedPersona(gMB.getPersonaBean().detallesPersonas(this.selectedPersona.getIdPersona()));
} catch (Exception e) {
JsfUtil.addErrorMessage(e, "Error: " + e.getMessage());
}
}

Resources