`
Linuxboy
  • 浏览: 77605 次
  • 来自: ...
社区版块
存档分类
最新评论

自定义分页Loop组件:PagedLoop

阅读更多

T5版本:tapestry-5.2.4

自定义PagedLoop组件的目的:

 

  1. 最主要的目的是对数据源进行分页。目前已公布的分页组件实现有两个,一个是chenillekit的PagedLoop, 一个是equanda版的JSPagedLoop。两者的区别是后者使用了ajax。
  2. 翻页时只从数据库中读取当前页的数据记录,而不是全部记录。
  3. 可以对读取的数据进行降序排列。
一、 源代码
PagedLoop.java

package org.example.components;

import java.util.Iterator;

import org.apache.tapestry5.Block;
import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.Component;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.corelib.components.Delegate;
import org.apache.tapestry5.corelib.components.Loop;
import org.apache.tapestry5.corelib.data.GridPagerPosition;
import org.apache.tapestry5.util.StringToEnumCoercion;
import org.northstar.worklive.internal.LoopDataSource;

public class PagedLoop implements ClientElement {

	 //@Parameter(value = "prop:componentResources.id", defaultPrefix = "literal")
	 //private String clientId;
	 
	 /**
	  * The element to render. If not null, then the loop will render the
	  * indicated element around its body (on each pass through the loop). The
	  * default is derived from the component template.
	  */
	 @Parameter(value = "prop:componentResources.elementName", defaultPrefix = "literal")
	 private String elementName;

	/**
	 * The element to render. If not null, then the loop will render the
	 * indicated element around its body (on each pass through the loop). The
	 * default is derived from the component template.
	 */
	 @Parameter(required = true, principal = true, autoconnect = true)
	 private LoopDataSource source;

	/**
	 * A wrapper around the provided Data Source that caches access to the
	 * availableRows property. This is the source provided to sub-components.
	 */
	private LoopDataSource cachingSource;
	
	/**
	 * Defines where the pager (used to navigate within the "pages" of results)
	 * should be displayed: "top", "bottom", "both" or "none".
	 */
	@Parameter(value = "bottom", defaultPrefix = "literal" )
	private String pagerPosition;
	
	private GridPagerPosition internalPagerPosition;

	/**
	 * The number of rows of data displayed on each page. If there are more rows
	 * than will fit, the Grid will divide up the rows into "pages" and
	 * (normally) provide a pager to allow the user to navigate within the
	 * overall result set.
	 */
	@Parameter("25")
	private int rowsPerPage;
	
	@Persist
	private int currentPage;
	
	/**
	 * The current value, set before the component renders its body.
	 */
	@SuppressWarnings("unused")
	@Parameter
	private Object value;
	
	/**
	 *If true and the Loop is enclosed by a Form, then the normal state saving logic is turned off.
     * Defaults to false, enabling state saving logic within Forms.
	 */
	@SuppressWarnings("unused")
	@Parameter(name = "volatile")
	private boolean volatileState;

	/**
     * The index into the source items.
     */
	@SuppressWarnings("unused")
	@Parameter
	private int index;
	
	/**
	 * Optional primary key converter; if provided and inside a form and not
	 * volatile, then each iterated value is converted and stored into the form.
	 */
	@SuppressWarnings("unused")
	@Parameter
	private ValueEncoder<?> encoder;
	
	@SuppressWarnings("unused")
	@Component(parameters = { "source=dataSource",
			"elementName=prop:elementName", "value=inherit:value",
			"volatile=inherit:volatileState", "encoder=inherit:encoder",
			"index=inherit:index" })
	private Loop loop;
	
	@Component(parameters = { "source=dataSource", "rowsPerPage=rowsPerPage",
	"currentPage=currentPage" })
	private Pager pager;
	
	@SuppressWarnings("unused")
	@Component(parameters = "to=pagerTop")
	private Delegate pagerTop;

	@SuppressWarnings("unused")
	@Component(parameters = "to=pagerBottom")
	private Delegate pagerBottom;
	
	/**
	 * A Block to render instead of the table (and pager, etc.) when the source
	 * is empty. The default is simply the text "There is no data to display".
	 * This parameter is used to customize that message, possibly including
	 * components to allow the user to create new objects.
	 */
	@Parameter(value = "block:empty")
	private Block empty;

	private String assignedClientId;
	
	@Parameter(name="OrderBy", defaultPrefix = "literal")
	private String propertyName;
	
	 /**
     * A version of LoopDataSource that caches the availableRows property. This addresses TAPESTRY-2245.
     */
    static class CachingDataSource implements LoopDataSource
    {
        private final LoopDataSource delegate;

        private boolean availableRowsCached;

        private int availableRows;

        CachingDataSource(LoopDataSource delegate)
        {
            this.delegate = delegate;
        }

        public int getTotalRowCount()
        {
            if (!availableRowsCached)
            {
                availableRows = delegate.getTotalRowCount();
                availableRowsCached = true;
            }

            return availableRows;
        }

        public void prepare(int startIndex, int endIndex, String propertyName)
        {
            delegate.prepare(startIndex, endIndex, propertyName);
        }

        /*public Object getRowValue(int index)
        {
            return delegate.getRowValue(index);
        }

        public Class<?> getRowType()
        {
            return delegate.getRowType();
        }*/

		@Override
		public Iterator<Object> iterator() {
			
			return delegate.iterator();
		}
    }

	public String getElementName() {
		return elementName;
	}
	
	public Object getPagerTop() {
		return internalPagerPosition.isMatchTop() ? pager : null;
	}

	public Object getPagerBottom() {
		return internalPagerPosition.isMatchBottom() ? pager : null;
	}

	public int getRowsPerPage() {
		return rowsPerPage;
	}

	public void setRowsPerPage(int rowsPerPage) {
		this.rowsPerPage = rowsPerPage;
	}
	
	public int getCurrentPage() {
		return currentPage;
	}

	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}

	void setupDataSource()
    {
        cachingSource = new CachingDataSource(source);

        int availableRows = cachingSource.getTotalRowCount();

        if (availableRows == 0)
            return;

        int maxPage = ((availableRows - 1) / rowsPerPage) + 1;

        // This captures when the number of rows has decreased, typically due to deletions.

        int effectiveCurrentPage = getCurrentPage();

        if (effectiveCurrentPage > maxPage)
            effectiveCurrentPage = maxPage;

        int startIndex = (effectiveCurrentPage - 1) * rowsPerPage;

        int endIndex = Math.min(startIndex + rowsPerPage - 1, availableRows - 1);

        cachingSource.prepare(startIndex, endIndex, propertyName);
    }
	
	Object setupRender() {
		if (currentPage == 0)
			currentPage = 1;
		
		internalPagerPosition = new StringToEnumCoercion<GridPagerPosition>(
                GridPagerPosition.class).coerce(pagerPosition);

		setupDataSource();

        // If there's no rows, display the empty block placeholder.

        return cachingSource.getTotalRowCount() == 0 ? empty : null;

	}

	Object beginRender() {
		// Skip rendering of component (template, body, etc.) when there's
		// nothing to display.
		// The empty placeholder will already have rendered.
		return (cachingSource.getTotalRowCount() != 0);
	}
	
	void onAction(int newPage){
		currentPage = newPage;
	}

	/**
     * Returns a unique id for the element. This value will be unique for any given rendering of a
     * page. This value is intended for use as the id attribute of the client-side element, and will
     * be used with any DHTML/Ajax related JavaScript.
     */
    @Override
	public String getClientId()
    {
        return assignedClientId;
    }
    
    public LoopDataSource getDataSource(){
    	return cachingSource;
    }
    
    public String getPropertyName(){
    	return propertyName;
    }
    public void setPropertyName(String name){
    	propertyName = name;
    }
    
}
 PagedLoop.tml

<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">

    <div t:id="pagerTop"/>

    <t:loop t:id="loop"><t:body/></t:loop>

    <div t:id="pagerBottom"/>

    <t:block>
        <div t:id="pager"/>
    </t:block>

    <t:block id="empty">${message:empty.list}</t:block>

</t:container>
 Pager.java(是复制GridPager.java过来的,只不过修改了source参数的数据类型)

package org.example.components;

import org.apache.tapestry5.*;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Events;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.dom.Element;
import org.apache.tapestry5.grid.GridDataSource;
import org.apache.tapestry5.internal.InternalConstants;
import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.ClientBehaviorSupport;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.northstar.worklive.internal.LoopDataSource;

/**
 * Generates a series of links used to jump to a particular page index within the overall data set.
 */
@Events(InternalConstants.GRID_INPLACE_UPDATE + " (internal event)")
public class Pager
{
    /**
     * The source of the data displayed by the grid (this is used to determine {@link GridDataSource#getAvailableRows()
     * how many rows are available}, which in turn determines the page count).
     */
    @Parameter(required = true)
    private LoopDataSource source;

    /**
     * The number of rows displayed per page.
     */
    @Parameter(required = true)
    private int rowsPerPage;

    /**
     * The current page number (indexed from 1).
     */
    @Parameter(required = true)
    private int currentPage;

    /**
     * Number of pages before and after the current page in the range. The pager always displays links for 2 * range + 1
     * pages, unless that's more than the total number of available pages.
     */
    @Parameter("5")
    private int range;

    /**
     * If not null, then each link is output as a link to update the specified zone.
     */
    @Parameter
    private String zone;

    private int lastIndex;

    private int maxPages;

    @Inject
    private ComponentResources resources;

    @Inject
    private Messages messages;

    @Environmental
    private ClientBehaviorSupport clientBehaviorSupport;

    @Environmental
    private JavaScriptSupport jsSupport;

    void beginRender(MarkupWriter writer)
    {
        int availableRows = source.getTotalRowCount();

        maxPages = ((availableRows - 1) / rowsPerPage) + 1;

        if (maxPages < 2) return;

        writer.element("div", "class", "t-data-grid-pager");

        lastIndex = 0;

        for (int i = 1; i <= 2; i++)
            writePageLink(writer, i);

        int low = currentPage - range;
        int high = currentPage + range;

        if (low < 1)
        {
            low = 1;
            high = 2 * range + 1;
        }
        else
        {
            if (high > maxPages)
            {
                high = maxPages;
                low = high - 2 * range;
            }
        }

        for (int i = low; i <= high; i++)
            writePageLink(writer, i);

        for (int i = maxPages - 1; i <= maxPages; i++)
            writePageLink(writer, i);

        writer.end();
    }

    private void writePageLink(MarkupWriter writer, int pageIndex)
    {
        if (pageIndex < 1 || pageIndex > maxPages) return;

        if (pageIndex <= lastIndex) return;

        if (pageIndex != lastIndex + 1) writer.write(" ... ");

        lastIndex = pageIndex;

        if (pageIndex == currentPage)
        {
            writer.element("span", "class", "current");
            writer.write(Integer.toString(pageIndex));
            writer.end();
            return;
        }

        Object[] context = zone == null
                           ? new Object[] { pageIndex }
                           : new Object[] { pageIndex, zone };

        Link link = resources.createEventLink(EventConstants.ACTION, context);

        Element element = writer.element("a",
                                         "href", zone == null ? link : "#",
                                         "title", messages.format("goto-page", pageIndex));

        writer.write(Integer.toString(pageIndex));
        writer.end();

        if (zone != null)
        {
            String id = jsSupport.allocateClientId(resources);

            element.attribute("id", id);

            clientBehaviorSupport.linkZone(id, zone, link);
        }
    }

    /**
     * Normal, non-Ajax event handler.
     */
    void onAction(int newPage)
    {
        // TODO: Validate newPage in range

        currentPage = newPage;
    }

    /**
     * Akjax event handler, passing the zone along.
     */
    boolean onAction(int newPage, String zone)
    {
        onAction(newPage);

        resources.triggerEvent(InternalConstants.GRID_INPLACE_UPDATE, new Object[] { zone }, null);

        return true; // abort event
    }
}
 

数据源接口:LoopDataSource.java

 

 

package org.example.internal;


public interface LoopDataSource extends Iterable<Object>{
	
	int getTotalRowCount();
	
	void prepare(int startIndex, int endIndex, String propertyName);
	
	//Object getRowValue(int index);

	//Class<?> getRowType();
}

 

数据源接口实现:HibernateLoopDataSource.java

 

 

package org.example.internal;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;

public class HibernateLoopDataSource implements LoopDataSource {
	
	private final Session session;

    private final Class<?> entityType;

    //private int startIndex;

    private List<Object> preparedResults;

    public HibernateLoopDataSource(Session session, Class<?> entityType){
    	assert session != null;
        assert entityType != null;
        this.session = session;
        this.entityType = entityType;
    }

    public int getTotalRowCount(){
    	Criteria criteria = session.createCriteria(entityType);
    	criteria.setProjection(Projections.rowCount());

        Number result = (Number) criteria.uniqueResult();

        return result.intValue();
    }
    
    @SuppressWarnings("unchecked")
	public void prepare(int startIndex, int endIndex, String propertyName){
    	Criteria crit = session.createCriteria(entityType);

        crit.setFirstResult(startIndex).setMaxResults(endIndex - startIndex + 1);
        if(propertyName!=null){
        
        crit.addOrder(Order.desc(propertyName));}
        
        //this.startIndex = startIndex;
        
        preparedResults = crit.list();
    }
    
    /*public Object getRowValue(int index)
    {
        return preparedResults.get(index - startIndex);
    }

    *//**
     * Returns the entity type, as provided via the constructor.
     *//*
    public Class<?> getRowType()
    {
        return entityType;
    }
*/
	@Override
	public Iterator<Object> iterator() {
		return preparedResults.iterator();
	}
}

 二、应用

 

 

<t:pagedLoop source="blogs" rowsperpage="10" value="blog"/>

 

@Inject private Session session; //Hibernate session

@Property
private Blog blog; //Entity

public LoopDataSource getBlogs(){
    return new HibernateLoopDataSource(session, Blog.class);
}
分享到:
评论

相关推荐

    小程序自定义分页选择组件

    "小程序自定义分页选择组件"是一个专门为解决页面分页问题而设计的组件,它提供了灵活的配置选项,允许开发者根据实际需求定制分页显示效果。下面我们将深入探讨这个组件的核心功能、参数设置以及如何在小程序中应用...

    qt自定义分页组件源代码

    QT自定义分页组件是软件开发中常见的设计模式,特别是在GUI应用中,用于展示大量数据时,分页能有效提高用户体验。在这个项目中,我们有`qt自定义分页组件源代码`,它包含了实现这一功能的核心源码和测试代码。下面...

    java分页标签自定义分页标签自定义分页标签

    自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签自定义分页标签...

    WinForm自定义分页控件

    使用`BindingSource`组件可以方便地实现数据绑定和分页操作。 4. **分页逻辑**:在事件处理程序中,你需要实现分页逻辑。这通常涉及计算当前页码、确定数据范围,然后更新数据显示。例如,如果每页显示10条数据,第...

    分页:高度自定义分页

    2.1.目前网络上很多分页主键,不能进行按钮选择, 2.2.甚至对样式修改的难度也较大, 2.3.使用在开发过程中由于使用到了分页功能,在这里写了一个分页功能。 3.按钮选择 之前的很多按钮是通过参数进行选择,这样不...

    asp.net 自定义分页

    本文将深入探讨如何在ASP.NET中实现自定义分页,以及与SQL数据库的集成。 首先,我们要理解ASP.NET中的分页原理。分页的基本思想是通过在服务器端对数据进行切割,每次只取一部分数据返回给客户端,然后在客户端...

    MyBatis高级应用:实现自定义分页插件

    为了提高代码的可维护性和可移植性,我们可以通过编写自定义分页插件来实现更灵活的分页逻辑。本文将详细介绍如何在 MyBatis 中实现自定义分页插件。 自定义分页插件提供了一种灵活且高效的方式来实现 MyBatis 的...

    .net分页控件,.net自定义分页控件

    首先,分页控件是数据展示的核心组件,尤其是在大数据集的场景下。通过分页,用户可以逐页浏览数据,而不是一次性加载所有记录,这大大减轻了服务器的压力,提升了网页加载速度。.NET框架提供了一些内置的分页控件,...

    Qt 实现的自定义分页控件

    实现自定义分页控件通常涉及以下几个关键组件: 1. **基类选择**:你可以选择继承`QWidget`或`QAbstractButton`作为自定义控件的基础。如果需要一个完整的组件,可以选择`QWidget`,因为它提供了一个空白画布,可以...

    mvc自定义分页封装

    LibHelper是一个支持自定义分页的工具类,详细介绍看这里 “MVC实现自定义分页”http://blog.csdn.net/xyj2014/article/details/49761963

    gridview实现自定义分页

    然而,标准的GridView控件默认的分页功能可能无法满足所有用户的需求,这时我们需要自定义分页来提供更加灵活和个性化的用户体验。本文将深入探讨如何在GridView中实现自定义分页,以及这一过程中的关键知识点。 ...

    swiper自定义分页器的样式

    本文实例为大家分享了swiper自定义分页器的样式代码,供大家参考,具体内容如下 js主要代码 pagination: { // 自定义分页器的类名----必填项 el: '.custom-pagination', // 是否可点击----必填项 clickable: ...

    java自定义分页标签

    在Java开发中,自定义分页标签是一种常见的需求,它能帮助我们构建用户友好的界面,使得大量数据的展示变得更加高效和便捷。本教程将详细讲解如何创建和使用自定义分页标签,以及如何实现淘宝购物分页标签的样式。 ...

    android自定义分页控件

    本教程将深入探讨如何在Android中创建一个自定义分页控件,以便实现高效、用户友好的界面。 首先,我们需要理解分页的基本原理。分页通常是通过在后台服务器上对数据进行切片,每次请求只加载一部分数据,而不是一...

    Angular2自定义分页组件

    在Angular2中,自定义分页组件是一种常见的需求,它能帮助用户更高效地浏览大量数据。本篇文章将深入探讨如何在Angular2中创建并使用一个自定义分页组件。 首先,我们需要理解分页的基本原理。分页通常涉及到以下几...

    基于vue2和element-ui实现的自定义分页表格组件

    这个资源是一个基于vue2和element-ui实现的自定义分页表格组件,是将element-ui的表格组件和分页组件封装成了一个组件,可以指定接口地址,快速实现分页表格的渲染,减少前端代码的编写。使用的技术:vue2.6.14、...

    自定义分页控件的实例演示WEB版

    本文将详细介绍如何创建一个自定义的分页控件,并通过Web版实例进行演示。 一、分页控件的基本概念 分页控件是网页设计中用于分割大量数据的一种交互组件,它允许用户通过点击不同页码来查看数据的不同部分。分页...

Global site tag (gtag.js) - Google Analytics