PrimeFaces dataTable with variable columns and specific editable cells - jsf

I need to create a table where the headers list are brought from a model. The table contents are also stored in the model and p:dataTable loop on the data to show the content based on the column name.
The issue is that I need to make some specific cells editable. For outputting data there is no problem since I use model method which takes both the entity and the column name and return the correct info from the entity based on the column name. The issue is with inputs of the editable cells which I don't know how to set in the entity.
<p:dataTable id="processTable" var="entity" value="#{home.process.headerEntities}" tableStyle="width:auto" draggableColumns="true" editable="true" editMode="cell">
<p:columns value="#{home.process.columns}" var="columnHead" >
<f:facet name="header">
<h:outputText value="#{columnHead}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{home.process.getData(entity, columnHead)}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{home.process.getData(entity, columnHead)}" rendered="#{home.process.isEditable(columnHead)}" style="width:100%" />
</f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
After change based on BEST ANSWER
<p:dataTable id="processTable" var="entity" value="#{home.process.headerEntities}" tableStyle="width:auto" draggableColumns="true" editable="true" editMode="cell">
<p:columns value="#{home.process.columns}" var="columnHead" >
<f:facet name="header">
<h:outputText value="#{columnHead}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{entity[home.process.columnPropertyMap[columnHead]]}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{entity[home.process.columnPropertyMap[columnHead]]}" rendered="#{home.process.isEditable(columnHead)}" style="width:100%" />
</f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>

