Datatable with dynamic columns, multiple colums per object - jsf

I have dynamic number of Tests (measurements) for each item (Apple, Banana) and every Test has 2 Properties (mandatory, visible). What I need is table that shows these Tests as columns (colspan=2) and its Properties (colspan=1), like in example below. I can do it for table header (using Primefaces' p:columnGroup) but I don't know how to fill two columns for every Property while iterating through properties list.
Only idea I have is to make new List where every item will exist twice (Diameter, Diameter, Length, Length), iterate over it and use some modulo condition to choose correct property.
My current solution is:
xhtml
<p:dataTable var="scenario">
...
<p:columns var="property" value="#{view.getXTimesProperties()}"
columnIndexVar="index">
<c:set var="test" value="#{view.getTest(scenario, property)}"/>
<c:choose>
<c:when test="${index % 2 == 0}">
<h:outputText value="#{test.mandatory}"/>
</c:when>
<c:when test="${index % 2 == 1}">
<h:outputText value="#{test.visible}"/>
</c:when>
</c:choose>
</p:columns>
</p:dataTable>
and View method is
public List<Property> getXTimesProperties() {
List<Property> list = new ArrayList<>(properties.size() * 2);
for (Property property : properties) {
list.add(property);
list.add(property);
}
return list;
}
Is there a better way? Thanks in advance for better idea!

Yes, better way (credits to Kukeltje):
<p:dataTable var="scenario">
...
<c:forEach var="property" items="#{view.properties}">
<c:set var="test" value="#{view.getTest(scenario, property)}"/>
<p:column>
<h:outputText value="#{test.mandatory}"/>
</p:column>
<p:column>
<h:outputText value="#{test.visible}"/>
</p:column>
</c:forEach>
</p:dataTable>

Related

How to create a textfield cell in primefaces datatable with action that do a computation

I'm currently working on a dynamic datatable. Let's say this table has 4 columns (name, x, y, z). x, y must be number text field and z is a label which shows the sum of x and y. When the values on either x or y is changed the value on z must be recomputed.
My problem right now is I'm not able to bind the text fields to the datatable entity, if I use the edit from primefaces, I need to add an edit event which I don't like. Also it display the cell initially as label, then need to click it to show the textfield.
Any suggestions? Thanks.
Here's my solution, instead of using primefaces datatable.edit feature. I just use a textfield in the cell and added a p:ajax that triggers a recompute method on change. Note the use of update parameter, we can directly reference a field on the row.
<h:form id="creditNoteLinesForm">
<p:dataTable id="resultsId_creditNoteLines" var="entity"
value="#{creditNoteBean.entity.creditNoteLines}">
<!-- // -->
<p:column
headerText="#{messages['creditNote.list.amountWithoutTax']}">
<p:inputText value="#{entity.amountWithoutTax}">
<p:ajax event="change"
listener="#{creditNoteBean.reCompute(entity)}"
update="amountWithTax"></p:ajax>
</p:inputText>
</p:column>
<p:column headerText="#{messages['creditNote.list.amountTax']}">
<p:inputText value="#{entity.taxAmount}">
<p:ajax event="change"
listener="#{creditNoteBean.reCompute(entity)}"
update="amountWithTax"></p:ajax>
</p:inputText>
</p:column>
<p:column
headerText="#{messages['creditNote.list.amountWithTax']}">
<h:outputText id="amountWithTax" value="#{entity.amountWithTax}" />
</p:column>
</p:dataTable>
</h:form>
sdfdsf
public void reCompute(CreditNoteLine line) {
line.computeWithTax();
}

update datatable footer primefaces (facet or columnGroup)

I want to make a footer in dataTable and I need to update when the values inside the DT changes.
The problem is that ajax update simply don't occurs.
I've tried two ways:
<p:dataTable
id="dataTableAvaliacao" var="aluno"
value="#{alunoAvaliacaoMB.alunos}">
<p:column>
<p:inputText id="inputNota"
value="#{aluno.getNota(avaliacao.property).vlNotaString}">
<p:ajax event="change"
update=":form:dataTableAvaliacao:mediaAluno, :form:dataTableAvaliacao:mediaAvaliacao" />
</p:inputText>
</p:column>
<p:columnGroup type="footer" id="mediaAvaliacao">
<p:row>
<p:column
footerText="Nota média da avaliação" />
</p:row>
<p:row>
<ui:repeat value="#{alunoAvaliacaoMB.colunasAvaliacoes}"
var="avaliacao">
<p:column id="colunaMedia"
footerText="#{alunoAvaliacaoMB.getMediaAvaliacao(avaliacao.property)}"/>
</ui:repeat>
</p:row>
</p:columnGroup>
<p:dataTable>
The update doesn't occurs...
second way (based on this answer on SO: How to ajax update an item in the footer of a PrimeFaces dataTable?):
<p:remoteCommand name="refreshFooter" update=":form:dataTableAvaliacao:outputMediaAvaliacao"/>
<p:dataTable
id="dataTableAvaliacao" var="aluno"
value="#{alunoAvaliacaoMB.alunos}">
<p:column>
<p:inputText id="inputNota"
value="#{aluno.getNota(avaliacao.property).vlNotaString}">
<p:ajax event="change"
update=":form:dataTableAvaliacao:mediaAluno" oncomplete="refreshFooter();" />
</p:inputText>
</p:column>
<p:dataTable>
<f:facet name="footer">
<h:outputText colspan="1" value="Nota média da avaliação:"/>
<ui:repeat value="#{alunoAvaliacaoMB.colunasAvaliacoes}"
var="avaliacao">
<h:outputText id="outputMediaAvaliacao"
value="#{alunoAvaliacaoMB.getMediaAvaliacao(avaliacao.property)}"
</ui:repeat>
</f:facet>
I've also tried
<p:remoteCommand name="refreshFooter" update=":form:outputMediaAvaliacao"/>
If I put
<p:ajax event="change"
update=":form:dataTableAvaliacao:mediaAluno, :form:dataTableAvaliacao" />
in the first way, it works, but I don't wanna update all dataTable every time.
What I'm doing wrong? is it a bug?
Referring on 2nd way from your question that you based on this answer...
actually it is possible to update p:dataTable footer (even from within table itself)...but with one modification of your code.
Reason why your implementation does not work is that you did not take into account that <h:outputText id="outputMediaAvaliacao"../> is wrapped with ui:repeat.
In that case, p:remoteCommand is not able to find h:outputText component ID defined in update attribute because that ID does not exist in DOM:
ui:repeat is actually copying h:outputText as many times as list from value attribute is long and appending all parent IDs to newly created native HTML components
<span id="form:dataTableAvaliacao:avaliacao:0:outputMediaAvaliacao">...</span>
<span id="form:dataTableAvaliacao:avaliacao:1:outputMediaAvaliacao">...</span>
<span id="form:dataTableAvaliacao:avaliacao:2:outputMediaAvaliacao">...</span>
...
(you can see the same using DOM inspector of your favorite browser)
SOLUTION
After you learn and understand all facts stated above (like I did :) ), solution is quite simple:
wrap ui:repeat with some parent element and assign ID to parent element. For example like this
<f:facet name="footer">
<h:outputText value="Nota média da avaliação:"/>
<h:panelGroup id="footerPanel">
<ui:repeat value="#{alunoAvaliacaoMB.colunasAvaliacoes}"
var="avaliacao" id="avaliacao">
<h:outputText id="outputMediaAvaliacao" value="#{alunoAvaliacaoMB.getMediaAvaliacao(avaliacao.property)}" />
</ui:repeat>
</h:panelGroup>
</f:facet>
and modify p:ajax of p:inputText to update newly created element after text is changed
<p:ajax event="change" update=":form:dataTableAvaliacao:footerPanel" />
On this way all children elements of h:panelGroup will be updated (meaning only footer will be updated and not entire table).
And now, after you solved updating, you can focus on designing of all UI elements under h:panelGroup to make them fit your requirements.

