h:dataTable Nested Sorting Issue - sorting

I have a sorting issue in a JSF h:dataTable.
I have to structure a table where the initial overall-sorting-criterion is a score-number in the first column.
If there are multiple entries with the same score, then those entries shall be sorted in function of a second column with names, which shall appear in alphabetical order.
If again there should be multiple entries with the same values in the first and second column, then those entries shall be sorted in function of a third column, which is a location name, which shall be listed again in alphabetical order.
If again there should be multiple entries with the same values in the first, second, and third column, then those entries shall be sorted in function of a fourth column, which is an age-number, which shall appear in ascending order.
I know that I can achieve the initial-sorting score-number with the "sortBy" attribute and sortingOrder=ascending. But I struggle with the other before mentioned sorting-requirements, which is where I am looking for help...Is there a way of doing this without having to use custom sort functions?
Here is the xhtml-code of the dataTable.
<h:dataTable value = "#{userData.employees}"
var = "employee"
styleClass = "employeeTable"
headerClass = "employeeTableHeader"
rowClasses = "employeeTableOddRow,employeeTableEvenRow"
sortBy=“#{employee.score}“ sortOrder=“ascending“>
<h:column>
<f:facet id = "header">score</f:facet>
#{employee.score}
</h:column>
<h:column>
<f:facet name = "header">Name</f:facet>
#{employee.name}
</h:column>
<h:column>
<f:facet name = "header">Location</f:facet>
#{employee.location }
</h:column>
<h:column>
<f:facet name = "header">Age</f:facet>
#{employee.age}
</h:column>
</h:dataTable>

TL;DR: You can do this by actually using the primefaces DataTable with sortMode="multiple" and sortBy="#{userData.employeeSortingMeta}" returning a List of org.primefaces.model.SortMeta instances.
First of all you'll have to convert your plain HTML JSF dataTable to a Primefaces Datatable, adding ID attributes to dataTable and columns plus adding sortBy attributes to each column:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
...
xmlns:p="http://primefaces.org/ui">
<h:form id="frmEmployees">
<p:dataTable id="tblEmployees" value = "#{userData.employees}"
var = "employee"
styleClass = "employeeTable"
headerClass = "employeeTableHeader"
rowClasses = "employeeTableOddRow,employeeTableEvenRow"
sortBy=“#{userData.employeeSortingMeta}“ sortMode=“multiple“>
<p:column id="score" sortBy="#{employee.score}">
<f:facet id = "header">score</f:facet>
#{employee.score}
</p:column>
<p:column id="name" sortBy="#{employee.name}">
<f:facet name = "header">Name</f:facet>
#{employee.name}
</p:column>
<p:column id="location" sortBy="#{employee.location}">
<f:facet name = "header">Location</f:facet>
#{employee.location }
</p:column>
<p:column id="age" sortBy="#{employee.age}">
<f:facet name = "header">Age</f:facet>
#{employee.age}
</p:column>
</p:dataTable>
In your bean, create methods to build the sorting meta data:
private List<SortMeta> employeeSortingMeta;
public List<SortMeta> getEmployeeSortingMeta() {
return this.employeeSortingMeta;
}
#PostConstruct
public void initialize() {
employeeSortingMeta = new ArrayList<>(4);
employeeSortingMeta.add(createSortMeta("score", SortOrder.DESCENDING));
employeeSortingMeta.add(createSortMeta("name", SortOrder.ASCENDING));
employeeSortingMeta.add(createSortMeta("location", SortOrder.ASCENDING));
employeeSortingMeta.add(createSortMeta("age", SortOrder.ASCENDING));
}
private SortMeta createSortMeta(final String sortProperty, final SortOrder order) {
// find the sort column component
final UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
final String columnIdPrefix = "frmEmployees:tblEmployees:";
final UIColumn column = (UIColumn) viewRoot.findComponent(columnIdPrefix + sortProperty);
assert null != column : "wtf?";
// create sort meta
final SortMeta sortMeta = new SortMeta();
sortMeta.setSortBy(column);
sortMeta.setSortField(sortProperty);
sortMeta.setSortOrder(order);
return sortMeta;
}
That should be it.

