Lazy Loading + Command Button on Primefaces [duplicate] - jsf

This question already has answers here:
Primefaces DataTable, lazy loading and CommandButton per row
(2 answers)
Closed 5 years ago.
I'm newbie on JSF and I'm working with primefaces-2.2.RC2.
I have a commandButton and when I click I want that it fill a dataTable that use lazyLoading (my work is based on primefaces showcase).
When I click on commandButton, the data is not loaded.
Here piece of my code:
<p:commandButton value="Search"
action="#{tableBean.search()}"
update="carList"
/>
<p:dataTable id="carList"
var="car"
value="#{tableBean.lazyDataModel}"
paginator="true"
rows="10"
dynamic="true"
lazy="true"
paginatorPosition="bottom"
>
Ande on my bean:
#ViewScoped
public class TableBean {
LazyDataModel<Car> lazyDataModel;
public TableBean() {
lazyDataModel = new LazyDataModel<Car>() {
#Override
public List<Car> load(int i, int i1, String string, boolean bln, Map<String, String> map) {
return new ArrayList<Car>();
}
};
}
public LazyDataModel<Car> getLazyDataModel() {
return lazyDataModel;
}
public void search() {
lazyDataModel = new LazyDataModel<Car>() {
#Override
public List<Car> load(int first, int pageSize, String sortField,boolean sortOrder, Map<String, String> filters) {
return fetchLazyData(first, pageSize);
}
#Override
public void setRowIndex(int rowIndex) {
setPageSize(10);
if (rowIndex == -1 || getPageSize() == 0) {
super.setRowIndex(-1);
} else {
super.setRowIndex(rowIndex % getPageSize());
}
}
public List<Car> fetchLazyData(int first, int pageSize) {
System.out.println("Loading the lazy car data between " + first + " and " + (first + pageSize));
List<Car> lazyCars = new ArrayList<Car>();
for (int i = 0; i < pageSize; i++) {
int offset = i + first;
lazyCars.add(new Car("Model_" + offset, (int) (Math.random() * 60 + 1960), "Brand_" + offset, "Color_" + offset));
}
return lazyCars;
}
};
lazyDataModel.setRowCount(10000);
}
}
update: I descovered that I have to bind my DataTable and on my bean I have to call loadLazyData() function.

I descovered that I have to bind my DataTable with my bean and on my bean I have to call loadLazyData() function to inform to table to load it.

Related

Rowkey is null when celledit with LazyDataModel

I am using PrimeFaces 6.1. I have implemented LazyDataModel and I am able to load the rows, owever, when I update a cell, the method onCellEdit is called but event.rowKey is always null.
<p:dataTable emptyMessage="Nessun record trovato."
style="margin-top: 50px" id="cars" var="produit"
value="#{gecomparBean.lazyModel}"
editable="true" editMode="cell" widgetVar="carsTable"
filteredValue="#{gecomparBean.filteredCars}"
rows="10"
lazy="true"
scrollRows="10"
scrollable="true" liveScroll="true" scrollHeight="250"
resizableColumns="true" resizeMode="expand"
draggableColumns="true"
rowStyleClass="#{produit.id == gecomparBean.selectedObject.id ? 'selectedRow' : null}"
>
<p:ajax event="colReorder"
listener="#{gecomparBean.onColumnReorder}" />
<p:ajax event="cellEdit" listener="#{gecomparBean.onCellEdit}"
oncomplete="updateTable()" update=":form:remote" />
Managed Bean: [Session scoped]
wrapper.setSession(activeSession.getId());
lazyModel = new LazyProductDataModel(wrapper);
LazyProductDataModel
public class LazyProductDataModel extends LazyDataModel<ProductSessionDTO> implements Serializable {
private List<ProductSessionDTO> products;
private ProductSearchWrapper wrapper;
public LazyProductDataModel(ProductSearchWrapper wrapp) throws RestRequestException {
super();
wrapper = wrapp;
}
#Override
public ProductSessionDTO getRowData(String rowKey) {
for (ProductSessionDTO car : getProducts()) {
if (car.getId() == (Long.parseLong(rowKey))) {
return car;
}
}
return null;
}
#Override
public Object getRowKey(ProductSessionDTO car) {
return car.getId();
}
#Override
public List<ProductSessionDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
List<ProductSessionDTO> data = new ArrayList<ProductSessionDTO>();
try {
System.out.println("First " + first + " Max " + pageSize);
wrapper.setFirst(first);
wrapper.setPageSize(pageSize);
products=ServiceUtils.getInstance().getProducts(wrapper);
if (getProducts() != null) {
Comparator<ProductSessionDTO> lengthComparator = new Comparator<ProductSessionDTO>() {
#Override
public int compare(ProductSessionDTO o1, ProductSessionDTO o2) {
return Long.compare(o1.getId(), o2.getId());
}
};
Collections.sort(products, lengthComparator);
}
} catch (RestRequestException ex) {
Logger.getLogger(LazyProductDataModel.class.getName()).log(Level.SEVERE, null, ex);
}
data = products;
//rowCount
int dataSize = data.size();
this.setRowCount(1000);
//paginate
if (dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
} catch (IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
} else {
return data;
}
}
}
Note: I found a post which mentioned that we need to remove the rowkey in order for the getRowKey() to get called automatically - so I have removed that attribute.

