I'm attempting to implement lazy loading into my application with sorting, but am having little issues with sorting and paginator. SortBy does work so like Paginator single but when i click on sort on first column with page > 0, the LazyDataModel#load is called with "first" parameter = 0!
Then instead of sorting data of page 1 return on page 0
Here is my managed bean class code:
#ViewScoped
#ManagedBean
public class PlayerMB implements Serializable {
private static final long serialVersionUID = 1L;
private LazyDataModel<Player> players = null;
private int sizePlayer;
private Player player;
public LazyDataModel<Player> getAllPlayers() {
if (players == null) {
players = new PlayerLazyList();
}
setSizePlayer(players.getRowCount());
return players;
}
public Player getPlayer() {
if(player == null){
player = new Player();
}
return player;
}
public void setPlayer(Player player) {
this.player = player;
}
public int getSizePlayer()
{
return sizePlayer;
}
public void setSizePlayer(int sizePlayer) {
this.sizePlayer = sizePlayer;
}
}
Player class code:
public class Player implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
#Override
public int hashCode() {
return getId();
}
#Override
public boolean equals(Object obj) {
if(obj instanceof Player){
Player player = (Player) obj;
return player.getId() == getId();
}
return false;
}
/*public int sortByModel(Object player1, Object player2)
{
return ((Player) player1).getName().compareTo(((Player) player2).getName());
}*/
}
dataTable.xhtml:
<f:view>
<h:form>
<p:dataTable id="lazyDataTable"
value="#{playerMB.allPlayers}"
var="player" paginator="true"
rows="10"
selection="#{playerMB.player}"
selectionMode="single"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15"
style="width: 80%;margin-left: 10%;
margin-right: 10%;"
lazy="true">
<f:facet name="header">List of players</f:facet>
<p:ajax event="rowSelect" update=":playerDialogForm" oncomplete="PF('playerDetails').show();" />
<p:ajax event="page"
listener="#{playerMB.casePage}"/>
<p:column sortBy="#{player.name}" headerText="Name" > <!-- sortBy does not work with lazyDataTable but more work occurs-->
<h:outputText value="#{player.name}" />
</p:column>
<p:column sortBy="#{player.age}" headerText="Age">
<h:outputText value="#{player.age}" />
</p:column>
<f:facet name="footer">tot: #{playerMB.sizePlayer}</f:facet>
</p:dataTable>
</h:form>
PlayerLazyList class code who extends LazyDataModel
public class PlayerLazyList extends LazyDataModel<Player> {
//private int pagina;
#Override
public List<Player> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
List<Player> allplayers = CreatePlayers.players; //here load all Player
System.out.println("first =" + first + "; pagesize =" + pageSize + "; sortField = " + sortField);
players = CreatePlayers.getPlayers(first, pageSize,allplayers.size()); //here load player from "first" for "max" element
if (sortField != null && sortField.equals("name") && sortOrder.name().equals("ASCENDING"))
Collections.sort(players, new NamePlayerComparatorAsc());
if (sortField != null && sortField.equals("name") && sortOrder.name().equals("DESCENDING"))
Collections.sort(players, new NamePlayerComparatorDesc());
System.out.println("size dopo" + players.size());
// set the total of players
if(getRowCount() <= 0){
//setRowCount(playerDAO.countPlayersTotal());
setRowCount(allplayers.size());
}
// set the page dize
setPageSize(pageSize);
return players;
}
private static final long serialVersionUID = 1L;
private List<Player> players;
//private MyTransaction transaction;
#Override
public Object getRowKey(Player player) {
return player.getId();
}
#Override
public Player getRowData(String playerId) {
Integer id = Integer.valueOf(playerId);
for (Player player : players) {
if(id.equals(player.getId())){
return player;
}
}
return null;
}
}
Primefaces 5.2, Wildfly 8.2.0, Mojarra 2.2.8
You seem to expect it sorts just the page you are on. That is just not how it works. It works perfectly and as designed and as expected by (almost) everybody (that is everybody but you). It sorts the full possible resultset. All items that were on page 0 will now most likely be spread over several other pages. So staying on the newly populalted page 1 totally does not make any sense.
The behaviour you describe can be seen in the showcase to iirc
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;
}
}
I have to turn my dataTable to lazy dataTable. How can I do it? Here is my sample code:
IndexBean.java
#Name("indexBean")
public class IndexBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
UserService userService;
private List<User> userList;
#Inject
MessageService messageService;
private List<Message> messageList;
private Integer dataTableRendered;
public void getAllUser() {
setDataTableRendered(1);
userList = new ArrayList<User>();
userList = userService.getAllUser();
if (userList.size() > 0) {
addMessageSuccess("Success", "Success");
} else {
addMessageError("Failure", "Failure!");
}
}
public void getAllMessages() {
setDataTableRendered(2);
messageList = new ArrayList<Message>();
messageList = messageService.getAllMessages();
if (messageList.size() > 0) {
addMessageSuccess("Success", "Success");
} else {
addMessageError("Failure", "Failure!");
}
}
public void addMessageSuccess(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
public void addMessageError(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public List<Message> getMessageList() {
return messageList;
}
public void setMessageList(List<Message> messageList) {
this.messageList = messageList;
}
public Integer getDataTableRendered() {
return dataTableRendered;
}
public void setDataTableRendered(Integer dataTableRendered) {
this.dataTableRendered = dataTableRendered;
}
}
index.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<h:head></h:head>
<p:layout fullPage="true">
<!-- Status Waiting Dialog -->
<p:ajaxStatus onstart="PF('statusDialog').show()"
onsuccess="PF('statusDialog').hide()" />
<p:dialog widgetVar="statusDialog" modal="true" draggable="false"
closable="false" resizable="false" showHeader="true"
header="Please wait...">
<p:graphicImage value="/images/loader.gif"
style=" float: left;position: relative;left: 30%;" />
</p:dialog>
<!-- Left Layout -->
<p:layoutUnit position="west" id="west">
<p:growl autoUpdate="true" id="infoMessage" showDetail="true"
showSummary="true" sticky="false" />
<p:commandButton value="Get Users"
actionListener="#{indexBean.getAllUser}"
update="infoMessage,centerForm,centerForm:userTable" />
<p:commandButton value="Get Messages"
actionListener="#{indexBean.getAllMessages}"
update="infoMessage,centerForm,centerForm:messageRaporTable" />
</p:layoutUnit>
<!-- Center Layout -->
<p:layoutUnit position="center" id="center">
<h:form id="centerForm">
<!-- User dataTable -->
<p:dataTable id="userTable" var="user"
value="#{indexBean.userList }" paginator="true" rows="10"
lazy="true" rendered="#{indexBean.dataTableRendered ==1}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15,20,25,50,100">
<p:column headerText="userid" sortBy="#{user.userId}"
filterBy="#{user.userId}">
<h:outputText value="#{user.userId}"></h:outputText>
</p:column>
<p:column headerText="username" sortBy="#{user.userName}"
filterBy="#{user.userName}">
<h:outputText value="#{user.userName}"></h:outputText>
</p:column>
<p:column headerText="firstname" sortBy="#{user.userFirstName}"
filterBy="#{user.userFirstName}">
<h:outputText value="#{user.userFirstName}"></h:outputText>
</p:column>
</p:dataTable>
<p:column headerText="Message Id"
filterBy="#{message.messageId}"
sortBy="#{message.messageId}">
<h:outputText value="#{message.messageId}" />
</p:column>
<p:column headerText="Sender"
sortBy="#{message.messageSender}" filterBy="#{message.messageSender}">
<h:outputText value="#{message.messageSender}" />
</p:column>
<p:column headerText="Receiver"
sortBy="#{message.messageReceiver}"
filterBy="#{message.messageReceiver}">
<h:outputText value="#{message.messageReceiver}" />
</p:column>
<p:column headerText="Subject"
sortBy="#{message.messageSubject}"
filterBy="#{message.messageSubject}">
<h:outputText value="#{message.messageSubject}" />
</p:column>
<p:column headerText="Message"
sortBy="#{message.messageText}"
filterBy="#{message.messageText}">
<h:outputText value="#{message.messageText}" />
</p:column>
</p:dataTable>
</h:form>
</p:layoutUnit>
</p:layout>
UserService.java
public interface UserService {
public List<User> getAllUser();
}
UserServiceImpl.java
#Service("userService")
#Component
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public List<User> getAllUser() {
return userDAO.getAllUser();
}
}
UserDAO.java
public interface UserDAO {
public List<User> getAllUser();
}
UserDAOImpl.java - Actually this sql more complicated.
#Component
#Repository("userDAO")
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#SuppressWarnings("unchecked")
#Override
public List<User> getAllUser() {
List<User> userList = new ArrayList<User>();
String sql = "select * from SECRETDB.USER";
try {
Query query = getSession().createSQLQuery(sql).addScalar("userOID", StringType.INSTANCE)
.addScalar("userName", StringType.INSTANCE).addScalar("firstName", StringType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(User.class)).setCacheable(false)
.setCacheMode(CacheMode.IGNORE);
userList = query.list();
getSession().clear();
} catch (HibernateException e) {
getSession().clear();
e.printStackTrace();
}
return userList;
}
User.java
public class User {
private String userOID;
private String userName;
private String firstName;
public String getUserOID() {
return userOID;
}
public void setUserOID(String userOID) {
this.userOID = userOID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
HibernateDaoSupport.java
public abstract class HibernateDaoSupport {
#Autowired
private SessionFactory sessionFactory;
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
}
In your datatable in index.xhtml i see
value="#{indexBean.userList }" paginator="true" rows="10"
lazy="true"
where userList is pointing to
private List<User> userList;
This is wrong. It should be
private LazyUserDataModel<User> userList
Please read showcase and userguide pages 167-168 to implement LazyDataModel. Nobody here is going to teach you to implement it. An example of a generic lazydatamodel using hibernate and JPA is below.
public class GenericLazyDataModel<T> extends LazyDataModel<T> {
#Transient
protected static Logger debugLogger = Logger.getLogger("DebugLogger");
protected EntityManager entityManager;
protected String hqlQuery;
private String countQuery;
protected List<SortFieldInfoDTO> defaultSortFields;
protected List<T> currentObjectList;
protected List<Object> parameterValues;
protected List<String> parameterName;
protected List<Object> filterFieldValues;
protected List<String> filterFieldName;
public GenericLazyDataModel(EntityManager entityManager, String hqlQuery,
String countQuery, List<SortFieldInfoDTO> sortFieldInfos,
List<Object> parameterValues, List<String> parameterName) {
super();
this.entityManager = entityManager;
this.hqlQuery = hqlQuery;
this.countQuery = countQuery;
this.defaultSortFields = sortFieldInfos;
this.parameterValues = parameterValues;
this.parameterName = parameterName;
this.setRowCount(calculateRecordCount());
}
#Override
public List<T> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
String sqlQuery = hqlQuery;
int filterCount = 0;
if(sortOrder==null){
sortOrder=SortOrder.ASCENDING;
}
if (filterFieldValues != null && filterFieldName != null) {
filterFieldValues.clear();
filterFieldName.clear();
}
for (Iterator<String> iterator = filters.keySet().iterator(); iterator
.hasNext();) {
String filterField = iterator.next();
if (debugLogger.isInfoEnabled()) {
debugLogger.info("Filter Field Name " + filterField);
}
// Check if the HQL Query already has a where clause
if (sqlQuery.toLowerCase().indexOf("where") != -1) {
sqlQuery = sqlQuery + " and " + filterField
+ " like :filterFieldValue" + filterCount;
} else {
sqlQuery = sqlQuery + " where " + filterField
+ " like :filterFieldValue" + filterCount;
}
if (filterFieldValues == null) {
filterFieldValues = new ArrayList<Object>();
}
if (filterFieldName == null) {
filterFieldName = new ArrayList<String>();
}
filterFieldValues.add(((String)filters.get(filterField)).toUpperCase() + "%");
filterFieldName.add("filterFieldValue" + filterCount);
filterCount++;
}
//************************
boolean isOrderFieldFound=Boolean.FALSE;
if(sortField !=null && !sortField.isEmpty()){
isOrderFieldFound=Boolean.TRUE;
sqlQuery = sqlQuery + " order by " + sortField+(sortOrder==SortOrder.ASCENDING?" ASC ":" DESC ");
}
if(isOrderFieldFound){
if(defaultSortFields !=null && !defaultSortFields.isEmpty()){
for(SortFieldInfoDTO fld:defaultSortFields){
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery=sqlQuery+" , "+fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
}
}
}else{
if(defaultSortFields !=null && !defaultSortFields.isEmpty()){
int index=0;
for(SortFieldInfoDTO fld:defaultSortFields){
if(index==0){
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery = sqlQuery + " order by " + fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
index++;
}else{
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery=sqlQuery+" , "+fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
}
}
}
}
//*********************************
if (debugLogger.isInfoEnabled())debugLogger.info("sqlQuery:"+sqlQuery);
Query query = entityManager.createQuery(sqlQuery);
int parameterCount = 0;
if (parameterValues != null) {
for (Iterator<Object> iterator = parameterValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(parameterName.get(parameterCount), type);
parameterCount++;
}
}
int parameterCount1 = 0;
if (filterFieldValues != null && !filterFieldValues.equals("")) {
for (Iterator<Object> iterator = filterFieldValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(filterFieldName.get(parameterCount1), type);
parameterCount1++;
}
}
if (debugLogger.isInfoEnabled())debugLogger.info("first page value " + first);
if (debugLogger.isInfoEnabled())debugLogger.info("pageSize page value " + pageSize);
query.setFirstResult(first);
query.setMaxResults(pageSize);
currentObjectList = query.getResultList();
return currentObjectList;
}
private int calculateRecordCount() {
Query query = entityManager.createQuery(countQuery);
int parameterCount = 0;
if (parameterValues != null) {
for (Iterator<Object> iterator = parameterValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(parameterName.get(parameterCount), type);
parameterCount++;
}
}
if (filterFieldValues != null) {
for (Iterator<Object> iterator = filterFieldValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(filterFieldName.get(parameterCount), type);
parameterCount++;
}
}
Long rowCount = (Long) query.getSingleResult();
if (debugLogger.isInfoEnabled()) {
debugLogger.info("rowCount Application Data Model" + rowCount);
}
return rowCount.intValue();
}
public List<T> getCurrentObjectList() {
return currentObjectList;
}
}
Using this and learning on your own figure this out and good luck.
this is how I am doing it.
public class ImagePaginator extends LazyDataModel<PhotoObject> {
private ISession session;
private int startId;
private int endId;
private User user;
private static final long serialVersionUID = 1L;
private SearchController searchController;
public ImagePaginator(User user, SearchController searchController) {
this.session = user.getSession();
this.user = user;
this.searchController = searchController;
}
#Override
public List<PhotoObject> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
// collect paginated data from server
Collection<IObject> photos = searchController.getResultAccount().getPaginatedObjects(first, first + 3,
SystemConstants.PHOTO);
List<PhotoObject> data = new ArrayList<PhotoObject>();
for (IObject resultPhoto : photos) {
IPhoto photo = (IPhoto) resultPhoto;
data.add(new PhotoObject(photo));
}
this.setRowCount(data.size() + 1);
return data;
}
#Override
public Object getRowKey(PhotoObject photoObject) {
return photoObject.getId();
}
public ISession getSession() {
return session;
}
public void setSession(ISession session) {
this.session = session;
}
public int getStartId() {
return startId;
}
public void setStartId(int startId) {
this.startId = startId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public SearchController getSearchController() {
return searchController;
}
public void setSearchController(SearchController searchController) {
this.searchController = searchController;
}
public int getEndId() {
return endId;
}
public void setEndId(int endId) {
this.endId = endId;
}
just like you I use a service to retrieve the results. The sql that I am sending (Depending in the situation) is:
public List<IEntity> retrievePaginatedById(String classType, String targetId, String status, int id, int offset,
int limit, boolean forContactRequestNotification) {
SessionFactory sessionFactory = createSessionFactory();
Session session = sessionFactory.openSession();
String hql = null;
if (forContactRequestNotification) {
hql = "FROM " + classType + " D WHERE " + targetId + "=" + "'" + id + "' AND D.status = '" + status
+ "' ORDER BY D.id DESC";
} else {
hql = "FROM " + classType + " D WHERE " + targetId + "=" + "'" + id + "'" + " ORDER BY D.id DESC";
}
Query query = session.createQuery(hql).setFirstResult(offset).setMaxResults(limit);
#SuppressWarnings("unchecked")
List<IEntity> results = query.list();
session.close();
sessionFactory.close();
return results;
}
There is a <p:selectOneListbox> with about 20 items. The first 5 items can be viewed in the list and then the remaining can be scrolled to and selected. The list is ajaxified. When the item numbers greater than 5 is selected (after scrolling to it) the scrollbar does not remain at its place; it moves to the top position. This makes the selected item (for example item 9) invisible. The program uses a ajax listener. The app uses PrimeFaces 5.0.
But, when the ajax listener is removed the selected (item 9) is visible and the scroller does not move to top (remove the p:ajax tag's listener attribute in the below JSF page).
I would like to know how to make the scrollbar not move when any item is selected while using the ajax listener.
The JSF page:
<p:selectOneListbox id="list"
scrollHeight="100"
value="#{bean.todo}">
<f:selectItems value="#{bean.data}"
var="t"
itemLabel="#{t.name}"
itemValue="#{t.name}"/>
<p:ajax process="#this"
update="#this msg"
listener="#{bean.valueChanged}" />
</p:selectOneListbox>
<br /><h:outputText id="msg" value="#{bean.message}" />
The bean's code:
import javax.faces.bean.*;
import java.io.Serializable;
import java.util.*;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.component.UIOutput;
#ManagedBean(name="bean")
#SessionScoped
public class TodosBean implements Serializable {
private List<Todo> data;
private String todo; // selected item value
private String msg;
public TodosBean() {
loadData();
if (data.size() == 0) {
return;
}
Todo t = data.get(0);
String name = t.getName();
setTodo(name); // select the first item in the list
setMessage(name);
}
private void loadData() {
data = new ArrayList<>();
data.add(new Todo("1first", "1"));
data.add(new Todo("2second", "2"));
data.add(new Todo("3third", "3"));
data.add(new Todo("4fourth", "4"));
data.add(new Todo("5fifth", "5"));
data.add(new Todo("6sixth", "6"));
data.add(new Todo("7seventh", "7"));
data.add(new Todo("8eighth", "8"));
}
public List<Todo> getData() {
return data;
}
public void setMessage(String msg) {
this.msg = msg;
}
public String getMessage() {
return msg;
}
public String getTodo() {
return todo;
}
public void setTodo(String t) {
todo = t;
}
public void valueChanged(AjaxBehaviorEvent e) {
String name = (String) ((UIOutput) e.getSource()).getValue();
setMessage(name + " selected.");
}
}
public class Todo {
private String name;
private String desc;
public Todo() {}
public Todo(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
#Override
public String toString() {
return this.name;
}
}
Remove the update="#this will solve it. Updating a control will move it to a default state. It might also be that in a newer version of PF this default state is improved and it still is at the correct position
This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 7 years ago.
I use SelectOneMenu, first i choose location, next building in this location then zone in building and subZone in this zone. First two select are good but when i take building choose there is no zones to show and i have error: j_idt31:selectedBld: Validation Error: Value is not valid. I don't know why;/
this is xhtml:
<h:form>
<p:panelGrid columns="2" position="center top">
<h:outputLabel for="selectedLoca" value="#{loc['location']}" />
<p:selectOneMenu value="#{caretakerCautionBean.location}" id="selectedLoca" converter="#{locationConverter}">
<f:selectItem itemLabel="" itemValue="#{null}" />
<f:selectItems value="#{caretakerCautionBean.locationList}" var="loca" itemLabel="#{loca.name}" itemValue="#{loca}"/>
<p:ajax update="selectedBld :messages" event="change" process="selectedLoca" listener="#{caretakerCautionBean.locationChanged}" />
</p:selectOneMenu>
<h:outputLabel for="selectedBld" value="#{loc['building']}" />
<p:selectOneMenu value="#{caretakerCautionBean.building}" id="selectedBld" converter="#{buildingConverter}">
<f:selectItem itemLabel="" itemValue="#{null}" />
<f:selectItems value="#{caretakerCautionBean.buildingList}" var="bld" itemLabel="#{bld.name}" itemValue="#{bld}" />
<p:ajax update="selectedZone :messages" event="change" process="selectedLoca selectedBld" listener="#{caretakerCautionBean.buildingChanged}" />
</p:selectOneMenu>
<h:outputLabel for="selectedZone" value="#{loc['zone']}" />
<p:selectOneMenu value="#{caretakerCautionBean.zone}" id="selectedZone" converter="#{zoneConverter}">
<f:selectItem itemLabel="" itemValue="#{null}" />
<f:selectItems value="#{caretakerCautionBean.zoneList}" var="zone" itemLabel="#{zone.name}" itemValue="#{zone}" />
<p:ajax update="selectedSubZone :messages" event="change" process="selectedLoca selectedBld selectedZone" listener="#{caretakerCautionBean.zoneChanged}" />
</p:selectOneMenu>
<h:outputLabel for="selectedSubZone" value="#{loc['cleaningPlanner.tableHeader.subzone']}" />
<p:selectOneMenu value="#{caretakerCautionBean.subZone}" id="selectedSubZone" converter="#{subZoneConverter}">
<f:selectItem itemLabel="" itemValue="#{null}" />
<f:selectItems value="#{caretakerCautionBean.subZoneList}" var="subZone" itemLabel="#{subZone.name}" itemValue="#{subZone}" />
<p:ajax update=":messages" event="change" process="selectedLoca selectedBld selectedZone selectedSubZone" listener="#{caretakerCautionBean.subZoneChanged}" />
</p:selectOneMenu>
<f:facet name="footer">
<p:commandButton process="#form" value="#{loc['addComment']}" actionListener="#{caretakerCautionBean.addNewPosition()}" update=":messages" oncomplete="caretakerCautionsDialog.hide()"/>
</f:facet>
</p:panelGrid>
</h:form>
Bean:
#ManagedBean
public class CaretakerCautionBean {
#ManagedProperty(value="#{caretakerCautionRepository}")
private CaretakerCautionRepository caretakerCautionRepo;
#ManagedProperty(value="#{locationRepository}")
private LocationRepository locationRepo;
#ManagedProperty(value = "#{buildingRepository}")
private BuildingRepository buildingRepo;
#ManagedProperty(value="#{zoneRepository}")
private ZoneRepository zoneRepo;
#ManagedProperty(value="#{subZoneRepository}")
private SubZoneRepository subZoneRepo;
#ManagedProperty (value="#{userRepository}")
private UserRepository userRepo;
#ManagedProperty(value="#{userService}")
private UserService userSvc;
private List<CaretakerCaution> caretakerCautionList;
private List<Location> locationList;
private List<User> userList;
private List<SubZone> subZoneList;
private List<Building> buildingList;
private List<Zone> zoneList;
private Location location;
private SubZone subZone;
private Building building;
private Zone zone;
private User user;
private String description;
private String comment;
private String actionsTaken;
private int id;
private transient Logger logger = Logger.getLogger("aplikacjaLogger");
public void locationChanged(AjaxBehaviorEvent ev) {
if(location !=null)
logger.log(Level.INFO, "locationChanged: "+location.getName());
buildingList = null;
getBuildingList();
}
public void buildingChanged(AjaxBehaviorEvent ev) {
if(building != null)
logger.log(Level.INFO, "buildingChanged: "+building.getName());
zoneList = null;
getZoneList();
}
public void zoneChanged(AjaxBehaviorEvent ev) {
if(zone != null)
logger.log(Level.INFO, "zoneChanged: "+zone.getName());
subZoneList = null;
getSubZoneList();
}
public void subZoneChanged(AjaxBehaviorEvent ev) {
if(subZone != null)
logger.log(Level.INFO, "subZoneChanged: "+subZone.getName());
}
public void addNewPosition() {
CaretakerCaution cc = new CaretakerCaution();
cc.setLocation(location);
cc.setBuilding(building);
cc.setZone(zone);
cc.setSubZone(subZone);
getCaretakerCautionRepo().save(cc);
logger.log(Level.INFO, "Dodano uwage.");
}
public BuildingRepository getBuildingRepo() {
return buildingRepo;
}
public void setBuildingRepo(BuildingRepository buildingRepo) {
this.buildingRepo = buildingRepo;
}
public ZoneRepository getZoneRepo() {
return zoneRepo;
}
public void setZoneRepo(ZoneRepository zoneRepo) {
this.zoneRepo = zoneRepo;
}
public UserRepository getUserRepo() {
return userRepo;
}
public void setUserRepo(UserRepository userRepo) {
this.userRepo = userRepo;
}
public Building getBuilding() {
return building;
}
public void setBuilding(Building building) {
this.building = building;
}
public Zone getZone() {
return zone;
}
public void setZone(Zone zone) {
this.zone = zone;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public List<Location> getLocationList() {
if(locationList == null)
locationList = getUserSvc().getLoggedIn().getCaretakenLocations();
return locationList;
}
public void setLocationList(List<Location> locationList) {
this.locationList = locationList;
}
public List<CaretakerCaution> getCaretakerCautionList() {
return caretakerCautionList;
}
public void setCaretakerCautionList(List<CaretakerCaution> caretakerCautionList) {
this.caretakerCautionList = caretakerCautionList;
}
public SubZoneRepository getSubZoneRepo() {
return subZoneRepo;
}
public void setSubZoneRepo(SubZoneRepository subZoneRepo) {
this.subZoneRepo = subZoneRepo;
}
public List<SubZone> getSubZoneList() {
if(subZoneList == null && zone != null)
subZoneList = getSubZoneRepo().findByZone(zone);
return subZoneList;
}
public void setSubZoneList(List<SubZone> subZoneList) {
this.subZoneList = subZoneList;
}
public SubZone getSubZone() {
return subZone;
}
public void setSubZone(SubZone subZone) {
this.subZone = subZone;
}
public LocationRepository getLocationRepo() {
return locationRepo;
}
public void setLocationRepo(LocationRepository locationRepo) {
this.locationRepo = locationRepo;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public List<Building> getBuildingList() {
if(buildingList == null && location != null)
buildingList = getBuildingRepo().findByLocation(location);
return buildingList;
}
public void setBuildingList(List<Building> buildingList) {
this.buildingList = buildingList;
}
public List<Zone> getZoneList() {
if(zoneList == null && building != null)
zoneList = getZoneRepo().findByBuilding(building);
return zoneList;
}
public void setZoneList(List<Zone> zoneList) {
this.zoneList = zoneList;
}
public CaretakerCautionRepository getCaretakerCautionRepo() {
return caretakerCautionRepo;
}
public void setCaretakerCautionRepo(
CaretakerCautionRepository caretakerCautionRepo) {
this.caretakerCautionRepo = caretakerCautionRepo;
}
public UserService getUserSvc() {
return userSvc;
}
public void setUserSvc(UserService userSvc) {
this.userSvc = userSvc;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getActionsTaken() {
return actionsTaken;
}
public void setActionsTaken(String actionsTaken) {
this.actionsTaken = actionsTaken;
}
}
Converter:
#Override
public Object getAsObject(FacesContext fc, UIComponent uiComp, String stringVal) {
if(stringVal == null || stringVal.trim().equals("")) return null;
else {
Integer userId = Integer.parseInt(stringVal);
for(Zone zon: this.getZonRepo().findAll())
if(zon.getId() == userId) {
return zon;
}
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uiComp, Object obj) {
if(obj == null || obj.equals("")) {
return "";
} else {
try {
return String.valueOf(((Zone)obj).getId());
} catch(ClassCastException cce) {
return "";
}
}
}
public ZoneRepository getZonRepo() {
return zonRepo;
}
public void setZonRepo(ZoneRepository zonRepo) {
this.zonRepo = zonRepo;
}
}
You have not specified the bean scope, so by default it is RequestScoped. Currently you are doing AJAX requests without execute="" attibutes, the default is #this so the bean is recreated only with part of the data.
In your case you need to keep values between actions so you will need to use ViewScoped or SessionScoped.
I suggest you to use ViewScoped like this :
#ManagedBean
#ViewScoped
public class CaretakerCautionBean {
// ...
}
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.