Related

Display Column Total in Primefaces data table

The Primefaces table which will be filtered by different column attributes needs to have a summary row that will update its value as the user change the values in filters. Is there any way to do this?
Create a Column Group footer in the payments.xhtml:
<p:dataTable var="payments" value="#{testBean.payments}" filteredValue="#{testBean.filteredPayments}">
<p:column headerText="Payment status:" filterBy="#{payment.status}">
<h:outputText value="#{payment.status}"/>
</p:column>
<p:columnGroup type="footer">
<p:row>
<p:column colspan="6" headerText="Total: "/>
<p:column colspan="2" headerText="#{testBean.totalPaymentsSum}"/>
</p:row>
</p:columnGroup>
</p:dataTable/>
In the TestBean.java add a method as a getter to calculate the summary row:
List<Payment> payments;
List<Payment> filteredPayments;
public String getTotalPaymentsSum() {
int total = 0;
for(Payment payment : filteredPayments) {
total += payment.getAmount();
}
return new DecimalFormat("###,###.###").format(total);
}
For more filter option see the Datatable Filter showcase.
If you have a lot of data and you are using datatable Lazy loading, you can calculate the summary row in the load() or filter() method.

Table disappears after cell edit in primefaces (also data not saved)

I have an input field and a table on page. Whenever a number in a field changes (ajax listener to change event) - I want number of rows to change accordingly.
I pointed the table to an Arraylist in a Backing bean, and play with its size.
But then when I edit a value in the table - and press enter, the table disappears, and reappears only when I change field value again.
As a bonus, in debug mode, I see that backing arraylist always have empty values.
Here goes the Bean code:
#ManagedBean(name = "bean")
#ViewScoped
public class TestBean {
private List<String> hostnames = new ArrayList<String>();
private int copiesQuantityJustCopy;
public int getCopiesQuantityJustCopy() {
return copiesQuantityJustCopy;
}
public void setCopiesQuantityJustCopy(int copiesQuantityJustCopy) {
this.copiesQuantityJustCopy = copiesQuantityJustCopy;
}
public List<String> getHostnames() {
return hostnames;
}
public void setHostnames(List<String> hostnames) {
this.hostnames = hostnames;
}
public void onUpdateCount() {
int hostnamesCount = hostnames.size();
System.out.println("delta = " + hostnamesCount + " - " + copiesQuantityJustCopy);
int delta = hostnamesCount - copiesQuantityJustCopy;
if (delta > 0)
for (int i = 1; i < delta; i++)
hostnames.remove(delta);
if (delta < 0)
for (int i = 0; i < -delta; i++)
hostnames.add("");
}
}
and the view code:
<h:form id="form1">
<p:inputMask mask="9?99" maxlength="3" placeHolder=" " value="#{bean.copiesQuantityJustCopy}">
<p:ajax event="change" listener="#{bean.onUpdateCount()}" update=":form1:hostnamesTable" />
</p:inputMask>
<p:outputPanel id="hostnamesTable" rendered="true">
<p:dataTable value="#{bean.hostnames}" var="hostname"
id="hostnames" editable="true" editMode="cell">
<p:ajax event="cellEdit" update=":form1:hostnamesTable" process="#this" />
<p:column>
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{hostname}" />
</f:facet>
<f:facet name="input">
<p:inputText value="#{hostname}" style="width:96%" />
</f:facet>
</p:cellEditor>
</p:column>
</p:dataTable>
</p:outputPanel>
</h:form>
I think my answer won't help #Anton, but it may be helpful to another "stackers" facing same problem.
My problem is similar (dataTable dissapears when cellEdit event is completed). In my case the cause was the table update performed by ajax. Since I realy don't need table update after listener firied I simply removed update attribute and now everything works good to me.

JSF submitButton that renders on datatable

