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" οnclick="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();
}
}
}