Suppose the code of this page:
<h:form prependId="false" id="form">
<h:selectManyCheckbox id="checkBoxList" value="#{backedBean.lstIdSelectedItems}" layout="pageDirection">
<f:selectItems value="#{backedBean.lstAvailableItems}" var="item" itemLabel="#{item.label}" itemValue="#{item.value}" />
<f:ajax listener="#{backedBean.itemClicked}" />
</h:selectManyCheckbox>
</h:form>
And the code of a session managed bean:
public class BackedBean implements Serializable {
private List<SelectItem> lstAvailableItems;
private List<Long> lstIdSelectedItems;
public BackedBean() {
lstAvailableItems = new ArrayList<SelectItem>();
lstIdSelectedItems = new ArrayList<Long>();
}
#PostConstruct
private void postConstruct(){
for (int i = 0; i < 10; i++) {
SelectItem item = new SelectItem(new Long(i), "CHKID " + i);
lstAvailableItems.add(item);
}
}
public void itemClicked(AjaxBehaviorEvent ae){
HtmlSelectManyCheckbox uiCmp = (HtmlSelectManyCheckbox)ae.getSource();
// (1) Here I would like to get the ID of the item that has been clicked.
}
In (1) I would like to get the ID of the element that has been clicked by the user. I can see in the lstIdSelectedItems array list the IDs of all elements selected by the user, but how can I get the ID of the element that the user has clicked?
I have tried to use the f:attribute tag inside of the selectManyCheckbox, but the attribute is not in the component map when the ajax listener method is called in the backed bean. I have used this, but doesn't work:
<h:selectManyCheckbox id="checkBoxList" value="#{backedBean.lstIdSelectedItems}" layout="pageDirection">
<f:selectItems value="#{backedBean.lstAvailableItems}" var="item" itemLabel="#{item.label}" itemValue="#{item.value}">
<f:attribute name="clicked" value="#{item.value}" />
</f:selectItems>
<f:ajax listener="#{backedBean.itemClicked}" />
</h:selectManyCheckbox>
Any ideas?
Regards.
You're thus interested in the actual value change and not only in the new value. Bring in a valueChangeListener which compares the old value with the new value and prepares some properties which the ajax listener method could intercept on.
E.g.
<h:selectManyCheckbox value="#{bean.selectedItems}" valueChangeListener="#{bean.selectedItemsChanged}" converter="javax.faces.Long">
<f:selectItems value="#{bean.availableItems}" />
<f:ajax listener="#{bean.itemSelected}" />
</h:selectManyCheckbox>
with
private Map<String, Long> availableItems; // +getter
private List<Long> selectedItems; // +getter+setter
private Long selectedItem;
private boolean selectedItemRemoved;
#PostConstruct
public void init() {
availableItems = new LinkedHashMap<String, Long>();
for (long i = 0; i < 10; i++) {
availableItems.put("CHKID " + i, i);
}
}
public void selectedItemsChanged(ValueChangeEvent event) {
List<Long> oldValue = (List<Long>) event.getOldValue();
List<Long> newValue = (List<Long>) event.getNewValue();
if (oldValue == null) {
oldValue = Collections.emptyList();
}
if (oldValue.size() > newValue.size()) {
oldValue = new ArrayList<Long>(oldValue);
oldValue.removeAll(newValue);
selectedItem = oldValue.iterator().next();
selectedItemRemoved = true;
}
else {
newValue = new ArrayList<Long>(newValue);
newValue.removeAll(oldValue);
selectedItem = newValue.iterator().next();
selectedItemRemoved = false;
}
}
public void itemSelected(AjaxBehaviorEvent event) {
System.out.println("Selected item: " + selectedItem);
System.out.println("Selected item removed? " + selectedItemRemoved);
}
When in the list "selectedItems" you unchecked the end element, the code no call the method itemSelected.
Related
I've a dataTable within a dataTable. The initial dataTable has a list of components that appear on the page. One of these components will be a list of strings which can have elements added or deleted. When I change a string in this list, I expect the value to show up in the bean and it is not.
Below I have an example of my problem. The page renders a text input field for the first component then three text input fields to represent the second component which is a list of three input fields.
I have valueChange listener on all the input fields. The listener, is in the InnerBean class, prints out the source and the value that changed.
For the standalone input field, the listener correctly prints out the changed value and shows that the bean has been updated with this value. For any of the input fields from the list, the listener prints out the previous value of the input field and the bean has not been updated. On the ajax update of the inner datatable, the changed value is replace with the original value.
Since the valueChange listener is called, it appears that Primefaces knows that the value has changed. The code just doesn't seem to record the changed value.
Any help is appreciated.
I'm using Primefaces 8.0 and JSF 2.2.20.
Here is the xhtml:
<p:panel id="testPanel" header="#{myController.outerBean.name}" toggleable="true" collapsed="false" >
<p:dataTable id="testTable" value="#{myController.outerBean.innerBeanList}" var="bean">
<p:column >
<!-- TEXT COMPONENT-->
<h:panelGroup rendered="#{bean.type eq 'text'}" >
<p:inputText id="textfield" value="#{bean.value}" style="width:100%;" >
<p:ajax event="valueChange" listener="#{bean.textListListener}" update="testTable" />
</p:inputText>
</h:panelGroup>
<!-- LIST COMPONENT -->
<h:panelGroup rendered="#{bean.type eq 'textlist'}" >
<p:dataTable id="testListTable" styleClass="datatableWithoutBorder" style="width:320px"
var="textAddition" value="#{bean.list}" rowIndexVar="rowIndex" >
<p:column >
<p:inputText id="textAdd" value="#{textAddition}" style="width: 100%;">
<p:ajax event="valueChange" listener="#{bean.textListListener}" update="testListTable"/>
</p:inputText>
</p:column>
</p:dataTable>
</h:panelGroup>
</p:column>
</p:dataTable>
<h:panelGrid columns="1" style="width:100%;">
<h:panelGroup style="float:right">
<p:commandButton id="submitBtn" value="Submit"
action="#{dummyController.submit}"
update="messages #this"
icon="fa fa-save"/>
</h:panelGroup>
</h:panelGrid>
</p:panel>
My controller code:
public class MyController {
private OuterBean outerBean;
public MyController() {
System.out.println("MyController instantiated");
setOuterBean(new OuterBean());
}
public void submit() {
for (InnerBean ab: outerBean.getInnerBeanList()) {
System.out.println(ab.getLabel() + ": " + ab.getValue() + ":" + ab.getList() );
}
}
public void clear() {
// TODO
}
// Getter/Setter methods
public OuterBean getOuterBean() {
return outerBean;
}
public void setOuterBean(OuterBean outerBean) {
this.outerBean = outerBean;
}
}
My OuterBean with the list of components:
public class OuterBean implements Serializable {
private String name;
private String value;
private List<InnerBean> innerBeanList;
public OuterBean() {
name = "Entry Panel #1";
value = "";
innerBeanList = new ArrayList<InnerBean>();
InnerBean ab1 = new InnerBean();
ab1.setLabel("First Component");
ab1.setType("text");
ab1.setValue("Input text");
innerBeanList.add(ab1);
InnerBean ab2 = new InnerBean();
ab2.setLabel("Second Component");
ab2.setType("textlist");
ArrayList<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
ab2.setList(list);
innerBeanList.add(ab2);
}
//
// Getter/Setters
//
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public List<InnerBean> getInnerBeanList() {
return innerBeanList;
}
public void setInnerBeanList(List<InnerBean> innerBeanList) {
this.innerBeanList = innerBeanList;
}
}
My InnerBean which represents a component to be render. One of which can be a list of strings:
public class InnerBean implements Serializable {
// Type of component
public static final String TEXT = "text";
public static final String TEXTLIST = "textlist";
private String label;
private String type; // If TEXT, use value; if TEXTLIST, use list.
private String value;
private List<String> list = new ArrayList<String>();
public InnerBean() {
}
public void textListListener(AjaxBehaviorEvent event) {
System.out.println("Listener called");
System.out.println(" Source: " + event.getSource().toString());
System.out.println(" Value: " + ((UIInput)event.getSource()).getValue());
System.out.println(" List: " + list.toString());
System.out.println(" Event: " + event.toString());
}
//
// Setters and getters
//
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
There is a list that is dynamically generated at runtime. I am using a dataTable to represent it. Inside each row is a dropdown list. As soon as the user selects a value from the drop down list in a row, then all the other rows must be disabled.?
<h:dataTable value="#{user.orderList}" var="item">
<h:column>
<h:selectOneMenu value="#{user.sometuff}" >
<f:selectItems value="#{user.someItems}" />
</h:selectOneMenu>
</h:column>
</h:dataTable>
How can I achieve this with <f:ajax>?
here is a working example.
imho, it's way easier to implement with ajax4jsf (richfaces), using a4j:repeat and a4j:ajax tags.
xhtml code:
<h:form id="form">
<h:dataTable id="tableId" value="#{user.orderList}" var="item">
<h:column>
<h:selectOneMenu value="#{item.selectedItem}" disabled="#{user.oneItemSelected and (item.selectedItem == null || item.selectedItem == '')}">
<f:selectItems value="#{user.selectItemList}" />
<f:ajax execute="#this" render="#form" listener="#{user.updateSelectionFlag}"></f:ajax>
</h:selectOneMenu>
</h:column>
</h:dataTable>
</h:form>
order item code:
public class Order implements Serializable
{
private static final long serialVersionUID = 1L;
private String selectedItem;
public String getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(String selectedItem) {
this.selectedItem = selectedItem;
}
}
managed bean code:
#Named
#ViewScoped
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private List<Order> orderList;
private List<SelectItem> selectItemList;
private boolean oneItemSelected;
public List<SelectItem> getSelectItemList() {
if (selectItemList == null)
{
selectItemList = new ArrayList<SelectItem>();
selectItemList.add(new SelectItem("", "---Please Choose---"));
selectItemList.add(new SelectItem("1", "Item 1"));
selectItemList.add(new SelectItem("2", "Item 2"));
selectItemList.add(new SelectItem("3", "Item 3"));
selectItemList.add(new SelectItem("4", "Item 4"));
}
return selectItemList;
}
public List<Order> getOrderList() {
if (orderList == null)
{
orderList = new ArrayList<Order>();
for (int i=0 ; i<4 ; i++)
{
orderList.add(new Order());
}
}
return orderList;
}
public boolean isOneItemSelected() {
return oneItemSelected;
}
public void updateSelectionFlag()
{
oneItemSelected = false;
for (int i=0 ; i<getOrderList().size() ; i++)
{
Order order = getOrderList().get(i);
if (order.getSelectedItem() != null && !order.getSelectedItem().equals(""))
{
oneItemSelected = true;
break;
}
}
}
}
by the way, instead of checking the whole array in updateSelectionFlag, it's better to check only the submitted item's value. but i couldn't figure out how to get the clicked row index of h:dataTable. doing a binding prevents combo values from being submitted, and datatable does not provide a varStatus attribute like ui:repeat does.
I have two SelectoneMenu and one inputText. within inputText I called one Ajax event(blur). But this ajax method does not get selected value from SelectoneMenu. But I called the same method using CommandButton it will get selected values and worked fine.
Here is My Code:
<h:form id=HarvestRateForm>
<table width="670px;">
<tr>
<td width="100px;"><p:outputLabel value="Crushing Season" for="crushingSeason"/></td>
<td width="80px;">
<p:selectOneMenu id="crushingSeason" style="width: 110px;" value="#{harvestRateBean.selectedSeason}">
<f:selectItem itemValue="#{null}" itemLabel="Select"/>
<f:selectItems value="#{harvestRateBean.seasons}"/>
</p:selectOneMenu><p:growl for="crushingSeason"/>
</td>
<td width="60px;" align="right"><p:outputLabel value="Plant" for="plant"/></td>
<td width="80px;"><p:selectOneMenu id="plant" style="width: 85px;" value="#{harvestRateBean.selectedPlant}">
<f:selectItem itemValue="#{null}" itemLabel="Select"/>
<f:selectItems value="#{harvestRateBean.plants}"/>
</p:selectOneMenu><p:growl for="plant"/></td>
<td width="60px;" align="right"><p:outputLabel value="Plot No" for="plotNo"/></td>
<td><p:inputText id="plotNo" value="#{harvestRateBean.sapPlotNo}" size="16">
<p:ajax event="blur" listener="#{harvestRateBean.loadPlotDetails}"
update="HarvestRateForm:plotNo HarvestRateForm:ryotCode"/>
</p:inputText>
<p:growl for="plotNo"/>
</td>
</tr>
<tr>
<td><p:outputLabel value="Ryot No" for="ryotCode"/></td>
<td><p:inputText value="#{harvestRateBean.ryotNo}" size="15" id="ryotCode" readonly="true" style="background: transparent;background-color: #cccccc"/></td>
My Bean Class:
public class HarvestRateBean implements Serializable {
private Map<String, String> seasons;
private Map<String, String> plants;
private String selectedPlant;
private String selectedSeason;
private String sapPlotNo;
private String ryotNo;
public HarvestRateBean() {
seasons = new HashMap<String, String>();
plants = new HashMap<String, String>();
}
public Map<String, String> getSeasons() {
List<Season> season_list = loadSeason();
for (Iterator<Season> it = season_list.iterator(); it.hasNext();) {
Season season1 = it.next();
seasons.put(season1.getSeason(), season1.getSeason());
}
return seasons;
}
public void setSeasons(Map<String, String> seasons) {
this.seasons = seasons;
}
public List<Season> loadSeason() {
Session session = HibernateUtil.getSessionFactory().openSession();
List<Season> seasonlist = null;
try {
seasonlist = session.createCriteria(Season.class).list();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
session.close();
HibernateUtil.getSessionFactory().close();
}
return seasonlist;
}
public Map<String, String> getPlants() {
List<Plant> plant_list = loadPlants();
for (Iterator<Plant> it = plant_list.iterator(); it.hasNext();) {
Plant plant = it.next();
plants.put(plant.getId().getPlant(), plant.getId().getPlant());
}
return plants;
}
public void setPlants(Map<String, String> plants) {
this.plants = plants;
}
public List<Plant> loadPlants() {
Session session = HibernateUtil.getSessionFactory().openSession();
List<Plant> plantlist = null;
try {
plantlist = session.createCriteria(Plant.class).list();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
session.close();
}
return plantlist;
}
public String getSelectedPlant() {
return selectedPlant;
}
public void setSelectedPlant(String selectedPlant) {
this.selectedPlant = selectedPlant;
}
public String getSelectedSeason() {
return selectedSeason;
}
public void setSelectedSeason(String selectedSeason) {
this.selectedSeason = selectedSeason;
}
public String getSapPlotNo() {
return sapPlotNo;
}
public void setSapPlotNo(String sapPlotNo) {
this.sapPlotNo = sapPlotNo;
}
public String getRyotNo() {
return ryotNo;
}
public void setRyotNo(String ryotNo) {
this.ryotNo = ryotNo;
}
public void loadPlotDetails {
Session session = HibernateUtil.getSessionFactory().openSession();
List<HarvesterRate> rateList = null;
FacesContext context = FacesContext.getCurrentInstance();
try {
if (getSelectedSeason() == null || getSelectedSeason().isEmpty()) {
context.addMessage(null, new FacesMessage("Season is required", ""));
} else if (getSelectedPlant() == null || getSelectedPlant().isEmpty()) {
context.addMessage(null, new FacesMessage("Plant is required", ""));
} else if (getSapPlotNo() == null || getSapPlotNo().isEmpty()) {
context.addMessage(null, new FacesMessage("Plot No is required", ""));
} else {
rateList = session.createCriteria(HarvesterRate.class).add(Restrictions.eq("id.season", getSelectedSeason())).add(Restrictions.eq("id.plant", getSelectedPlant())).add(Restrictions.eq("id.plotNo", getSapPlotNo())).list();
if (rateList.size() > 0) {
for (Iterator<HarvesterRate> it = rateList.iterator(); it.hasNext();) {
HarvesterRate harvesterRate = it.next();
setSapPlotNo(harvestRate.getPlotNo());
setRyotNo(harvestRate.getVendorCode());
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
getSelectedSeason() and getSelectedPlant() always return null in ajax Call but I Called same method using CommandButton it return Selected value and everything works fine.
The <p:ajax> will by default process only the parent ClientBehavior component, which is in your particular case the <p:inputText>. In other words, the actual value of <p:ajax process> is #this. If you actually want to process the entire form, then you should be using #form as in
<p:inputText ...>
<p:ajax process="#form" ... />
</p:inputText>
The process attribute of <p:commandButton> defaults to #form, that's why it works therewith.
Or, if you have more input components in the form and intend to process only the input and those two dropdowns, then you can specify the (relative) client IDs space separated:
<p:inputText ...>
<p:ajax process="crushingSeason plant #this" ... />
</p:inputText>
Implement ajaxEvent in bean class
Ajax event is not like normal method rather than it invokes the listeners as input, you can configure the Blur action on a text field as following
In JSF page
<p:inputText id="name" value="#{loginBean.name}">
<f:ajax event="blur" listener="#{loginBean.ajaxEvent}" />
</p:inputText>
Ensure that the above must be inside in <h:form>
In ManagedBean class
public void ajaxEvent(AjaxBehaviorEvent e)
{
//Do your stuffs
}
Know more about JSF start from here
I do have a page, where I have a list of rich:collapsiblePanel that hold input elements. These collapsiblePanels themselves store their expanded/collapsed state within a backing bean.
Now I have the use case to Open/Close all of these collapsiblePanels at once, with one mouse click. So I have tried to achieve this with the two commandButtons over the list. These use the attached actionListener to iterate over all backing beans of the collapsiblePanels and set the expanded flag to true/false.
This seems to work, unless you Open or Close one of the collapsiblePanels on their own. As soon as that happens clicking the buttons does not do anything anymore.
<h:form prependId="false">
<a4j:commandButton value="Open All" actionListener="#{viewBean.doOpenAll}" render="c" />
<a4j:commandButton value="Close All" actionListener="#{viewBean.doCloseAll}" render="c" style="margin-left: 10px;" />
<a4j:outputPanel id="c">
<a4j:repeat id="repeat" value="#{viewBean.items}" var="item">
<rich:collapsiblePanel id="panel" expanded="#{item.expanded}">
<h:outputLabel id="text_lbl" value="text" />
<h:inputText id="text" value="#{item.text}" />
</rich:collapsiblePanel>
</a4j:repeat>
</a4j:outputPanel>
</h:form>
I have published a project on github so that you can try around with the code.
For completeness here are the two backing beans
#ViewScoped
#ManagedBean
public class ViewBean implements Serializable {
static final Logger LOG = LoggerFactory.getLogger(ViewBean.class);
private static final long serialVersionUID = -6239437588285327644L;
private List<ListItem> items;
public ViewBean() {
items = new ArrayList<ListItem>(10);
for (int i = 0; i < 10; i++) {
items.add(new ListItem("item " + i));
}
}
public void doOpenAll() {
LOG.debug("open all");
for (ListItem item : items) {
item.setExpanded(true);
}
}
public void doCloseAll() {
LOG.debug("close all");
for (ListItem item : items) {
item.setExpanded(false);
}
}
public List<ListItem> getItems() {
return items;
}
}
public class ListItem {
private boolean expanded;
private String text;
public ListItem(String text) {
super();
this.text = text;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
This may be related to this RichFaces bug?!: https://issues.jboss.org/browse/RF-11546
I just enabled multiple sorting in the showcase code for "DataTable - Lazy Loading"
datatableLazy.xhtml
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>CarDataTable</title>
</h:head>
<h:body>
<h:form id="form">
<p:dataTable var="car" value="#{tableBean.lazyModel}" paginator="true"
rows="10"
paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}"
rowsPerPageTemplate="5,10,15" id="carTable" lazy="true"
sortMode="multiple">
<p:ajax event="rowSelect" listener="#{tableBean.onRowSelect}"
update=":form:display" oncomplete="carDialog.show()" />
<p:column headerText="Model" sortBy="#{car.model}"
filterBy="#{car.model}">
<h:outputText value="#{car.model}" />
</p:column>
<p:column headerText="Year" sortBy="#{car.year}"
filterBy="#{car.year}">
<h:outputText value="#{car.year}" />
</p:column>
<p:column headerText="Manufacturer" sortBy="#{car.manufacturer}"
filterBy="#{car.manufacturer}">
<h:outputText value="#{car.manufacturer}" />
</p:column>
<p:column headerText="Color" sortBy="#{car.color}"
filterBy="#{car.color}">
<h:outputText value="#{car.color}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
TableBean.java
package com.solartis.primefaces.sample;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.faces.bean.ManagedBean;
import org.primefaces.model.LazyDataModel;
#ManagedBean
public class TableBean {
private LazyDataModel<Car> lazyModel;
private Car selectedCar;
private List<Car> cars = new ArrayList<Car>();
private final static String[] colors;
private final static String[] manufacturers;
static {
colors = new String[10];
colors[0] = "Black";
colors[1] = "White";
colors[2] = "Green";
colors[3] = "Red";
colors[4] = "Blue";
colors[5] = "Orange";
colors[6] = "Silver";
colors[7] = "Yellow";
colors[8] = "Brown";
colors[9] = "Maroon";
manufacturers = new String[10];
manufacturers[0] = "Mercedes";
manufacturers[1] = "BMW";
manufacturers[2] = "Volvo";
manufacturers[3] = "Audi";
manufacturers[4] = "Renault";
manufacturers[5] = "Opel";
manufacturers[6] = "Volkswagen";
manufacturers[7] = "Chrysler";
manufacturers[8] = "Ferrari";
manufacturers[9] = "Ford";
}
public TableBean() {
populateRandomCars(cars, 50);
lazyModel = new LazyCarDataModel(cars);
}
public Car getSelectedCar() {
return selectedCar;
}
public void setSelectedCar(Car selectedCar) {
this.selectedCar = selectedCar;
}
public LazyDataModel<Car> getLazyModel() {
return lazyModel;
}
private void populateRandomCars(List<Car> list, int size) {
for (int i = 0; i < size; i++) {
list.add(new Car(getRandomModel(), getRandomYear(),
getRandomManufacturer(), getRandomColor()));
}
}
private String getRandomColor() {
return colors[(int) (Math.random() * 10)];
}
private String getRandomManufacturer() {
return manufacturers[(int) (Math.random() * 10)];
}
private int getRandomYear() {
return (int) (Math.random() * 50 + 1960);
}
private String getRandomModel() {
return UUID.randomUUID().toString().substring(0, 8);
}
}
LazyCarDataModel.java
package com.solartis.primefaces.sample;
import java.util.ArrayList;
/**
* Dummy implementation of LazyDataModel that uses a list to mimic a real
datasource like a database.
*/
public class LazyCarDataModel extends LazyDataModel<Car> {
private List<Car> datasource;
public LazyCarDataModel(List<Car> datasource) {
this.datasource = datasource;
}
#Override
public Car getRowData(String rowKey) {
for(Car car : datasource) {
if(car.getModel().equals(rowKey))
return car;
}
return null;
}
#Override
public void setRowIndex(int rowIndex) {
if (rowIndex == -1 || getPageSize() == 0) {
super.setRowIndex(-1);
} else
super.setRowIndex(rowIndex % getPageSize());
}
#Override
public Object getRowKey(Car car) {
return car.getModel();
}
#Override
public List<Car> load(int first, int pageSize,
List<SortMeta> multiSortMeta,Map<String, String> filters) {
System.out.println("\nTHE INPUT PARAMETER VALUE OF LOAD METHOD :
\t"+"first=" + first + ", pagesize=" + pageSize + ", multiSortMeta=" +
multiSortMeta + " filter:" + filters);
System.out.println("\nTHE MULTISORTMETA CONTENT : \t");
if (multiSortMeta != null) {
for (SortMeta sortMeta : multiSortMeta) {
System.out.println("SORTFIELD:" +sortMeta.getSortField());
System.out.println("SORTORDER:" +sortMeta.getSortOrder());
System.out.println("SORTFUNCTION:"
+sortMeta.getSortFunction());
System.out.println("COLUMN:" +sortMeta.getColumn());
System.out.println("CLASS:" +sortMeta.getClass());
}
}
List<Car> data = new ArrayList<Car>();
//filter
for(Car car : datasource) {
boolean match = true;
for(Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
String filterValue = filters.get(filterProperty);
String fieldValue = String.valueOf(car.getClass().
getField(filterProperty).get(car));
if(filterValue == null || fieldValue.startsWith(filterValue)) {
match = true;
}
else {
match = false;
break;
}
} catch(Exception e) {
match = false;
}
}
if(match) {
data.add(car);
}
}
//rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
//paginate
if(dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
}
catch(IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
}
else {
return data;
}
}
}
It works well except when I paginate with multiple columns sorting, the load() method with List<SortMeta> does not give me the column details which are currently sorted to carry over to the other page, unlike the load() method with String sortField, SortOrder sortOrder which gives those sorting details.
For example:
Click on the sorting arrow in "manufacturer" and then Ctrl+click on the sorting arrow of "year"
you would get the sorting column details to the load() method (I have printed the input parameters value inside load method).
Now, do pagination. Here the load() method fails to give the sorting columns detail
not only for pagination, if you enter column filter values after clicking on the sorting columns, the same problem exist
How can I fix this?
I solved this question in a temporary way...
Have a sessionscoped managed bean for storing the sorting column details, inorder to get within load() during pagination,
like:-
#ManagedBean
#SessionScoped
public class StoreSortColumnDetail implements Serializable{
/** holds multisort values**/
private List<SortMeta> mMultiSortMeta;
public List<SortMeta> getMultiSortMeta() {
return mMultiSortMeta;
}
public void setMultiSortMeta(List<SortMeta> multiSortMeta) {
mMultiSortMeta = multiSortMeta;
}
public void clearMultiSortMeta() {
if(this.mMultiSortMeta != null)
this.mMultiSortMeta.clear();
}
}
and use it in load() as like this:
#Override
public List<Car> load(int first, int pageSize,
List<SortMeta> multiSortMeta,Map<String, String> filters) {
/** Instance to the SessionScoped scoped StoreSortColumnDetail managed bean*/
#ManagedProperty(value="#{StoreSortColumnDetail }")
private StoreSortColumnDetail storeSortColumnDetail ;
public void setStoreSortColumnDetail (StoreSortColumnDetail sortColumnDetail ) {
this.storeSortColumnDetail = sortColumnDetail ;
}
/** to hold the handled sort column detail**/
List<SortMeta> handledMultiSortMeta = new ArrayList<SortMeta>();
/*Here starts the multisortmeta handling process*/
/** check for List<SortMeta> for null**/
if(multiSortMeta != null ) {
/** updates StoreSortColumnDetail's List<SortMeta> with Load()'s List<SortMeta>**/
storeSortColumnDetail.setMultiSortMeta(multiSortMeta);
handledMultiSortMeta = multiSortMeta;
}
/** check for List<SortMeta> for notnull **/
else if (multiSortMeta == null) {
/**assigns Load()'s List<SortMeta> with StoreSortColumnDetail's List<SortMeta>**/
handledMultiSortMeta = storeSortColumnDetail.getMultiSortMeta();
}
/*Now u have handled multisortmeta from load()...
and u can process now from handledMultiSortMeta*/
}
i hope u came to know how i handled, if not intimate me...
but this is a temporary way,need to handle it through primefaces way...
While this approach might do the trick, you can easily delegate the actual operation to Primefaces in your lazyDataModel. By doing that you keep your codebase clearer since you won't have any other classes to operate and you will reuse the already developed components(which you should anyhow).
If you examined the PrimeFaces source code you will see that DataTable uses concrete classes for each of its features such as filtering, sorting, expanding etc... For sorting Primefaces uses a class called BeanPropertyComparator this class requires certain properties to its constructor but most of these attributes can be get from sortMeta attribute sent to the load method of lazyDataModel. But if you want to get all the attributes you need to have the DataTable object which you can get from FacesContext if you know the client side id of the DataTable in question.
Suppose you have the datatable instance in a variable called activeTable than all you have to do is this:
UIColumn sortColumn = sortMeta.getSortColumn()
if(sortColumn.isDynamic())
((DynamicColumn)sortColumn).applyStatelessModel();
//for single mode sorting
ValueExpression sortByVal = activeTable.getValueExpression("sortBy");
//multiColumn sorting
ValueExpression sortByVal = sortColumn.getValueExpression("sortBy");
int caseSensitive = activeTable.isCaseSensitiveSort();
SortOrder order = sortMeta.getSortOrder();
MethodExpression exp = sortColumn.getSortFunction();
//pass required properties to constructor
//single mode sort
Collections.sort(filteredItems, new BeanPropertyComparator(......))
//for multi column sort use ChainedBeanPropertyComparator and add every new comparator to it than
Collections.sort(filteredItems, chainedComparator);
that way by default your sort will support any feature that primefaces default mechanism support without sacrificing safety.