This is my JSF(2.2) page :
<h:form>
<p>
Hiker-name
</p>
<p>
<h:selectOneMenu id="smenu">
<f:ajax render="hikerActivities"/>
<c:forEach items="#{hikerPresenter.hikers}" var="hiker">
<f:selectItem itemValue="#{hiker.hikerId}" itemLabel="#{hiker.hikerName}"/>
</c:forEach>
</h:selectOneMenu>
<h:commandButton id="submitHiker" value="Submit" action="#{hikerPresenter.getHikerActivities(hiker.hikerId)}">
<f:ajax execute="smenu" render="hikerActivities"/>
</h:commandButton>
</p>
<p>
<h:dataTable id="hikerActivities" var="hikerActivity" border="1">
<h:column>
<f:facet name="header" >Trip-date</f:facet>
#{hikerActivity.tripDate}
</h:column>
<h:column>
<f:facet name="header">Trip-fare</f:facet>
#{hikerActivity.tripFare}
</h:column>
<h:column>
<f:facet name="header">Trip-duration</f:facet>
#{hikerActivity.tripDuration}
</h:column>
</h:dataTable>
</p>
I want that with a click on the command button, my MBean's method get called with the hikerId from the selected item and gets render on the datatable.
this is my MBean :
#ManagedBean
#RequestScoped
public class HikerPresenter {
#EJB
private HikerControllerLocal hikerController;
public HikerPresenter() {
}
public List<HikerAccessDTO> getHikers(){
return hikerController.getHikers();
}
public List<HikerActivityDTO> getHikerActivities(long hikerId){
System.out.println("Get Hiker Activities Called");
List<HikerActivityDTO> l = new ArrayList();
l.add(new HikerActivityDTO(new Date(1417150800000L), "Montréal", "Valleyfield", "Amine", 22, 45));
l.add(new HikerActivityDTO(new Date(1417160800000L), "Montréal", "Québec", "Farida", 33, 44));
l.add(new HikerActivityDTO(new Date(1417170800000L), "Montréal", "Alloha", "dsdsd", 12, 2332));
return l;
}
}
I suppose that I call the MBean method getHikerActivities on the wrong place because it doesn't even System.out.
What am-I doing wrong?
1:
use f:selectItems instead of f:selectItem
2:
your table missed the value attribute like value="hikerPresenter.hikers"
3:
You must set the value attribute of h:selectOneMenu to set hikerId into the bean, so its not necessary to send it as parameter in the method getHikerActivities

Problems with ajax in jsf updating an output text field