How to do pagination in JSF 2

I am very new to JSF and was trying to implement a pagination using <ui:repeat>. I am using the #ViewScoped but currently my state is not saved. Every time the backing bean resets the page information and the next page is not displayed. Attaching the source code for reference.
WorkItemList.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!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:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core"
template="/WEB-INF/template/default.xhtml">
<ui:define name="content">
<h:form id="form">
<ui:repeat value="#{repeatPaginator.model}" var="listItem">
<div>
<h:outputText value="#{listItem}"/>
</div>
</ui:repeat>
<h:commandButton value="< prev" action="#{repeatPaginator.prev}"/>
<h:outputText value="#{repeatPaginator.pageIndex} / #{repeatPaginator.pages}"/>
<h:commandButton value="next >" action="#{repeatPaginator.next}"/>
<h:inputHidden value="#{repeatPaginator.pageIndex}"/>
</h:form>
</ui:define>
</ui:composition>
Backing Bean - RepeatPaginator.java
package test;
#ManagedBean
#ViewScoped
public class RepeatPaginator implements Serializable{
private static final long serialVersionUID = 1L;
private static final int DEFAULT_RECORDS_NUMBER = 2;
private static final int DEFAULT_PAGE_INDEX = 1;
private TestDelegate delegate;
private int records;
private int recordsTotal;
private int pageIndex;
private int pages;
private List<String> model;
public RepeatPaginator() {
delegate = new TestDelegate();
this.records = DEFAULT_RECORDS_NUMBER;
this.pageIndex = DEFAULT_PAGE_INDEX;
// Get Model
this.model = delegate.fetchCurrentList(getFirst(), getFirst()+records);
this.recordsTotal = delegate.getListSize();
if (records > 0) {
pages = records <= 0 ? 1 : recordsTotal / records;
if (recordsTotal % records > 0) {
pages++;
}
if (pages == 0) {
pages = 1;
}
} else {
records = 1;
pages = 1;
}
updateModel();
}
public void updateModel() {
int fromIndex = getFirst();
int toIndex = getFirst() + records;
if(toIndex > this.recordsTotal) {
toIndex = this.recordsTotal;
}
setModel(delegate.fetchCurrentList(fromIndex, toIndex));
}
public void next() {
if(this.pageIndex < pages) {
this.pageIndex++;
}
updateModel();
}
public void prev() {
if(this.pageIndex > 1) {
this.pageIndex--;
}
updateModel();
}
public int getRecords() {
return records;
}
public int getRecordsTotal() {
return recordsTotal;
}
public int getPageIndex() {
return pageIndex;
}
public int getPages() {
return pages;
}
public int getFirst() {
return (pageIndex * records) - records;
}
public List<String> getModel() {
if(model==null)
updateModel();
return model;
}
public void setModel(List<String> model) {
this.model = model;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
}
Delegate - TestDelegate.java
#ViewScoped
public class TestDelegate {
private List<String> list = new ArrayList<String>();
public TestDelegate()
{
this.list.add("Item 1");
this.list.add("Item 2");
this.list.add("Item 3");
this.list.add("Item 4");
this.list.add("Item 5");
this.list.add("Item 6");
this.list.add("Item 7");
this.list.add("Item 8");
this.list.add("Item 9");
this.list.add("Item 10");
this.list.add("Item 11");
}
public List<String> fetchCurrentList(int from, int to)
{
return list.subList(from, to);
}
public int getListSize()
{
return this.list.size();
}
}
The #ViewScoped bean is not "magically present all the time". At the end of the request, it's values will be serialized and the bean is destructed.
This causes a Re-Construct of the bean at the beginning of the next request (click on next). Your Container will take care to deserialize the stored values again, in order to return the actual State of the bean. BUT: This happens after Bean-Construction, meaning when you try to use the values within the Constructor of the bean, nothing is restored.
Move your logic into a method that you annotate with #PostConstruct - then all values will be loaded and you can fetch the correct result. The container will call the method annotated with #PostConstruct once it restored the prior state of the bean.
This becomes important when you start to use ManagedProperties or CDI-Injections.
public RepeatPaginator() {
//no serialized data available here, Good location to initialize some stuff
//that is independent.
delegate = new TestDelegate();
this.records = DEFAULT_RECORDS_NUMBER;
this.pageIndex = DEFAULT_PAGE_INDEX;
}
#PostConstruct
public void init() {
//Here the ViewScoped values are restored. We can start to use them.
// Get Model
this.model = delegate.fetchCurrentList(getFirst(), getFirst()+records);
this.recordsTotal = delegate.getListSize();
if (records > 0) {
pages = records <= 0 ? 1 : recordsTotal / records;
if (recordsTotal % records > 0) {
pages++;
}
if (pages == 0) {
pages = 1;
}
} else {
records = 1;
pages = 1;
}
updateModel();
}

Catching PrimeFaces's spinner value inside repeat structure

I'm using Primefaces and spinner component. My problem is that the spinner value is not set in the bean, if it's inside an iteration structure. My spinner is inside ui:repeat.
In the end, the problem is how to deal with different form controls mapping to the same property in bean.
<h:form>
<ui:repeat var="item" value="#{myBean.items}">
<p:spinner size="2" min="1" max="50" style="width:75px" value="#{cartBean.quantityToOrder}"/>
<p:commandButton value="Add to cart" action="#{cartBean.saveItemToCart(item)}" ajax="false"/>
</ui:repeat>
</h:form>
and my bean
#ManagedBean
#SessionScoped
public class CartBean extends BaseBean {
private int quantityToOrder;
//setter, getter...
//When called quantityToOrder = 0 always
public void saveItemToOrder(Item item) {
quantityToOrder IS 0.
}
}
I suspect it has to do with form submission, I have tried a form enclosing all elements in the collection and also a form enclosing any of the spinners + button. The generated client IDs are distinct for all spinners.
Any help would be appreciated.
Put a System.out.println("quantity: " + quantityToOrder) on your setQuantityToOrder(int quantityToOrder) method and will will see the problem. The value of the last spinner will prevail over the others because all the spinners are pointed to the same property (cartBean.quantityToOrder).
Try moving the quantityToOrder to the Item as follows:
<h:form id="mainForm">
<ui:repeat value="#{cartBean.items}" var="item">
<p:outputLabel value="#{item.name}: " for="sp" />
<p:spinner id="sp" size="2" min="1" max="50" style="width:75px" value="#{item.quantityToOrder}" />
<p:commandButton value="Add to cart" action="#{cartBean.saveItemToOrder(item)}" process="#this, sp" update=":mainForm:total" />
<br />
</ui:repeat>
Total: <h:outputText id="total" value="#{cartBean.quantityToOrder}" />
</h:form>
The cartBean:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class CartBean implements Serializable {
private List<CartItem> items;
private int quantityToOrder;
#PostConstruct
public void setup() {
items = new ArrayList<CartItem>();
items.add(new CartItem(1, "A"));
items.add(new CartItem(2, "B"));
items.add(new CartItem(3, "C"));
}
public void saveItemToOrder(CartItem item) {
//do whatever you want to do with the item quantity.
System.out.println("Qtd of " + item.getName() + ": " + item.getQuantityToOrder());
//to calculte the qtd of items on the cart.
quantityToOrder = 0;
for (CartItem cartItem : items) {
quantityToOrder += cartItem.getQuantityToOrder();
}
}
public List<CartItem> getItems() {
return items;
}
public void setItems(List<CartItem> items) {
this.items = items;
}
public int getQuantityToOrder() {
return quantityToOrder;
}
public void setQuantityToOrder(int quantityToOrder) {
this.quantityToOrder = quantityToOrder;
}
}
The CartItem:
import java.io.Serializable;
public class CartItem implements Serializable {
private Integer id;
private Integer quantityToOrder;
private String name;
public CartItem(Integer id, String name) {
this.id = id;
this.name = name;
quantityToOrder = 0;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getQuantityToOrder() {
return quantityToOrder;
}
public void setQuantityToOrder(Integer quantityToOrder) {
this.quantityToOrder = quantityToOrder;
}
#Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + (this.id != null ? this.id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CartItem other = (CartItem) obj;
if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
return false;
}
return true;
}
}

