javax.servlet.ServletException: class org.richfaces.component.html.HtmlDatascroller (id="j_id_jsp_1731599958_11pc5") did not find parent form.
javax.faces.webapp.FacesServlet.service(FacesServlet.java:249)
上面异常的原因是:datatable 和 dataScrollor一定要包含在form中
RichFaces中利用DataTable和dataScroller(基于request页不是session啊)完美分页解决方案
DataModel是一个抽象类,用于封装各种类型的数据源和数据对象的访问,JSF中dataTable中绑定的数据实际上被包装成了一个DataModel,以消除各种不同数据源和数据类型的复杂性,我们访问数据库并拿到了一个List,交给dataTable,这时候,JSF会将这个List包装成ListDataModel,dataTable访问数据都是通过这个DataModel进行的,而不是直接使用List。
接下来我们要将需要的页的数据封装到一个DataPage中去,这个类表示了我们需要的一页的数据,里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,startRow表示该页的起始行在数据库中所有记录集中的位置。
共用到三个类:PageListDataModel,PageListBaseBean,DataPage
其中faces-config.xml中受管Bean配置如下:
<managed-bean>
<managed-bean-name>test</managed-bean-name>
<managed-bean-class>com.oa.bean.Test</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
代码如下:
public class DataPage {
/** *//**
* 将需要的页的数据封装到一个DataPage中去, 这个类表示了我们需要的一页的数据,<br>
* 里面包含有三个元素:datasetSize,startRow,和一个用于表示具体数据的List。<br>
* datasetSize表示了这个记录集的总条数,查询数据的时候,使用同样的条件取count即可,<br>
* startRow表示该页的起始行在数据库中所有记录集中的位置
*/
private int datasetSize;
private int startRow;
private List data;
/** *//**
*
* @param datasetSize
* 数据集大小
* @param startRow
* 起始行
* @param data
* 数据list
*/
public DataPage(int datasetSize, int startRow, List data)
{
this.datasetSize = datasetSize;
this.startRow = startRow;
this.data = data;
}
/** *//**
*
* @return
*/
public int getDatasetSize()
{
return datasetSize;
}
public int getStartRow()
{
return startRow;
}
/** *//**
*
* @return 已填充好的数据集
*/
public List getData()
{
return data;
}
}
public abstract class PageListBaseBean {
/** *//**
* 当前页码,跟dataSroller的page属性绑定
*/
protected int scrollerPage = 1;
/** *//**
* 当前页面大小
*/
protected int pageSize = 5;
/** *//**
* 默认数据模型,如果你有多个数据集需要分页,请自定义PagedListDataModel和相应的getDataModel方法
*/
protected PagedListDataModel defaultDataModel;
public int getScrollerPage()
{
return scrollerPage;
}
public void setScrollerPage(int scrollerPage)
{
this.scrollerPage = scrollerPage;
}
public int getPageSize()
{
return pageSize;
}
public void setPageSize(int pageSize)
{
this.pageSize = pageSize;
}
public abstract PagedListDataModel getDefaultDataModel();
}
实现类如下:
public class Test extends PageListBaseBean {
public PagedListDataModel getDefaultDataModel()
{
if (defaultDataModel == null ) {
defaultDataModel = new PagedListDataModel(pageSize)
{
public DataPage fetchPage(int startRow, int pageSize)
{
// call enclosing managed bean method to fetch the data
PrivilegeDAO dao = new PrivilegeDAO();
String hql = "from PrivilegeGroup order by sort desc";
System.out.println("fetchPage is calling!");
List list = dao.getOnePageList(hql, pageSize,startRow);
int i=dao.getRows("select count(*) from PrivilegeGroup");
return new DataPage(i, startRow, list) ;
}
};
}
return defaultDataModel;
}
}
xhtml中使用如下(注意红色部份,要保持分页Bea状态):
<a4j:form>
<h:panelGroup id="del">
<rich:dataTable id="r"
onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
onRowMouseOut="this.style.backgroundColor='#ffffff'"
cellpadding="0" cellspacing="0"
width="700" border="0" var="pg" value="#{test.defaultDataModel}"
rows="#{test.pageSize}">
<f:facet name="header">
<rich:columnGroup>
<rich:column >
<h:outputText value="id" />
</rich:column>
<rich:column >
<h:outputText value="名称" />
</rich:column>
<rich:column>
<h:outputText value="排序" />
</rich:column>
<rich:column>
<h:outputText value="操作" />
</rich:column>
</rich:columnGroup>
</f:facet>
<rich:column>
<h:outputText value="#{pg.id}"></h:outputText>
</rich:column>
<rich:column>
<h:outputText value="#{pg.name}"></h:outputText>
</rich:column>
<rich:column>
<h:outputText value="#{pg.sort}"></h:outputText>
</rich:column>
<rich:column align="center">
<a4j:commandLink action="#{privilegeGroupBean.editPrivilegeGroup}" value="修改" immediate="true" reRender="out" ajaxSingle="true">
<f:param name="id" value="#{pg.id}"/>
</a4j:commandLink>|
<a4j:commandLink action="#{privilegeGroupBean.delPrivilegeGroup}" value="删除" reRender="del" onclick="if(!confirm('是否要真的删除?此操作不可恢复')){return false;}">
<f:param name="id" value="#{pg.id}"/>
</a4j:commandLink>#{test.scrollerPage}
</rich:column>
<f:facet name="footer">
<rich:columnGroup>
<rich:column>
<h:outputText value="#{report.expReport.totalMeals}"><f:convertNumber pattern="$####.00" /></h:outputText>
</rich:column>
<rich:column>
<h:outputText value="#{report.expReport.totalHotels}"><f:convertNumber pattern="$####.00" /></h:outputText>
</rich:column>
<rich:column>
<h:outputText value="#{report.expReport.totalTransport}"><f:convertNumber pattern="$####.00" /></h:outputText>
</rich:column>
<rich:column>
<h:outputText value="#{report.expReport.grandTotal}"><f:convertNumber pattern="$####.00" /></h:outputText>
</rich:column>
</rich:columnGroup>
</f:facet>
</rich:dataTable>
<rich:datascroller align="left" for="r" maxPages="20"
id="sc2" page="#{test.scrollerPage}"/>
</h:panelGroup>
<a4j:keepAlive beanName="test"/>
</a4j:form>
public abstract class PagedListDataModel extends DataModel {
int pageSize;
int rowIndex;
DataPage page;
/** *//**
* Create a datamodel that pages through the data showing the specified
* number of rows on each page.
*/
public PagedListDataModel(int pageSize) {
super();
this.pageSize = pageSize;
this.rowIndex = -1;
this.page = null;
}
/** *//**
* Not used in this class; data is fetched via a callback to the fetchData
* method rather than by explicitly assigning a list.
*/
public void setWrappedData(Object o) {
if (o instanceof DataPage) {
this.page = (DataPage) o;
} else {
throw new UnsupportedOperationException(" setWrappedData ");
}
}
public int getRowIndex() {
return rowIndex;
}
/** *//**
* Specify what the "current row" within the dataset is. Note that the
* UIData component will repeatedly call this method followed by getRowData
* to obtain the objects to render in the table.
*/
public void setRowIndex(int index) {
rowIndex = index;
}
/** *//**
* Return the total number of rows of data available (not just the number of
* rows in the current page!).
*/
public int getRowCount() {
return getPage().getDatasetSize();
}
/** *//**
* Return a DataPage object; if one is not currently available then fetch
* one. Note that this doesn't ensure that the datapage returned includes
* the current rowIndex row; see getRowData.
*/
private DataPage getPage() {
if (page != null) {
return page;
}
int rowIndex = getRowIndex();
int startRow = rowIndex;
if (rowIndex == -1) {
// even when no row is selected, we still need a page
// object so that we know the amount of data available.
startRow = 0;
} // invoke method on enclosing class
page = fetchPage(startRow, pageSize);
return page;
}
/** *//**
* Return the object corresponding to the current rowIndex. If the DataPage
* object currently cached doesn't include that index then fetchPage is
* called to retrieve the appropriate page.
*/
public Object getRowData() {
if (rowIndex < 0) {
throw new IllegalArgumentException(
" Invalid rowIndex for PagedListDataModel; not within page ");
} // ensure page exists; if rowIndex is beyond dataset size, then
// we should still get back a DataPage object with the dataset size
// in it
if (page == null) {
page = fetchPage(rowIndex, pageSize);
}
int datasetSize = page.getDatasetSize();
int startRow = page.getStartRow();
int nRows = page.getData().size();
int endRow = startRow + nRows;
if (rowIndex >= datasetSize) {
throw new IllegalArgumentException(" Invalid rowIndex ");
}
if (rowIndex < startRow) {
page = fetchPage(rowIndex, pageSize);
startRow = page.getStartRow();
} else if (rowIndex >= endRow) {
page = fetchPage(rowIndex, pageSize);
startRow = page.getStartRow();
}
return page.getData().get(rowIndex - startRow);
}
public Object getWrappedData() {
return page.getData();
}
/** *//**
* Return true if the rowIndex value is currently set to a value that
* matches some element in the dataset. Note that it may match a row that is
* not in the currently cached DataPage; if so then when getRowData is
* called the required DataPage will be fetched by calling fetchData.
*/
public boolean isRowAvailable() {
DataPage page = getPage();
if (page == null) {
return false;
}
int rowIndex = getRowIndex();
if (rowIndex < 0) {
return false;
} else if (rowIndex >= page.getDatasetSize()) {
return false;
} else {
return true;
}
}
/** *//**
* Method which must be implemented in cooperation with the managed bean
* class to fetch data on demand.
*/
public abstract DataPage fetchPage(int startRow, int pageSize);
/** *//**
* 进行删除等操作后会立即改变列表项并且返回列表页的,请调用此方法,用于刷新列表。
*/
public void refresh() {
if (this.page != null) {
this.page = null;
getPage();
}
}
}