I'm new to JSF and in class we should just set up a little "Pizza-Service" with logins etc. On the Page where i want to prepare my Order as a user I've a data table (primeface data table) with 4 columns. The last one is for the number of pizza of the type in the current cell.
What I want: If I am typing in a Number i want to set up a current Order (works fine so far with ajax) in the same moment i just want to show the user the current price. This is the little thing that is not working.
When I am using the tag there is just nothing happening and if I am taking the tag I'm getting this mistake:
<f:ajax> contains an unknown id 'ausgabe' - cannot locate it in the context of the component menge
What I thought was take the ajax tag set up a function (take the current order) and render an output text where the calculated price is shown directly. I called those function in german so bestellungAufnehmen = takeOrder and preisBerechnen = calculatePrice.
Thanks if someone could help me!
Here is the xhtml:
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<p:dataTable id="table" var="pizza" value="#{model.getPizzen()}">
<p:column headerText="Nr">
<h:outputText value="#{pizza.nr}"/>
</p:column>
<p:column headerText="Artikel">
<h:outputText value="#{pizza.name}"/>
</p:column>
<p:column headerText="Preis">
<h:outputText value="#{pizza.preis}"/>
</p:column>
<p:column headerText ="Menge">
<h:inputText id="menge" value="#{bestellung.anzahl}">
<f:ajax event="keyup" render="ausgabe" listener="#{bestellung.bestellungAufnehmen(pizza)}"/>
</h:inputText>
</p:column>
</p:dataTable>
<h:outputLabel value="Zu Zahlen: "/>
<h:outputText id="ausgabe" value="#{bestellung.preisBerechnen()} €"/>
</h:form>
</h:body>
and here is the the Bean with those functions.
#Named
#SessionScoped
public class Bestellung implements Serializable {
private static int bstzaehler=0;
private int bstnr;
private Map<String,Bestellposten> posten;
private int anzahl;
public Bestellung(){
this.bstnr=++bstzaehler;
this.posten = new HashMap<>();
}
/**
* #return the bstnr
*/
public int getBstnr() {
return bstnr;
}
/**
* #param bstnr the bstnr to set
*/
public void setBstnr(int bstnr) {
this.bstnr = bstnr;
}
public void bestellungAufnehmen(Pizza pizza){
if(this.posten.containsKey(pizza.getName()) == false){
Bestellposten b = new Bestellposten(this.getAnzahl(), pizza);
this.posten.put(pizza.getName(),b);
} else {
if(this.getAnzahl() > 0 ){
Bestellposten tmp = this.posten.get(pizza.getName());
tmp.setMenge(this.getAnzahl());
} else {
this.posten.remove(pizza.getName());
}
}
System.out.println("groesse: "+posten.size());
System.out.println("akt preis: "+this.preisBerechnen());
}
/**
* #return the anzahl
*/
public int getAnzahl() {
return anzahl;
}
/**
* #param anzahl the anzahl to set
*/
public void setAnzahl(int anzahl) {
this.anzahl = anzahl;
}
public double preisBerechnen(){
double sum = 0.0;
for(String key : this.posten.keySet()){
Pizza tmp = this.posten.get(key).getPizza();
sum += tmp.getPreis() * this.posten.get(key).getMenge();
}
return sum;
}
}
Try giving the full qualified id for the component:
<h:form id="frmPizza">
<p:dataTable id="table" var="pizza" value="#{model.getPizzen()}">
<p:column headerText="Nr">
<h:outputText value="#{pizza.nr}"/>
</p:column>
<p:column headerText="Artikel">
<h:outputText value="#{pizza.name}"/>
</p:column>
<p:column headerText="Preis">
<h:outputText value="#{pizza.preis}"/>
</p:column>
<p:column headerText ="Menge">
<h:inputText id="menge" value="#{bestellung.anzahl}">
<f:ajax event="keyup" render=":frmPizza:ausgabe" listener="#{bestellung.bestellungAufnehmen(pizza)}"/>
</h:inputText>
</p:column>
</p:dataTable>
<h:outputLabel value="Zu Zahlen: "/>
<h:outputText id="ausgabe" value="#{bestellung.preisBerechnen()} €"/>
</h:form>
I think, you should use update attribute to update that outputText, like update=":FormId:ausgabe"
<f:ajax event="keyup"
render=":frmPizza:ausgabe"
listener="#{bestellung.bestellungAufnehmen(pizza)}"
update=":frmPizza:ausgabe" />
The solution of luiggi worked but i couldnt answer directly because of my "low-level" here.
Iam not on my computer right now but isnt "update" an attribute of p:ajax and not of f:ajax and is the same as render? but i will try it aswell later on.
thx!

Issue faced with Trinidad Table, Partial Trigger, and Read Only