Dynamic Textfield In JSF 2.0

hey I am using the following code to create the number of text fields as the user wants
<h:form>
<p>Number Of News <h:inputText value="#{news.noOfFields}" /></p>
<ui:repeat value="#{news.values}" var="item">
<hr/>
News #{item.no}
<h:inputText value="#{item.news}" /><br/>
</ui:repeat>
<hr/>
<h:commandButton styleClass="btn btn-blue" action="#{news.submit}" value="Save" />
</h:form>
The managed bean news has a class News as
#ManagedBean
#SessionScoped
public class News
{
private String noOfFields;
private List<NewsVO> values;
public News()
{
this.values = new ArrayList<NewsVO>();
}
public String submit() {
for(NewsVO newsVO : this.values)
{
System.out.println(newsVO.getNews());
System.out.println(newsVO.getNo());
}
return null;
// save values in database
}
public String getNoOfFields() {
return noOfFields;
}
public List<NewsVO> getValues() {
return values;
}
public void setValues(List<NewsVO> values) {
this.values = values;
}
public void setNoOfFields(String noOfFields) {
this.values = new ArrayList<NewsVO>();
try {
for(int i=0;i<Integer.valueOf(noOfFields);i++)
{
NewsVO newsVO = new NewsVO();
newsVO.setNo(i+1);
this.values.add(newsVO);
}
this.noOfFields = noOfFields;
}
catch(NumberFormatException ex) {
/*values = new String[1];*/
noOfFields = "1";
}
}
}
The NewsVO is just a javaBean class as follows
public class NewsVO
{
public int no;
public String news;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getNews() {
return news;
}
public void setNews(String news) {
this.news = news;
}
}
The problem is the values inside the input Text doesn't get reflected on pressing the save button. It gives me null, even though, I have written something inside all the textfields.
<h:inputText value="#{item.news}" />
Everytime you push the submit button, all setters in the bean are called (including setNoOfFields()). In this setter you are resetting your list, that's why you loose your values. Since you only need to modify your list if there is a size change, here is a simple way doing it :
#ManagedBean
#SessionScoped
public class News
{
private int noOfFields;
private List<NewsVO> values;
public News()
{
this.values = new ArrayList<NewsVO>();
}
public String submit()
{
for(NewsVO newsVO : this.values)
{
System.out.println(newsVO.getNews());
System.out.println(newsVO.getNo());
}
return null;
// save values in database
}
public int getNoOfFields()
{
return noOfFields;
}
public List<NewsVO> getValues()
{
return values;
}
public void setValues(List<NewsVO> values)
{
this.values = values;
}
public void setNoOfFields(int noOfFields)
{
if(noOfFields < this.noOfFields)
{
for(int i = this.noOfFields - 1;i >= noOfFields;i--)
{
getValues().remove(i);
}
}
else if(noOfFields > this.noOfFields)
{
for(int i = this.noOfFields;i < noOfFields;i++)
{
NewsVO newsVO = new NewsVO();
newsVO.setNo(i+1);
getValues().add(newsVO);
}
}
}
}
Note : I've also changed your noOfFields getter/setter for simple int, JSF will do the conversion for you.

