JSF2 Paging / Pager for Repeater - jsf

Do you know this feeling when every code you write works immedietly and you underrun your schedule :-P It's like 'oh yeah now I have time to make it perfect'. That's where I am at the moment^^
So I implemented a repeater with JSF (ui:repeat) and I thought about a paging for all the entities. Is there maybe an easy way to do that? What are the points I have to think about?
Would be nice if someone gives me some help. My googleskills haven't helped me so far :-P
Cheers...

Here is a simple example that should give you the idea on how to implement this.
RepeatPaginator:
public class RepeatPaginator {
private static final int DEFAULT_RECORDS_NUMBER = 2;
private static final int DEFAULT_PAGE_INDEX = 1;
private int records;
private int recordsTotal;
private int pageIndex;
private int pages;
private List<?> origModel;
private List<?> model;
public RepeatPaginator(List<?> model) {
this.origModel = model;
this.records = DEFAULT_RECORDS_NUMBER;
this.pageIndex = DEFAULT_PAGE_INDEX;
this.recordsTotal = model.size();
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;
}
this.model = origModel.subList(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<?> getModel() {
return model;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
}
Bean:
public class TestBean {
private List<String> list;
private RepeatPaginator paginator;
#PostConstruct
public void init() {
this.list = new ArrayList<String>();
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");
paginator = new RepeatPaginator(this.list);
}
public RepeatPaginator getPaginator() {
return paginator;
}
}
XHTML:
<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"
template="/WEB-INF/template/default.xhtml">
<ui:define name="content">
<h:form>
<ui:repeat value="#{testBean.paginator.model}" var="listItem">
<div>
<h:outputText value="#{listItem}"/>
</div>
</ui:repeat>
<h:commandButton value="< prev" action="#{testBean.paginator.prev}"/>
<h:outputText value="#{testBean.paginator.pageIndex} / #{testBean.paginator.pages}"/>
<h:commandButton value="next >" action="#{testBean.paginator.next}"/>
<h:inputHidden value="#{testBean.paginator.pageIndex}"/>
</h:form>
</ui:define>
</ui:composition>

Pagination is in fact easy. You just have to keep passing one or two parameters around: firstrow and optionally rowcount (which can also be kept in the server side). When the enduser clicks Next, you just increment the value of firstrow with the value of rowcount. When the enduser clicks Back, you just decrement the value of firstrow with the value of rowcount. You only need to check if it doesn't exceed the borders of 0 and totalrows and alter accordingly.
Then, based on the desired firstrow and rowcount, you know exactly which data to display. If all the data is already in some List in Java's memory, then you just use List#subList() to obtain a sublist from it for display. It is however not efficient to duplicate the entire database table into Java's memory. It may not harm when it's only 100 rows, but when it's much more than that and/or you're duplicating it for every single user, then the application will run out of memory very soon.
In that case you'd rather like to paginate at DB level. In for example MySQL you can use LIMIT clause to obtain a subset of results from the DB. JPA/Hibernate even provides ways using setFirstResult() and setMaxResults() methods of Query and Criteria respectively. You can find examples in this and this answer.
You can find a basic JSF 1.2 targeted kickoff example of Google-like pagination (and sorting) in this article. It uses Tomahawk components, but in JSF 2.0 you can just leave them away by making the bean #ViewScoped (replaces t:saveState) and using ui:repeat (replaces t:dataList).
Last but not least, there are lot of component libraries which does all the works in a single component. For example RichFaces <rich:datascroller> and PrimeFaces <p:dataTable paginator="true"> (can also be done ajaxical).

Related

PrimeFaces LazyDataModel resolves rows incorrectly after live scroll?

I'm trying to use PrimeFaces 5.3 (with JSF 2.2) to implement a data table that contains items, and each item has a list of command buttons to perform server side actions. I use primefaces LazyDataModel because I have huge datasets and cannot afford to load all the items when the view loads. But after livescrolling happens, rows do not behave as expected.
I have setup a small project to illustrate my issue. (full sources at the end of the post)
The issue
When I press A0, "Executed A0" must be printed on the console (and so on).
Everything works fine for the first data set. Here's what happens when I press the first three buttons:
Fetching starting at 0 //expected result
Executed A0 //A0
Executed A1 //A1
Executed A2 //A2
But when I scroll, and 3 other items are loaded, the first three rows do not work correctly.
Here's what happens when I press all the buttons in order:
Fetching starting at 3 //expected result
Executed A3 //A0
Executed A4 //A1
Executed A5 //A2
Executed A3 //A3
Executed A4 //A4
Executed A5 //A5
The first three buttons execute the actions of the last three ones :(
A server-side issue
It is not a client-side issue, I have monitored the requests sent.
When I press A0, the request contains the following POST parameters:
j_idt8:resultsTable:0:j_idt12:0:j_idt13=j_idt8:resultsTable:0:j_idt12:0:j_idt13
javax.faces.source=j_idt8:resultsTable:0:j_idt12:0:j_idt13
And when I press A3:
j_idt8:resultsTable:3:j_idt12:0:j_idt13=j_idt8:resultsTable:3:j_idt12:0:j_idt13
javax.faces.source=j_idt8:resultsTable:3:j_idt12:0:j_idt13
The correct row is referenced after resultsTable:
Debug mode
I also messed with the debug mode. When I press A1, the UIData#brodacast(FacesEvent) is called with an event that contains a field rowIndex=1. So it works fine at this point.
Then, I don't really understand what happens. But at the end of the process, the target method is executed on the wrong instance...
Source code
Item.java
public class Item {
private int id;
private String name;
private List<ItemAction> actions;
public Item(int id, String name, List<ItemAction> actions) {
this.id = id;
this.name = name;
this.actions = actions;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public List<ItemAction> getActions() {
return actions;
}
}
ItemAction.java
public class ItemAction {
private String label;
public ItemAction(String label) {
this.label = label;
}
public void execute(){
System.out.println("Executed " + label);
}
public String getLabel() {
return label;
}
}
TheService.java
public class TheService {
private static final int ITEMS_COUNT = 8;
public int getItemsCount() {
return ITEMS_COUNT;
}
public List<Item> retrieveItems(int startIndex, int pageSize){
List<Item> result = new ArrayList<>(pageSize);
int endIndex = startIndex + pageSize;
if(endIndex > ITEMS_COUNT){
endIndex = ITEMS_COUNT;
}
for(int index=startIndex; index<endIndex; index++){
result.add(generateItem(index));
}
return result;
}
private Item generateItem(int id){
List<ItemAction> actions = new ArrayList<>(2);
actions.add(new ItemAction("A" + id));
actions.add(new ItemAction("B" + id));
return new Item(id, "Item " + id, actions);
}
}
DatatableLazyModel.java
#ManagedBean
#ViewScoped
public class DatatableLazyModel extends LazyDataModel<Item>{
private TheService service;
private List<Item> allResults;
public DatatableLazyModel() {
this.service = new TheService();
this.allResults = new ArrayList<>();
}
#Override
public int getRowCount() {
return service.getItemsCount();
}
#Override
public List<Item> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
System.out.println("Fetching starting at " + first);
List<Item> pageResults = service.retrieveItems(first, pageSize);
allResults.addAll(pageResults);
return pageResults;
}
}
index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition 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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui" template="template/ui.xhtml">
<ui:define name="body">
<h3>Datatablelazy load</h3>
<h:form>
<p:dataTable id="resultsTable" var="item"
value="#{datatableLazyModel}" liveScroll="true" scrollRows="3"
scrollHeight="100" scrollable="true" lazy="true">
<p:column headerText="Name">
<h:outputText value="#{item.name}" />
</p:column>
<p:column headerText="Actions">
<ui:repeat var="action" value="#{item.actions}">
<p:commandButton action="#{action.execute()}" value="#{action.label}">
</p:commandButton>
</ui:repeat>
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
pom.xml dependencies
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>5.3</version>
</dependency>
Questions
I have tried this with more items loaded. Each time, only methods on the last loaded set are executed. Even from old components.
So basically, the question is : "How can I make all the buttons work as expected?"
Why do methods do not get executed on the correct instance? How is the target instance resolved?
Thank you for your help :)
The issue is with the LazyDataModel implementation that is meant to be used with pages and not live scroll, especially this snippet :
if(rowIndex == -1 || pageSize == 0)
this.rowIndex = -1;
else
this.rowIndex = (rowIndex % pageSize);
I subclassed LazyDataModel with my own implementation to solve the problem.
LiveScrollLazyDataModel.java
public abstract class LiveScrollLazyModel<T> extends LazyDataModel<T> {
private int rowIndex;
#Override
public void setRowIndex(int rowIndex) {
int oldIndex = getRowIndex();
if (rowIndex == -1 || getPageSize() == 0){
super.setRowIndex(-1);
this.rowIndex = -1;
}else{
super.setRowIndex(rowIndex);
this.rowIndex = rowIndex;
}
if (getResults() == null) {
return;
}
DataModelListener[] listeners = getDataModelListeners();
if (listeners != null && oldIndex != this.rowIndex) {
Object rowData = null;
if (isRowAvailable()) {
rowData = getRowData();
}
DataModelEvent dataModelEvent = new DataModelEvent(this, rowIndex, rowData);
for (int i = 0; i < listeners.length; i++) {
listeners[i].rowSelected(dataModelEvent);
}
}
}
#Override
public T getRowData() {
return getResults().get(this.rowIndex);
}
#Override
public boolean isRowAvailable() {
if(getResults() == null) {
return false;
}
return rowIndex >= 0 && rowIndex < getResults().size();
}
/**
* The complete list of results
* #return
*/
public abstract List<T> getResults();
}
The implementor needs to implement the getResults() method that provides the complete list of results.

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();
}

Recommender system

I am trying to implement a mahout based recommender system. I am not able to display the results on a jsf page.
#ManagedBean(name="similarvaluerecommender")
#ViewScoped
public class SimilarValueRecommender implements Serializable {
private List recommendedItems;
#PostConstruct
public void init() {
this.recommendedItems = new ArrayList<>();
DataModel dm;
try {
dm = new FileDataModel(new File("Dataset/userdata.csv"));
//ItemSimilarity sim = new LogLikelihoodSimilarity(dm);
TanimotoCoefficientSimilarity sim = new TanimotoCoefficientSimilarity(dm);
GenericItemBasedRecommender recommender = new GenericItemBasedRecommender(dm, sim);
int x=1;
for(LongPrimitiveIterator items = dm.getItemIDs(); items.hasNext();) {
long itemId = items.nextLong();
List<RecommendedItem>recommendedItems1 = recommender.mostSimilarItems(itemId, 10);
this.recommendedItems.addAll(recommendedItems1);
x++;
}
} catch (TasteException ex) {
Logger.getLogger(SimilarValueRecommender.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex) {
Logger.getLogger(SimilarValueRecommender.class.getName()).log(Level.SEVERE, null, ex);
}
}
//getter and setter...
public List<RecommendedItem> getRecommendedItems(){
return recommendedItems;
}
public void setList(List<RecommendedItem> recommendedItems) {
this.recommendedItems=recommendedItems;
}
}
I would like to display the results of this page on a jsf page in the form of a table. This is the view
<ui:composition template="WEB-INF/commonlayout.xhtml">
<ui:define name="content">
<h:form>
hi
<h:dataTable id="similarvaluestable" value="#{similarvaluerecommender.recommendedItems}" var="recommendedItem">
<h:column>
#{recommendedItem.itemID}
</h:column>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
Do the following things:
Declare a List<RecommendedItem> recommendations attribute in your managed bean.
Define getter and setter methods for this attribute. Do not include any business logic in any of these methods.
Load the data in a #PostConstruct method and store it into recommendations attribute.
Display the data from recommendations attribute in the view.
In code:
#ManagedBean("similarValueRecommender")
#ViewScoped
public class SimilarValueRecommender implements Serializable {
List<RecommendedItem> recommendations;
#PostConstruct
public void init() {
this.recommendations = new ArrayList<RecommendedItem>();
//taken from your current code
//seems like this is the code to load recommendations
DataModel dm = new AlphaItemFileDataModel(new File("Dataset/userdata.csv"));
//ItemSimilarity sim = new LogLikelihoodSimilarity(dm);
TanimotoCoefficientSimilarity sim = new TanimotoCoefficientSimilarity(dm);
GenericItemBasedRecommender recommender = new GenericItemBasedRecommender(dm, sim);
int x=1;
for(LongPrimitiveIterator items = dm.getItemIDs(); items.hasNext();) {
long itemId = items.nextLong();
List<RecommendedItem>recommendations = recommender.mostSimilarItems(itemId, 10);
/*
for(RecommendedItem recommendation : recommendations) {
similaritemID=recommendation.getItemID();
System.out.println(itemId + "," + recommendation.getItemID() + "," + recommendation.getValue());
}
*/
this.recommendations.addAll(recommendations);
x++;
//if(x>10) System.exit(1);
}
}
//getter and setter...
}
In your view:
<h:dataTable value="#{similarValueRecommender.recommendations}" var="recommendation">
<h:column>
#{recommendation.text}
</h:column>
</h:dataTable>

commandButton actionListener not working on primeFaces CRUD Generator netbeans plugin nbpfcrudgen-0.15.2-7.3.1impl.nbm

I'm testing a primeFaces CRUD Generator on netbeans.
Web Site: PrimeFaces CRUD Generator for NetBeans beta
Is not working the actionListener when you push the save button of Create.xhtml page.
The same happens when you edit a row. There is nothing in the Glassfish error log, nothing on firebug too.
Tested on:
Plugin version: nbpfcrudgen-0.15.2-7.3.1impl.nbm
Ubuntu 12.04 64 bits
Netbeans NetBeans IDE 7.3
Primefaces 3.5
GlassFish Server Open Source Edition 4.0
Mozilla Firefox 22.0
MySql 5.6.11
You can watch the video of the test:
test video
Download source code:
PrimeFacesTestCRUD.zip
Database file
Inserts File
Button:
p:commandButton actionListener="#{countryController.saveNew}" value="#{myBundle.Save}" update="display,:CountryListForm:datalist,:growl" oncomplete="handleSubmit(xhr,status,args,CountryCreateDialog);"/>
Page Create.xhtml (go inside template.xhtml with the pages, Edit.xhtml List.xhtml View.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">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:composition>
<p:dialog id="CountryCreateDlg" widgetVar="CountryCreateDialog" modal="true" resizable="false" appendToBody="true" header="#{myBundle.CreateCountryTitle}">
<h:form id="CountryCreateForm">
<h:panelGroup id="display">
<p:panelGrid columns="2" rendered="#{countryController.selected != null}">
<p:outputLabel value="#{myBundle.CreateCountryLabel_name}" for="name" />
<p:inputText id="name" value="#{countryController.selected.name}" title="#{myBundle.CreateCountryTitle_name}" required="true" requiredMessage="#{myBundle.CreateCountryRequiredMessage_name}"/>
</p:panelGrid>
<p:commandButton actionListener="#{countryController.saveNew}" value="#{myBundle.Save}" update="display,:CountryListForm:datalist,:growl" oncomplete="handleSubmit(xhr,status,args,CountryCreateDialog);"/>
<p:commandButton value="#{myBundle.Cancel}" onclick="CountryCreateDialog.hide()"/>
</h:panelGroup>
</h:form>
</p:dialog>
</ui:composition>
</html>
Entity:
#Entity
#Table(name = "country")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = Country.FIND_ALL, query = "SELECT c FROM Country c"),
#NamedQuery(name = Country.FIND_BY_ID, query = "SELECT c FROM Country c WHERE c.id = :id"),
#NamedQuery(name = Country.FIND_BY_NAME, query = "SELECT c FROM Country c WHERE c.name = :name")})
public class Country implements Serializable, Comparable<Country> {
private static final long serialVersionUID = 1L;
public static final String FIND_ALL = "Country.findAll";
public static final String FIND_BY_ID = "Country.findById";
public static final String FIND_BY_NAME = "Country.findBySubtag";
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "seqCountry")
#TableGenerator(name = "seqCountry", initialValue = 10000)
#Basic(optional = false)
#Column(name = "id", unique = true, updatable = false)
private Long id;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "name", unique = true, updatable = false)
private String name;
...
AbstractController:
import org.primefaces.test.crud.bean.AbstractFacade;
import org.primefaces.test.crud.controller.util.JsfUtil;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.event.ActionEvent;
import java.util.ResourceBundle;
import javax.ejb.EJBException;
/**
* Represents an abstract shell of to be used as JSF Controller to be used in
* AJAX-enabled applications. No outcomes will be generated from its methods
* since handling is designed to be done inside one page.
*/
public abstract class AbstractController<T> {
private AbstractFacade<T> ejbFacade;
private Class<T> itemClass;
private T selected;
private List<T> items;
private enum PersistAction {
CREATE,
DELETE,
UPDATE
}
public AbstractController() {
}
public AbstractController(Class<T> itemClass) {
this.itemClass = itemClass;
}
protected AbstractFacade<T> getFacade() {
return ejbFacade;
}
protected void setFacade(AbstractFacade<T> ejbFacade) {
this.ejbFacade = ejbFacade;
}
public T getSelected() {
return selected;
}
public void setSelected(T selected) {
this.selected = selected;
}
protected void setEmbeddableKeys() {
// Nothing to do if entity does not have any embeddable key.
}
;
protected void initializeEmbeddableKey() {
// Nothing to do if entity does not have any embeddable key.
}
/**
* Returns all items in a List object
*
* #return
*/
public List<T> getItems() {
if (items == null) {
items = this.ejbFacade.findAll();
}
return items;
}
public void save(ActionEvent event) {
String msg = ResourceBundle.getBundle("/MyBundle").getString(itemClass.getSimpleName() + "Updated");
persist(PersistAction.UPDATE, msg);
}
public void saveNew(ActionEvent event) {
String msg = ResourceBundle.getBundle("/MyBundle").getString(itemClass.getSimpleName() + "Created");
persist(PersistAction.CREATE, msg);
if (!isValidationFailed()) {
items = null; // Invalidate list of items to trigger re-query.
}
}
public void delete(ActionEvent event) {
String msg = ResourceBundle.getBundle("/MyBundle").getString(itemClass.getSimpleName() + "Deleted");
persist(PersistAction.DELETE, msg);
if (!isValidationFailed()) {
selected = null; // Remove selection
items = null; // Invalidate list of items to trigger re-query.
}
}
private void persist(PersistAction persistAction, String successMessage) {
if (selected != null) {
this.setEmbeddableKeys();
try {
if (persistAction != PersistAction.DELETE) {
this.ejbFacade.edit(selected);
} else {
this.ejbFacade.remove(selected);
}
JsfUtil.addSuccessMessage(successMessage);
} catch (EJBException ex) {
String msg = "";
Throwable cause = JsfUtil.getRootCause(ex.getCause());
if (cause != null) {
msg = cause.getLocalizedMessage();
}
if (msg.length() > 0) {
JsfUtil.addErrorMessage(msg);
} else {
JsfUtil.addErrorMessage(ex, ResourceBundle.getBundle("/MyBundle").getString("PersistenceErrorOccured"));
}
} catch (Exception ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
JsfUtil.addErrorMessage(ex, ResourceBundle.getBundle("/MyBundle").getString("PersistenceErrorOccured"));
}
}
}
/**
* Creates a new instance of an underlying entity and assigns it to Selected
* property.
*
* #return
*/
public T prepareCreate(ActionEvent event) {
T newItem;
try {
newItem = itemClass.newInstance();
this.selected = newItem;
initializeEmbeddableKey();
return newItem;
} catch (InstantiationException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public boolean isValidationFailed() {
return JsfUtil.isValidationFailed();
}
}
https://sourceforge.net/p/nbpfcrudgen/tickets/14/
This seems to be caused by changes in JSF from 2.1 to 2.2 which is now the default for Glassfish 4. Registering and using a downloaded JSF 2.1 version in a new project with Glassfish 4 will not work. So please consider the CRUD Generator broken with Glassfish 4 and JSF 2.2. You may register a previous version Glassfish (3.1.2 is tested to work).
Temporary solution:
Only works classic option: (Tested on)
GlassFish 4 + jsf 2.2 + primefaces-4.0-20130711.071416-4.jar -> OK, except sortBy and filterBy
GlassFish 4 + jsf 2.2 + PrimeFaces 3.5 -> OK
GlassFish 3.1.2 + jsf 2.1 + PrimeFaces 3.5 -> OK
GlassFish 3.1.2 + jsf 2.1 + primefaces-4.0-20130711.071416-4.jar -> OK, except sortBy and filterBy
With primefaces 3.x:
sortBy="#{item.id}" filterBy="#{item.id}"
with primefaces 4.x: we can replace this on template
sortBy="id" filterBy="id"
nbpfcrudgen-0.15.2-src_javier_21_aug_2013.zip
Well Primefaces 3.5 works fine with NetBeans 7.3 and GlassFish 3.x, but you have to get the netbeans plugin version nbpfcrudgen-0.15.2-7.3impl.nbm instead of nbpfcrudgen-0.15.2-7.3.1impl.nbm.
However I got them working together, hope changing the plugin version and getting the appropriate one solves your problem.

How to Dynamically add a row in a table in JSF?

In my application i need to add a row on a click of a button and this button will be in all the rows. Need help to do this?
Item Class
public class Item {
public Item()
{
}
private String value;
public Item(String value) { this.value = value; }
public void setValue(String value) { this.value = value; }
public String getValue() { return value; }
}
Manage Bean Class
public class MyMB
{
private List<Item> list;
public void addItem() { // JSF action method
list.add(new Item("Default"));
Iterator<Item> iterator = list.iterator();
while(iterator.hasNext())
{
Item item = (Item)iterator.next();
System.out.println(item.getValue());
}
System.out.println();
}
/**
* #return the list
*/
public List<Item> getList() {
if(list==null)
{
loadList();
}
return list;
}
private void loadList() {
list = new ArrayList<Item>();
list.add(new Item("Data"));
}
}
JSF code
<h:form>
<rich:dataTable value="#{myMB.list}" var="item" id="tabel">
<h:column><h:inputText value="#{item.value}" /></h:column>
<h:column><a4j:commandButton value="Add" actionListener="#{myMB.addItem}" reRender="tabel"/></h:column>
All you need to do is basically indeed just adding an empty object to the datamodel behind the value attribute of h:dataTable.
But the same empty row needs to be preserved in the subsequent request as well. If the backing bean is request scoped, then the datamodel get reloaded without the empty row. This all should work when the bean is session scoped.
Further there are several errors in your JSF code. The h:dataTable var attribute is missing and the column content needs to be inside a h:column.
<h:form>
<h:dataTable value="#{bean.list}" var="item">
<h:column><h:inputText value="#{item.value}" /></h:column>
</h:dataTable>
<h:commandButton value="Add" action="#{bean.add}"/>
</h:form>
A session or view scoped bean can look like this:
public class Bean {
private List<Item> list;
public Bean() {
list = new ArrayList<Item>();
}
public void add() {
list.add(new Item());
}
public List<Item> getList() {
return list;
}
}
The Item class should of course have a default no-arg constructor. Normally this is already implicitly available, but if you define your own constructor with arguments, then it is not available anymore. You'll need to explicitly define it, otherwise you cannot do Item item = new Item(); anymore.
public class Item {
private String value;
public Item() {
// Keep default constructor alive.
}
public Item(String value) {
this.value = value;
}
// ...
}
If you prefer to keep the bean in the request scope, then you'll need to maintain the amount of newly added items, so that the bean can preserve the same amount on load.
public class Bean {
private List<Item> list;
private HtmlInputHidden count = new HtmlInputHidden();
public Bean() {
count.setValue(0);
}
public void add() {
list.add(new Item());
}
public List<Item> getList() {
if (list == null) loadList();
return list;
}
public HtmlInputHidden getCount() {
return count;
}
public void setCount(HtmlInputHidden count) {
this.count = count;
}
private void loadList() {
list = new ArrayList<Item>();
// Preserve list with newly added items.
for (int i = 0; i < (Integer) count.getValue(); i++) {
list.add(new Item());
}
}
}
You'll only need to add the following to the <h:form> of the JSF page:
<h:inputHidden binding="#{bean.count}" converter="javax.faces.Integer" />
For more insights about using datatables in any way you may find this article useful: Using Datatables. It also contains a WAR file with lot of examples in both request and session scope.
Take this table as an example:
<h:datatable value="#{myBean.list}" ...>
...
<h:column>
<h:commandButton value="Add a row" action="#{myBean.addRow}"/>
</h:column>
</h:datatable>
The method myBean.addRow will simply add a new element in your list:
public class MyBean {
private List<SomeClass> list;
...
public List<SomeClass> getList() {
return list;
}
public void addRow() {
list.add(new SomeClass());
}
}
When you will click on the button, the method addRow will add a new element in the list. The page will refresh and display the table with a new row.
Edit:
Regarding your post edition, three things:
Point 1: Could you please attach the stacktrace of your error?
Point 2: Your method addRow return a String which is an ID used by JSF for the navigation. As this action does not involve any navigation (i.e. the user stay on the same page), simply return null or "":
public String addRow() {
list.add(new Item("new data"));
return null;
}
Point 3: I suggest that your class Item provide an empty constructor (in addition of your current constructor):
public Item() {
}

Resources