How to render the p:commandButton at last row of the p:datatable?

I need to render the <p:commandButton> at last entry of the <p:datatable> only .
consider if p:datatable having n no.of rows means i have render the p:commandButton at nth row only.
I think that you better use <f:facet name="footer">
<f:facet name="footer">
<p:commandButton/>
</f:facet>
But if you really insist on last row...
Try the rowIndexVar, something like this :
<p:dataTable value="#{myBean.myList}" rowIndexVar="rowIndex" var="item">
<p:column>
<p:commandButton rendered="#{(rowIndex+1) eq myBean.myList.size()}"/>
</p:column>
</p:dataTable>
I don't know if I got the functional requirement right, but from what I understand you want to add a single commandButton at the bottom of the table, for that you might use the following inside datatable tags:
<f:facet name="footer">
<p:commandButton/>
</f:facet>

JSFpanelGrid dynamically populated

I want to render a panelGrid with a fixed number of columns but elements are loaded from a list. The code should be as follows:
<h:panelGrid columns="3">
<h:outputText value="Header 1"/>
<h:outputText value="Header 2"/>
<h:outputText value="Header 3"/>
<ui:repeat value="#{bean.collection}" var="obj">
<p:panel>
<h:outputText value="#{obj.value}"/>
</p:panel>
</ui:repeat>
</p:panelGrid>
The problem is this code is not rendering as I expected, because all panels are enclosed in the first TD generated by panelGrid, and I want a row break every 3 elements. It seems all repeat block is executed prior the rendering. I'm sure I can obtain this behaviour. What I'm doing wrong?
Thanks
ui:repeat is a component and it is part of the component tree. To create what you are planning try using tag handler c:forEach instead.
<c:forEach items="#{bean.collection}" var="obj">
<p:panel>
<h:outputText value="#{obj.value}"/>
</p:panel>
</c:forEach>