Primefaces picklist. Setter for DualListModel removes source and target

I am having a problem with my picklist. Showing the source and targets works, but when the commandbutton is pressed it calles the setter of the picklist and empties the source and target of the picklist. After that it calls the method connectTags().
What is the reason of this behavior and how could I solve this?
<h:form>
<p:pickList id="pickList" value="#{employeeEditController.dualListTags}"
var="tag" itemLabel="#{tag.value}" itemValue="#{tag}">
<f:facet name="sourceCaption">Available</f:facet>
<f:facet name="targetCaption">Connected</f:facet>
</p:pickList>
<p:commandButton value="Save" actionListener="#{employeeEditController.connectTag()}"/>
</h:form>
Bean:
#Named(value = "employeeEditController")
#SessionScoped
public class EmployeeEditController implements Serializable, IEditController {
private Employee selectedEmployee;
private DualListModel<Tags> dualListTags;
#Inject
private EmployeeFacade employeeFacade;
#Inject
private CompanyFacade companyFacade;
#Inject
private TagFacade tagFacade;
public void setSelectedEmployee(Employee selectedEmployee) {
System.out.println("setSelectedEmployee called. Value: " + selectedEmployee.getFirstName());
this.selectedEmployee = selectedEmployee;
this.refreshDualList();
}
private void refreshDualList(){
List<Tags> source = (List<Tags>) this.getCompanyTags();
List<Tags> target = (List<Tags>) this.getTags();
System.out.println("refreshDualList called. \nSource: " + source.size() +
"Target: " + target.size());
this.dualListTags = new DualListModel<Tags>(source, target);
}
public Collection<Tags> getTags(){
Collection<Tags> retVal;
if(this.selectedEmployee != null){
retVal = this.selectedEmployee.getTags();
if(retVal == null){
retVal = new ArrayList<Tags>();
}
} else{
System.out.println("selected emp is null");
retVal = new ArrayList<Tags>();
}
return retVal;
}
public Collection<Tags> getCompanyTags() {
Collection<Tags> retVal = new ArrayList<Tags>();
if(this.companyID == null){
String userstr = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
this.companyID = this.companyFacade.getCompanyIDByUser(userstr);
}
retVal = this.tagFacade.getTagsFromCompanyID(companyID);
return retVal;
}
public void save(){
if(this.selectedEmployee != null){
System.out.println("Save called. Value: " + this.selectedEmployee.getFirstName());
this.employeeFacade.edit(this.selectedEmployee);
}
}
public void connectTag() {
if(this.dualListTags != null){
System.out.println("connectTag() called.\n" + "source: " + this.dualListTags.getSource().size() +
"\ntarget: " + this.dualListTags.getTarget().size());
this.selectedEmployee.setTags((Collection<Tags>)this.dualListTags.getTarget());
this.save();
}
}
public DualListModel<Tags> getDualListTags() {
System.out.println("getDualList called. \nSource :" + this.dualListTags.getSource().size()
+ "\nTargets: " + this.dualListTags.getTarget().size());
return this.dualListTags;
}
public void setDualListTags(DualListModel<Tags> dualListTags) {
System.out.println("setDualList called. \nSource :" + this.dualListTags.getSource().size()
+ "\nTargets: " + this.dualListTags.getTarget().size());
this.dualListTags = dualListTags;
System.out.println("setDualList called after set. \nSource :" + this.dualListTags.getSource().size()
+ "\nTargets: " + this.dualListTags.getTarget().size());
}
}
I had the same problem and it seems that the solution is to impement a custom converter for the entity you use in picklist.
This is how my custom converter looks like:
#ViewScoped
#ManagedBean(name = "vehicleGroupConverter")
public class VehicleGroupConverter implements Converter {
#ManagedProperty(value = "#{vehiclesBean}")
private VehiclesBean vehiclesBean;
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
long groupId = Long.parseLong(value);
List<AssetCategory> groups = vehiclesBean.getVehicleGroups();
for (AssetCategory group : groups) {
if (group.getCategoryId() == groupId) {
return group;
}
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
return Long.toString(((AssetCategory) value).getCategoryId());
}
public void setVehiclesBean(VehiclesBean vehiclesBean) {
this.vehiclesBean = vehiclesBean;
}
}
And this is the picklist template part:
<p:pickList id="vehicleGroups"
value="#{vehiclesBean.selectedVehicleGroups}"
var="group"
itemLabel="#{group.name}"
itemValue="#{group}"
converter="#{vehicleGroupConverter}"
styleClass="pf-panel-grid"
style="margin-bottom:10px">
<f:facet name="sourceCaption">#{i18n['label-available-groups']}</f:facet>
<f:facet name="targetCaption">#{i18n['label-assigned-groups']}</f:facet>
</p:pickList>
Sets duallistmodel when source or target not empty:
public void setDualListTags(DualListModel<Tags> dualListTags) {
if(this.dualListTags.getSource().size()!=0 && this.dualListTags.getTarget().size()!=0) {
this.dualListTags = dualListTags;
}
}

Resources