How to display variable number of columns in primefaces datatable - jsf

I have the following ArrayList
class Report{
private String manufacturer;
private String color;
private List<Double> revenue;
}
How can I display it on primefaces datatable. I tried using p:columns it is not working.
I have following code on XHTML page
<p:dataTable value="#{tableBean.reportList}" var="report" id="table">
<p:column headerText="Manufacturer">#{report.manufacturer}</p:column>
<p:column headerText="Color">#{report.color}</p:column>
</p:dataTable >
I also tried p:columns and ui:repeat. But I am not able to achieve desired output. result.
<p:columns var="amount" value="#{report.revenue}">#{amount}</p:columns>
<ui:repeat var="amount" value="#{report.revenue}">
<p:column headerText="Revenue">#{amount}</p:column>
</ui:repeat>
I need output as a table with manufacturer name, color and all revenues

Your mistake is that you referenced the currently iterated row (the object behind <p:dataTable var> as <p:columns value>. This is not possible. The amount of columns cannot vary based on the currently iterated row. It can only be set table-wide. The <p:columns value> should reference a property in a backing bean. For a real world example, see also this answer: Creating and populating a DataTable dynamically in JSF2.0.
In your particular case, you just basically want to use <ui:repeat> inside <p:column>:
<p:dataTable value="#{tableBean.reportList}" var="report">
<p:column>
#{report.manufacturer}
</p:column>
<p:column>
#{report.color}
</p:column>
<p:column>
<ui:repeat value="#{report.revenue}" var="revenue">#{revenue}</ui:repeat>
</p:column>
</p:dataTable>
(if you want to print them in separate lines, print a <br/> after #{revenue}; or if you want to print them commaseparated, make use of varStatus)
or maybe a nested table:
<p:column>
<h:dataTable value="#{report.revenue}" var="revenue">#{revenue}</h:dataTable>
</p:column>

Try with
<p:column headerText="Manufacturer">#{report.manufacturer}</p:column>
<p:column headerText="Color">#{report.color}</p:column>
<p:columns value="#{tableBean.columns}" var="column">
<f:facet name="header">
#{column.header}
</f:facet>
#{report.revenue.get(column.property)}
</p:columns>
Where in your bean columns is:
List<ColumnModel> columns;
static public class ColumnModel implements Serializable {
private String header;
private int property;
public ColumnModel(String header, int property) {
this.header = header;
this.property = property;
}
public String getHeader() {
return header;
}
public int getProperty() {
return property;
}
}
Filled as follow:
for(int i = 0; i < TOTAL_NUMBER_OF_REVENUES; i++){
ColumnModel col = new ColumnModel("Revenue "+i, i);
columns.add(col);
}
Source : http://www.primefaces.org/showcase/ui/datatableDynamicColumns.jsf

Related

Accessing DataTable item returns incorrect selected item when order is changed

To better read the contents of a cell within a dataTable, I used a commandLink to bring up a dialog box.
This works fine as long as the sortOrder of the dataTable is set to ascending. Upon using sortOrder desc and clicking the commandLink, the dialog brings up the result of the item that would have been there, had the sortOrder been ascending (in other words, in a desc dataTable -8,7,....,2,1- with 8 rows, clicking on row with id =2 will bring up the contents of row id=7).
What causes this mix-up in IDs? Am I not storing the actual clicked on item in the backing bean temporarily, which should not be affected by the sortOrder? IS there a better practice for what I am trying to accomplish?
PF version 5.3, JSF 2.2.7
dataTable and dialog
<p:dataTable id="improvementTable" var="improvement" widgetVar="improvementsTable" value="#{Controller.improvements}" sortBy="#{improvement.id}" sortOrder="descending">
<p:column headerText="ID">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{improvement.id}" />
</f:facet>
<f:facet name="input">
<p:inputText id="modelInput" value="#{improvement.id}" />
</f:facet>
</p:cellEditor>
</p:column>
<p:column>
<p:commandLink id="detailOut" value="#{improvement.detail}" action="#{Controller.setSelectedImprovement(improvement)}" process="#this" oncomplete="PF('wDetail').show();" update=":dlgDetail" />
</p:column>
</p:dataTable>
</h:form>
<p:dialog id="dlgDetail" widgetVar="wDetail">
<h:outputText value="#{Controller.selectedImprovement.detail}" />
</p:dialog>
In the Bean
#ManagedBean (name="Controller")
#RequestScoped
public class Controller{
private List<Improvement> improvements;
private Improvement selectedImprovement;
#PostConstruct
public void load() {
CIMImprovementDao cimDao = new CIMImprovementDao();
improvements = cimDao.getAll();
}
public List<Improvement> getImprovements() {
return improvements;
}
public Improvement getSelectedImprovement() {
return selectedImprovement;
}
public void setSelectedImprovement(Improvement selectedImprovement) {
this.selectedImprovement = selectedImprovement;
}
}

Add a row to rich:datatable manually for input text

I want to add a row at the end of my rich:datatable that shows information
and in this row I want to put some input that enable user to add new data
I read this answer :
RICH:dataTable - add new row
but for me is a little different
I don't have any list
my xhtml is like this :
<rich:dataTable value="#{serverMB.allServer}" var="servers1" iterationStatusVar="it" id="table" rows="15"
style="direction: rtl ; text-align: right">
<rich:column>
<f:facet name="header">#</f:facet>
#{it.index+1}
</rich:column>
<f:facet name="header">
<h:outputText value="#{msgs.servers}"/>
</f:facet>
<rich:column filterValue="#{serverMB.serverNameFilter}"
filterExpression="#{fn:containsIgnoreCase(servers1.server_name,serverMB.serverNameFilter )}">
<f:facet name="header">
<h:outputText value="#{msgs.server_name}"/>
<h:inputText value="#{serverMB.serverNameFilter}">
<a4j:ajax event="blur" render="table" execute="#this"/>
</h:inputText>
</f:facet>
<h:outputText value="#{servers1.server_name}"/>
</rich:column>
.
.
.
and my managedbean is like this
public List<Server> getAllServer() {
return serverFacade.findAll();
}
I use this method to show information so there is not any list that I personally can add a row to it and so on ...
so what I must do ?
Your serverList should be populated and set as a class-level variable. Use an #PostConstruct method to populate the list:
#PostConstruct
public void initServerList(){
//declare serverList as a class-scoped variable in your managed bean
serverList = serverFacade.findAll();
}
Declare a method in your managed bean that will add to the serverList
public void incrementList(){
Server server = new Server(); //create a dummy Server object
serverList.add(server); // List has been incremented. Be sure to AJAX-refresh the datatable after calling this method to reflect the new row.
}
Collect information from the JSF view which you'll then use to fill the fields of the dummy object:
public void updateLatestServer(){
Server lastServer = serverList.get(serverList.size-1);
lastServer.setName(serverNameFromView);
lastServer.setIP(IPFromView);
}
The serverNameFromView and IPFromView are hypothetical variables you'll collect in your JSF Form.

JSF Cannot save change to datatable row to the database

I have a primefaces datatable. I populate it from the database. One of the fields is a boolean represented by a checkbox. I want that if I check or uncheck the checkbox, that I can save the change back to the database.
I have tried passing the current value of the row to the managed bean to save, but the new value of the checkbox isn't reflected in the current row object. How can I get the change into the current row object so I can successfully save the change to the DB?
Here is what I am doing now... I have tried to provide just what is needed. If it is too much information or too little, let me know. Thanks.
#ManagedBean(name = "itemManagerBean")
#ViewScoped
public class ItemManagerBean implements Serializable {
...
public ArrayList<Item> getAllItemsForUser() {
List list = ecf.findByPartyId(user.getPartyId());
ArrayList<Item> itemList = new ArrayList<>(list);
return (itemList);
}
...
public String saveItem(Item item){
System.out.println(item.toString());
ecf.updateRecord(item);
return (null);
}
}
//item class
public class Item {
private BigInteger itemId;
private String name;
priave boolean saleable; //database column is not null
//getters and setters
}
//facelet
<h:form>
<p:dataTable id="id_itemList"
var="item"
value="#{itemManagerBean.allItemsForUser}" >
<p:column headerText="ID">
<h:outputText value="#{item.itemId}" />
</p:column>
<p:column headerText="Name">
<h:outputText value="#{item.name}" />
</p:column>
<p:column headerText="Saleable" >
<p:selectBooleanCheckbox value="#{item.saleable}" />
</p:column>
<p:column width="15" >
<p:commandButton id="id_saveRowButton" icon="ui-icon-disk"
title="Save" action="#{itemManagerBean.saveItem(item)}"/>
</p:column>
</p:dataTable>
</h:form>
You need to create a selectedItem property in ItemManagerBean and update its value when the user clicks on the commandButton:
In ItemManagerBean
private Item selectedItem;
// getter and setter
In the xhtml page
<p:column width="15" >
<p:commandButton id="id_saveRowButton" icon="ui-icon-disk"
title="Save" action="#{itemManagerBean.saveItem}">
<f:setPropertyActionListener value="#{item}" target="#{itemManagerBean.selectedItem}" />
</p:commandButton>
</p:column>
(Note that you don't need to pass item through saveItem method. Modify saveItem in the managed bean in order to make it work with selectedItem instead of accepting an input item).
Links:
example in the PrimeFaces showcase
Passing parameter to JSF action
BalusC blog

Using <h:selectBooleanCheckbox> with Map in a paginated <p:dataTable> throws NullPointerException

I am using PrimeFaces <p:dataTable> with pagination. I use <h:selectBooleancheckbox> with a Map property for corresponding row selection. The problem which I am facing is when I select and submit the values, there is a NullPointerException. The values are checked for particular rows only. I am using JSF 2.0 and PrimeFaces 3.0.
My page is:
<p:dataTable id="ngoPhotoTab" paginator="true" rows="10" value="# {photoApprovelBean.lstNgoPhotos}" var="ngoPhoto">
<p:column headerText="NgoName">
#{ngoPhoto.ngoName}
</p:column>
<p:column headerText="Select">
<h:selectBooleanCheckbox id="ngoSelect" layout="pageDirection" value="#{photoApprovelBean.checked[ngoPhoto.photo_id]}" />
</p:column>
<f:facet name="footer">
<p:commandButton onclick="deletePhoto();" value="Delete" />
</f:facet>
</p:dataTable>
Backing bean logic:
public class PhotoApprovelBean {
public String deleteActPhoto() {
List checkedItems = new ArrayList();
try {
for (Iterator<PhotoApprovelBean> itr = disAppPhotoList.iterator(); itr.hasNext();) {
PhotoApprovelBean item = (PhotoApprovelBean) itr.next();
if (checked.get(item.getPhotoId())) {
checkedItems.add(item.getPhotoId());
}
}
toDeletePhoto(checkedItems);
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
public Map<Long, Boolean> getChecked() {
return checked;
}
}
The NullPointerException is caused in the line if (checked.get(item.getPhotoId())). The The Map is filled with values of first page only. How is this caused and how can I solve it?
You got a NullPointerException, because the if statement expects a boolean, but the Map contains Boolean values only which can possibly be null. They will be null for other pages. The Boolean value of null can't be autoboxed to a boolean. You'd need to add a nullcheck.
Boolean itemChecked = checked.get(item.getPhotoId());
if (itemChecked != null && itemChecked) {
// ...
}
Better, however, is to use PrimeFaces own <p:column selectionMode="multiple"> instead. It will remember the selection on other pages. The Map approach is only suitable if you don't use pagination. See also the showcase example. Here's how it can look like for you:
<p:dataTable id="ngoPhotoTab" paginator="true" rows="10"
value="#{photoApprovelBean.lstNgoPhotos}" var="ngoPhoto"
selection="#{photoApprovelBean.selectedNgoPhotos}" rowKey="#{ngoPhoto.photoId}"
>
<p:column headerText="NgoName">
#{ngoPhoto.ngoName}
</p:column>
<p:column headerText="Select" selectionMode="multiple" />
<f:facet name="footer">
<p:commandButton onclick="deletePhoto();" value="Delete" />
</f:facet>
</p:dataTable>
with
private PhotoApprovelBean[] selectedNgoPhotos;

Dynamic Column in JSF Datatable JSF2.0

I need one help from you. I am using JSF 2.0 and I have a datatable component . One of the column in the datatable is an action column and I need to create a toolbar which contains different type of actionsource component such as command button, link etc. The type of actionsource is determined at run time and number of actionsource is also done at run time. How I can implement this in JSF 2.0
<p:dataTable value="#{listBranchBean1.rowDataModel}" var="rowItem"
id="myId" paginator="true"
paginatorTemplate="{FirstPageLink}{PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}{RowsPerPageDropdown} "
rowsPerPageTemplate="10,5,2" previousPageLinkLabel="<"
nextPageLinkLabel=">" widgetVar="branchTable"
selection="#{listBranchBean1.selectedBranchesPrime}"
resizableColumns="true"
sortBy="#{rowItem.columnsValueMap['branchId'].value}">
<f:facet name="header">
<p:outputPanel>
<h:outputText value="Search all fields:" />
<p:inputText id="globalFilter" onkeyup="branchTable.filter()"
style="width:150px" />
</p:outputPanel>
</f:facet>
<p:column selectionMode="multiple" style="text-align:left">
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<h:outputText value="#{rowItem.uniqueId}" />
</p:column>
<p:column
rendered="#{listBranchBean1.columnsMap['objectId'].hidden==false}"
sortBy="#{rowItem.columnsValueMap['objectId'].value}"
filterBy="#{rowItem.columnsValueMap['objectId'].value}">
<f:facet name="header">
<h:outputText
value="#{listBranchBean1.columnsMap['objectId'].displayLabel}" />
</f:facet>
<h:outputText
value="#{rowItem.columnsValueMap['objectId'].value}" />
</p:column>
<p:column
rendered="#{listBranchBean1.columnsMap['actions'].hidden==false}">
<f:facet name="header">
<h:outputText
value="#{listBranchBean1.columnsMap['actions'].displayLabel}" />
</f:facet>
<p:toolbar>
<p:toolbarGroup>
<ui:repeat var="action"
value="#{rowItem.columnsValueMap['actions'].value}">
<p:commandButton title="#{action}" type="button">
</p:commandButton>
</ui:repeat>
</p:toolbarGroup>
</p:toolbar>
</p:column>
</p:dataTable>
I want to replace the last column with something like
<p:toolbar binding="#{listBranchBean1.getActions(rowItem)}">
</p:toolbar>
I appreciate your help
Prajeesh Nair
There is a difference between build-time and render-time in JSF. Build-time tags like <ui:repeat> have the ability to create new components dynamically, but they can only use data that is available at build-time.
However, using Java you are also allowed to alter the component tree programmatically, but this too can not just happen at any moment. The safe moment to do this is the preRenderViewEvent, which is a good bit later than the build-time moment (which is the restore view phase) and you should have all the data you need by then.
Inside an event handler for this event you can reference the tool bar you bound to your backing bean, and programmatically add columns to it.
For examples see:
http://balusc.omnifaces.org/2006/06/using-datatables.html#PopulateDynamicDatatable
http://arjan-tijms.omnifaces.org/2011/09/authoring-jsf-pages-in-pure-java.html
Do note that if your backing bean is #ViewScoped, you'd better not use binding but use a manual lookup instead. This is due to some bugs with respect to the view scope and binding components in JSF.
below code will create dynamic column on the basis of selected country
public void loadDynamicList() throws Exception {
int i=0;
dynamicList = new ArrayList<List<String>>();
dynamicList.add(Arrays.asList(new String[] { "ID1" }));
existingCountryList = new ArrayList<Country>();
String countryCode="US";
existingCountryList.add(getCountryService().getCountryByCode(countryCode));
Country country=getCountryService().getCountryByCode(countryCode);
countryLanguageSet=country.getCountryLanguage();
i=country.getCountryLanguage().size();
dynamicHeaders = new String[i] ;
int j=0;
for (CountryLanguage count: countryLanguageSet) {
System.out.println(count.getLanguage().getLanguageName());
dynamicHeaders[j]=count.getLanguage().getLanguageName();
j++;
}
}
public void populateDynamicDataTable() {
debugLogger.debug("populateDynamicDataTable:Enter");
// Create <h:dataTable value="#{myBean.dynamicList}" var="dynamicItem">.
HtmlDataTable dynamicDataTable = new HtmlDataTable();
dynamicDataTable.setValueExpression("value", createValueExpression("#{relationBean.dynamicList}", List.class));
dynamicDataTable.setVar("dynamicItem");
// Iterate over columns.
for (int i = 0; i < dynamicHeaders.length; i++) {
// Create <h:column>.
HtmlColumn column = new HtmlColumn();
dynamicDataTable.getChildren().add(column);
// Create <h:outputText value="dynamicHeaders[i]"> for <f:facet name="header"> of column.
HtmlOutputText header = new HtmlOutputText();
header.setValue(dynamicHeaders[i]);
column.setHeader(header);
HtmlInputText input=new HtmlInputText();
column.getChildren().add(input);
}
dynamicDataTableGroup = new HtmlPanelGroup();
dynamicDataTableGroup.getChildren().add(dynamicDataTable);
debugLogger.debug("populateDynamicDataTable:Exit");
}
public HtmlPanelGroup getDynamicDataTableGroup() throws Exception {
// This will be called once in the first RESTORE VIEW phase.
if (dynamicDataTableGroup == null) {
loadDynamicList(); // Preload dynamic list.
populateDynamicDataTable(); // Populate editable datatable.
}
return dynamicDataTableGroup;
}
public List<List<String>> getDynamicList() {
return dynamicList;
}
public void setDynamicList(List<List<String>> dynamicList) {
this.dynamicList = dynamicList;
}
public void setDynamicDataTableGroup(HtmlPanelGroup dynamicDataTableGroup) {
this.dynamicDataTableGroup = dynamicDataTableGroup;
}
public ValueExpression createValueExpression(String valueExpression, Class<?> valueType) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return facesContext.getApplication().getExpressionFactory().createValueExpression(
facesContext.getELContext(), valueExpression, valueType);
}

Resources