I have a trinidad table with one column contains drop downs (select one choice) for each row and another column contains an input text for each row.
When a particular option is selected, the input becomes editable (else it remains read only).
The read only behaviour and the partial event are working perfectly as expected. The problem is whenever the input text is supposed to move from editable to read only, I still see the value previously entered when the input text was editable.
From here if I select another option (for which the input text would also be read only) the value goes away.
The JSP Code is as follows:
<tr:table emptyText="No Data" var="vo" autoSubmit="true" partialTriggers="accountType" value="#{testingBean.voList}" binding="#{testingBean.table}" width="50%">
<tr:column>
<f:facet name="header">
<tr:outputText value="Sl No" />
</f:facet>
<tr:outputText value="#{vo.slNo}" />
</tr:column>
<tr:column>
<f:facet name="header">
<tr:outputText value="Account Type" />
</f:facet>
<tr:selectOneChoice unselectedLabel="Select" id="accountType" value="#{vo.accountType}"
autoSubmit="true" immediate="true" valuePassThru="true"
valueChangeListener="#{testingAction.optionsChanged}">
<f:selectItems value="#{testingBean.options}" />
</tr:selectOneChoice>
</tr:column>
<tr:column>
<f:facet name="header">
<tr:outputText value="Other" />
</f:facet>
<tr:inputText value="#{vo.otherType}"
partialTriggers="accountType"
readOnly="#{vo.readOnlyFlag}"></tr:inputText>
</tr:column>
</tr:table>
The code for the valueChangeListener is as follows:
public void optionsChanged(ValueChangeEvent vce) {
try {
System.out.println("Inside Method");
testingBean = (TestingBean) FacesContext.getCurrentInstance()
.getExternalContext().getSessionMap().get("testingBean");
ArrayList<TableRowVO> list = testingBean.getVoList();
UIXTable table = testingBean.getTable();
TableRowVO vo = list.get(table.getRowIndex());
vo.setReadOnlyFlag(true);
vo.setAccountType(vce.getNewValue().toString());
vo.setOtherType("");
if (vce.getNewValue().equals("3")) {
System.out.println("Setting false");
vo.setReadOnlyFlag(false);
}
list.set(table.getRowIndex(), vo);
table.setValue(list);
testingBean.setVoList(list);
FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().put("testingBean", testingBean);
} catch (Exception e) {
e.printStackTrace();
}
}
Is this behaviour expected? Or is there a workaround?
Interestingly, the "View Page Source" option in Firefox does not contain the text, even though I can see it on the page. I had to use FireBug to analyze the element which revealed the following:
<div class="af_inputText_content"
id="j_id_jsp_545172797_17:0:j_id_jsp_545172797_27">
tttt
</div>
Version used:
Trinidad 2.x
JSF 2.x
Tomcat 7.0
Ok, so I got it to work a while back but forgot to post the answer....
I used FacesContext to find the CoreComponent for the input text and then reset the components value.
Basically:
Find the CoreTable object
Find the CoreColumn object
Find the CoreInputText object
Reset the value for CoreInputText
The Modified JSP Code is as follows:
<tr:table emptyText="No Data" var="vo" autoSubmit="true" partialTriggers="accountType" id="voTable" value="#{testingBean.voList}" binding="#{testingBean.table}" width="50%">
<tr:column id="slNoCol">
<f:facet name="header">
<tr:outputText id="slNoHdr" value="Sl No" />
</f:facet>
<tr:outputText id="slNo" value="#{vo.slNo}" />
</tr:column>
<tr:column id="accountTypeCol">
<f:facet name="header">
<tr:outputText id="accountTypeHdr" value="Account Type" />
</f:facet>
<tr:selectOneChoice unselectedLabel="Select" id="accountType" value="#{vo.accountType}" autoSubmit="true" immediate="true" valuePassThru="true" valueChangeListener="#{testingAction.optionsChanged}">
<f:selectItems value="#{testingBean.options}" />
</tr:selectOneChoice>
</tr:column>
<tr:column id="otherTypeCol">
<f:facet name="header">
<tr:outputText id="otherTypeHdr" value="Other" />
</f:facet>
<tr:inputText value="#{vo.otherType}" columns="15" partialTriggers="accountType" id="otherType" readOnly="#{vo.readOnlyFlag}" immediate="true">
</tr:inputText>
</tr:column>
</tr:table>
The code for the modified valueChangeListener is as follows:
public void optionsChanged(ValueChangeEvent vce) {
try {
testingBean = (TestingBean) FacesContext.getCurrentInstance()
.getExternalContext().getSessionMap().get("testingBean");
ArrayList<TableRowVO> list = testingBean.getVoList();
CoreTable table = (CoreTable) FacesContext.getCurrentInstance()
.getViewRoot().findComponent("voTable");
CoreColumn column = (CoreColumn) table
.findComponent("otherTypeCol");
CoreInputText text = (CoreInputText) column
.findComponent("otherType");
text.setValue("");//This is the trick....
TableRowVO vo = list.get(table.getRowIndex());
vo.setReadOnlyFlag(true);
vo.setAccountType(vce.getNewValue().toString());
vo.setOtherType("");
if (vce.getNewValue().equals("3")) {
vo.setReadOnlyFlag(false);
}
list.set(table.getRowIndex(), vo);
table.setValue(list);
testingBean.setVoList(list);
FacesContext.getCurrentInstance().getExternalContext()
.getSessionMap().put("testingBean", testingBean);
} catch (Exception e) {
e.printStackTrace();
}
}

Resources