The input component's value must be bound to a writable value expression. What you've there is a direct getter method invocation and thus essentially read-only. This is indeed not going to work. You need to specify a property name of the #{entity}. You can use the brace notation to specify the property name as a variable like so #{entity[propertyName]}.
So, basically:
<p:dataTable value="#{bean.entities}" var="entity" editable="true" editMode="cell">
<p:columns value="#{bean.propertyNames}" var="propertyNames">
<p:cellEditor>
<f:facet name="output">
#{entity[propertyName]}
</f:facet>
<f:facet name="input">
<p:inputText value="#{entity[propertyName]}" />
</f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
As to the column header, rather refactor out that into a Map<String, String> where the key is the propertyName and the value is the header.
<f:facet name="header">
#{bean.columnHeaders[propertyName]}
</f:facet name="header">
Or better yet, use a normal i18n resource bundle for that where the propertyName represents part of the bundle key.
<f:facet name="header">
#{bundle['table.column.header.' += propertyName]}
</f:facet name="header">
As to the editable check, rather wrap propertyName and editable in another bean (and perhaps also columnHeader if you don't want to use a i18n bundle), e.g. Field and then use like below:
<p:columns value="#{bean.fields}" var="field">
<p:cellEditor>
<f:facet name="output">
#{entity[field.propertyName]}
</f:facet>
<f:facet name="input">
<p:inputText value="#{entity[field.propertyName]}" rendered="#{entity[field.editable]}" />
</f:facet>
</p:cellEditor>
</p:columns>
All in all, it just boils down to preparing and providing the right model the view expects. This way the getData() thing isn't necessary.

Related

How to iterate over datatable without columns iterator?

I have the following code:
<p:dataTable id="oc_pc" editable="true" editMode="cell" rendered="#{dersRelation.isOC_PC}" value="#{dersRelation.rel_oc_pc}" var="oc_pc_rel">
<f:facet name="header">
#{dersRelation.relName}
</f:facet>
<p:ajax event="cellEdit" listener="#{dersRelation.onCellEdit}" update="main_form:growl,#this" />
<p:column headerText="Program Çıktıları">
<h:outputText value="#{oc_pc_rel.pc}"/>
</p:column>
<p:columns value="#{dersRelation.ocs}" var="oc" columnIndexVar="colIndex" >
<f:facet name="header">
<h:outputText value="#{oc.description}"/>
</f:facet>
<p:cellEditor>
<f:facet name="output"><h:outputText value="#{oc_pc_rel.values[colIndex]}"/></f:facet>
<f:facet name="input"><p:rating value="#{oc_pc_rel.values[colIndex]}"/></f:facet>
</p:cellEditor>
</p:columns>
</p:dataTable>
As it seems between column tags "oc" iterator is active. But i have to iterate with main iterator in table for "oc_pc_rel.values[colIndex]". This code fills all rows with count of "oc_pc_rel.pc". All rows refer to same index in rel_oc_pc collection. How can i fix that.

Primefaces exporting only one expandable table

I am trying to export a view to xlsx using PrimeFaces extensions. I have a main table and an expandable row with 2 tables inside.
The exporter works fine for the first dataTable in the expandable row, but not for the other one. Any ideas?
<p:dataTable id="mainTable" var="mainObject" value="#{mainBean.mainList}">
<p:column exportable="false" width="5%">
<p:rowToggler />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="main column A"/>
</f:facet>
<h:outputText value="#{mainObject.columnA}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="main column B"/>
</f:facet>
<h:outputText value="#{mainObject.columnB}" />
</p:column>
<p:rowExpansion>
<p:datTable id="relatedTableA" var="relatedA" value="#{mainObject.relatedA}">
<f:facet name="header">
<h:outputText value="Related A"/>
</f:facet>
<p:column>
<f:facet name="header">
<h:outputText value="Related A column A"/>
</f:facet>
<h:outputText value="#{relatedA.columnA}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Related A column B"/>
</f:facet>
<h:outputText value="#{relatedA.columnB}" />
</p:column>
</p:dataTable>
<p:datTable id="relatedTableB" var="relatedB" value="#{mainObject.relatedB}">
<f:facet name="header">
<h:outputText value="Related B"/>
</f:facet>
<p:column>
<f:facet name="header">
<h:outputText value="Related B column A"/>
</f:facet>
<h:outputText value="#{relatedB.columnA}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Related B column B"/>
</f:facet>
<h:outputText value="#{relatedB.columnB}" />
</p:column>
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
<h:commandLink>
<p:graphicImage url="/resources/images/Excel32.png" width="32"/>
<pe:exporter type="xlsx" target="mainTable" fileName="fileExport" facetBackground="#AAFFBB" datasetPadding="4" />
</h:commandLink>
I followed this guide: https://www.primefaces.org/showcase-ext/sections/exporter/expandableTable.jsf
In my app, the view works perfect. I use the toggle and then it shows the 2 tables. The only problem is that it exports only one of the expandables.
Thanks for your time.
Seemes this is simply not supported.
In the exporter component source code when it comes to exporting the row expensions they have hard coded to consider the first child for each rowExpansion only:
if (rowExpansion.getChildren().get(0) instanceof DataTable) {
final DataTable childTable = (DataTable) rowExpansion.getChildren().get(0);
// ...
}
This is why you only get the first sub table in your output.
Using the customExporter feature you have the chance to extend the ExcelExporter and override the method exportCells which seemes responsible for your problem. Then change the behavior to do a loop on rowExpansion.getChildren() instead of just getting the first element.
General steps to configure a custom exporter from the linked site:
Step 1: Create a folder named META-INF under resouces folder.Below
META-INF folder create another folder called services.
Step 2: Creae a
file with the name "ExporterFactory" as a service(Fully binary name of
the service).
Here it should be org.primefaces.extensions.component.exporter.ExporterFactory.
Step 3:
Provide your own implementaions/providers of Exporter factory anywhere
in your project.
And copy the absolute path of custom exporter factory implementation in the ExporterFactory file
How to do : Copy the file content of DefaultExporterFactory and rename the file as CustomExporterFactory.Copy the absolute path
org.primefaces.extensions.showcase.util.CustomExporterFactory in
ExporterFactory file.
Step 4: Copy the exporter implementations and
add your own changes.And call these custom implementations(Ex
PDFCustomExporter,ExcelCustomExporter) instead built-in
implmentations(Ex PDFExporter,ExcelExporter)

JSF PrimeFaces dataTable changing value

I have the following code:
<p:dataTable style="background-color:white;color: white;" var="url" value="#{data.data.requirementsDocuments}" editable="true" id="urlTable" editMode="cell">
<p:ajax event="cellEdit" update="urlTable" immediate="true" process="#this"/>
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{url}" />
</f:facet>
<f:facet name="input">
<p:inputText id="modelInput" value="#{url}" style="width:96%" />
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
The problem is that each time I wrote a new value inside the cell it is not updating it. Lets say I have a cell with text "Hello" when I edit it to lets say "World" and press enter, it is not updating the value. requirementsDocuments is List with strings.

Primefaces 3.5 dataexporter does not export values p:cellEditor

I want to export values from my table.
However, my columes look like that:
<p:column headerText="Special Remarks" style="width:15%">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{product.specialRemarks}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{product.specialRemarks}" style="width:96%" />
</f:facet>
</p:cellEditor>
</p:column>
When I export the files I get org.primefaces.component.celleditor.CellEditor#446d0c6a in every cell back?
Any idea how to fix that?

Cell edit in primefaces is not updating the value

I have a datatable in my primefaces application . The code for the frontend has
<!-- Start of customer datatable -->
<p:dataTable var="customer" value="#{customerBean.customers}" paginator="true" selection="#{customerBean.selectedCustomer}"
selectionMode="single" onRowSelectUpdate=":custList" onRowSelectComplete="custTab.show()" id="custList" widgetVar="custList" update=":custList">
<f:facet name="header">
List of Customers
<p:outputPanel>
<p:commandButton value="+" type="button" onclick="addCustDlg.show()"/>
</p:outputPanel>
</f:facet>
<p:column sortBy="#{customer.id}" filterBy="#{customer.id}" update=":custList">
<f:facet name="header">
<h:outputText value="ID"/>
</f:facet>
<h:outputText value="#{customer.id}"/>
</p:column>
<p:column sortBy="#{customer.name}" filterBy="#{customer.name}" headerText="NAME" filterMatchMode="contains" update=":custList">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{customer.name}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{customer.name}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{customer.description}" filterBy="#{customer.description}" headerText="DESCRIPTION">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{customer.description}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{customer.description}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{customer.signupDate}" filterBy="#{customer.signupDate}" headerText="SIGN UP DATE">
<f:facet name="output">
<h:outputText value="#{customer.signupDate}"/>
</f:facet>
</p:column>
<p:column sortBy="#{customer.validUntil}" filterBy="#{customer.validUntil}" headerText="EXPIRY DATE">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{customer.validUntil}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{customer.validUntil}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column sortBy="#{customer.status}" filterBy="#{customer.status}" headerText="STATUS">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{customer.status}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{customer.status}"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="CREATION DATE" sortBy="#{customer.creationDate}" filterBy="#{customer.creationDate}">
<f:facet name="output">
<h:outputText value="#{customer.creationDate}"/>
</f:facet>
</p:column>
<p:column headerText="LAST UPDATE DATE" sortBy="#{customer.lastUpdateDate}" filterBy="#{customer.lastUpdateDate}">
<f:facet name="output">
<h:outputText value="#{customer.lastUpdateDate}"/>
</f:facet>
</p:column>
<p:column headerText="Options">
<p:rowEditor/>
</p:column>
</p:dataTable>
<!-- End of dataTable (customer datatable) -->
And the function for handling the rowEvent is specified in the bean as
public void custRowEdit(RowEditEvent event){
Customer cust = (Customer) event.getObject();
EntityManagerHelper.beginTransaction();
custDao.update(cust);
EntityManagerHelper.commit();
}
However , on an update event , when I am editing the cell in the table , I do not get the new updated value of the attribute .
Like in the image below , when I edit the status of the entry with ID 1 from 11 to 4 , in the function custRowEdit , when I try to get the customer object , I still get the status of the customer as 11 and not 4 .
Can anyone help me with understanding why the value of the cell is not being set ?
from Where You are invoking custRowEdit(RowEditEvent event) method. I have not any related thing in your code.
In order to make your listener invoke add below attribute in your datatable declaration.
rowEditListener="#{customerBean.listenerInBackingBean}"
<p:dataTable var="customer" value="#{customerBean.customers}" paginator="true" selection="#{customerBean.selectedCustomer}"
selectionMode="single" onRowSelectUpdate=":custList" onRowSelectComplete="custTab.show()" id="custList" widgetVar="custList" update=":custList">
<f:facet name="header"
rowEditListener="#{customerBean.cutRowEvent}"
>
Check the implementation of customerBean.customers. I reloaded the content from the database every time the method got called. Wrong. This should happen in the constructor instead. Now everything works fine. Thought it was a JavaScript error ...
Thanks this helped me.
May I add that instead of loading the list from a query in the constructor, if one has a #SessionScoped managed bean one can instead use a reset() method to reset lists to null and then lazily populate the lists from the query. The reset can then be called on page load using an f:event:
<f:view>
<f:metadata>
<f:event type="preRenderView" listener="#{sessionScopedBean.reset}"/>
</f:metadata>
</f:view>
I encountered a similar situation that was resolved by removing the update="" tag from the dataTable. The table's default behavior is to update the row, which evidently, in my case, was not occurring.
You are missin the p:ajax event to trigger the method, function or whatever you wanna do after the cell editing
it's something like
<p:ajax event="cellEdit" listener="#{mBean.onCellEdit}" update="elementX" />

Resources