Value of <h:selectBooleanCheckbox> inside <p:dataTable> remains false on submit

I have a primefaces datatable and inside the primefaces datatable, I have a column, which contains the . The issue is,I have set the default value for the as false. When I click/check the , its still retrieving the value as false. I tried it multiple times but not sure why its returning false. Please find the sample code below.
<p:dataTable id="review-table" var="item" value="#{demandBean.filterVOList}">
<p:column id="SelectallID" style="text-align: left; width:40px;" rendered="#{demandBean.screeRenderVo.selectAllRenderer}">
<f:facet name="header" >
<h:outputText id="selectId" value="#{demandBean.dmdScreenLabelVO.selectAll}" />
<div></div>
<h:selectBooleanCheckbox id="checkbox1" value="Select All" onclick="checkAll(this)"/>
</f:facet>
<h:selectBooleanCheckbox id="checkbox2" value="#{item.selected}"/>
</p:column>
Im getting the value as false, when I check the and click on the save button. I have written an Action listerner, below is the code corresponding to the actionListener
public void saveData(ActionEvent event)
{
System.out.println("Entering the Save :");
selected = isSelected();
System.out.println("value of Selected"+selected);
}
I have tried debugging the code as well, but not sure why the value for is getting displayed as false. Please Assist. Thanks in Advance
You seem to be binding the value of all checkboxes in the column to the one and same bean property. This way the value will ultimately end up to be the one of the last row in the column.
This is not how it's supposed to be used.
You basically need to bind the value of the checkbox to the property of the currently iterated row object (the one behind the var attribute of the datatable).
<p:dataTable value="#{bean.items}" var="item">
<p:column>
<p:selectBooleanCheckbox value="#{item.selected}" />
Alternatively, you could use the <p:column selectionMode="multiple" /> to use builtin multiple selection support of the PrimeFaces datatable (see also the showcase example).
<p:dataTable value="#{bean.items}" var="item" rowKey="#{item.id}" selection="#{bean.selectedItems}">
<p:column selectionMode="multiple" />

Resources