article_list.jsp的部分核心代码:
Pager.jsp的代码:
taglib.jsp
Pager.jsp中引入了taglib.jsp,用到tablib.jsp中的<%@ taglib prefix="pg" uri="http://jsptags.com/tags/navigation/pager" %>
将分页处理的jsp代码封装到一个通用的jsp中,在显示页面article_list.jsp中只能看到include了这个pager.jsp页面,这就是封装。
分析pager.jsp:
1、在article_list.jsp中有如下代码,动态包含了pager.jsp页面:
<jsp:include page="/backend/common/pager.jsp">
<jsp:param name="url" value="SearchArticlesServlet" />
</jsp:include>
这个就相当于是写成<jsp:include page="/backend/common/pager.jsp?url=SearchArticlesServlet">。在pager.jsp中可以通过request.getParameter
来把数据拿出来,不过可以用el表达式的${param.url}来代替request.getParameter,这样做的好处就是增加pager.jsp的可复用性(而不是直接在pager.jsp页面中定死url=SearchArticlesServlet)。
在pager.jsp中通过<pg:pager url="${param.url}" items="${total}" maxPageItems="${pagesize}" maxIndexPages="10" export="currentPageNumber=pageNumber">。
这里的url="${param.url}"的意思是从article_list.jsp中根据<jsp:param name="url" value="SearchArticlesServlet" />取出value="SearchArticlesServlet" 赋值给url,而这里的url的作用是:点击页面上的时转向url指向的地址(即SearchArticlesServlet),当然在SearchArticlesServlet后面还有一些参数。
如果在article_list.jsp页面中:
<jsp:include page="/backend/common/pager.jsp">
<jsp:param name="params" value="title,source,content…" />
</jsp:include>
在pager.jsp中这么取:
<c:forEach items="${param.params}" var="p">
<pg:param name="${p}" />
</c:forEach>
<pg:param> 这个标签解释的时候,将向父标签<pg:pager>插入参数param,<pg:param>会跟<pg:pager>交互,涉及到底层的taglib交互的知识,先略过吧。
2、items="${total}"表示通过在servlet中根据查询条件查询数据库确定了符合查询条件的总共的文章数total,将这个数赋值给了pager-taglib中的默认参数items,items是一个int类型的值,表示总记录数,pager-taglib根据items(和其他参数)来自动分页(这里的items不同于<c:forEach items="${articles}" var="a">,这个的items表示的是一个循环,articles是循环的集合对象)
3、maxPageItems="${pagesize}" maxIndexPages="10",maxPageItems表示一个页面最大显示的文章数,maxIndexPages表示页面下边显示的最大的索引数。
4、 <pg:last>
共 <strong>${pageNumber}</strong> 页
</pg:last> :这里用到一个小技巧,即总共的页面数即为最后最后一页的页码。
5、<pg:param name="title" />这句话的意思是:自动从当前的request中getParameter("title"),如果request中存在这个参数的话,就自动把这个参数的值附加到url链接(即SearchArticlesServlet)的后面。
6、<pg:first>
<a id="firstPage" href="${pageUrl}">首页</a>
</pg:first>
pager-taglib自动计算出地址(包含了pager.offset值),调用pageContext的setAttribute方法,将地址放进去,取名为pageUrl(setAttribute("pageUrl",地址~~~)),在<pg:first>中就可以通过el表达式取出pageUrl的值。
注意这里默认叫做pager.offset(通过这个pager.off可以在servlet中查询从pager.off到pager.off+pagesize的文章)
pageUrl、pageNumber、firstItem、lastItem…叫做<pg:first>标签的导出参数。
这里的pageUrl是跟外围标签关联的,
<pg:prev>
<a href="${pageUrl}">前页</a>
</pg:prev>这里的pageUrl的值就是前页的地址,跟<pg:first>中的pageUrl不同。
<pg:pages>
<c:choose>
<c:when test="${currentPageNumber eq pageNumber}">
<font color="red">${pageNumber}</font>
</c:when>
<c:otherwise>
<a href="${pageUrl}">${pageNumber}</a>
</c:otherwise>
</c:choose>
</pg:pages>
<pg:pages>相当于是循环(不只是能显示从第一页到最后一页,也可以自行设置),在这里当前页面和<pg:pages>中的每一页名字都叫pageNumber,所以在<pg:pager url="${param.url}" items="${total}" maxPageItems="${pagesize}" maxIndexPages="10" export="currentPageNumber=pageNumber">中设置export="currentPageNumber=pageNumber",即将当前页面的名字改为currentPageNumber,然后判断如果当前页的页码和页面下方显示的一排页码中的某个页码相等时,将那个页码红色显示,其他页码则可以导航到相应页面。
7、当article_list.jsp页面的下拉选择框发生变化时,调用selectPagesize()方法
function selectPagesize(field){
window.location = document.getElementById("firstPage").href + "&pagesize="+field.value;
}
因为当下拉选择框发生变化,即每页显示的文章数发生变化时,肯定需要重新显示第一页,
这里通过小技巧,在pager.jsp中设定了首页的id="firstPage",
<pg:first>
<a id="firstPage" href="${pageUrl}">首页</a>
</pg:first>
所以在selectPagesize()方法中可以直接通过document.getElementById("firstPage").href+ "&pagesize="+field.value获取第一页的地址,而不用写死成window.location= "SearchArticleServlet?title=${param.title}&pagesize="+field.value,增加了pager.jsp页面的可复用性。
在来个小问题:
如果在类路径中存在重复的类,放在前面的会被先加载,而后面的将不会被加载。
在WEB-INF中classes先与lib被加载,也就是说如果classes中有个类,而lib包中也有个相同的类,这样的话,会只加载classes中的类
在默认情况下pager-taglib使用系统默认编码(GBK),如果jsp页面使用的UTF-8编码的话,就会出一些问题,这是pager-taglib里面的bug,解决方案:
新建一个pager-taglib目录,将原来的pager-taglib.jar包里的文件copy到这个目录下,然后修改新pager-taglib目录下的\com\jsptags\navigation\pager下的PagerTag.java类 (jsp页面中使用的<pg:pager>对应的类)的final void addParams(String name, String value){}f方法:
final void addParam(String name, String value) {
if (value != null) {
name = java.net.URLEncoder.encode(name, pageContext.getResponse().getCharacterEncoding());
value = java.net.URLEncoder.encode(value, pageContext.getResponse().getCharacterEncoding());
uri.append(params == 0 ? '?' : '&')
.append(name).append('=').append(value);
params++;
} else {
String[] values = pageContext.getRequest().getParameterValues(name);
if (values != null) {
name = java.net.URLEncoder.encode(name, pageContext.getResponse().getCharacterEncoding());
for (int i = 0, l = values.length; i < l; i++) {
value = java.net.URLEncoder.encode(values[i], pageContext.getResponse().getCharacterEncoding());
uri.append(params == 0 ? '?' : '&')
.append(name).append('=').append(value);
params++;
}
}
}
}
为了更简便,新建一个工程pager-taglib,然后将中的pager-taglib目录的内容复制到pager-taglib工程的src目录下,由于taglib需要用到javaee的一些jar包,所以需要引入Java EE 6 Library,同时还需要将原pager-taglib中的taglib.tld文件copy到
这里还有一个bug,即当搜索没有匹配结果时,尾页的url的pager.offset居然是-5(当然如果pagesize是10的话,pager.offset就是-10了),应该为0,修改方法为:
原来的\com\jsptags\navigation\pager下的LastTag.java类为:
修改后的LastTag.java类为:
package com.jsptags.navigation.pager;
public final class LastTag extends JumpTagSupport {
protected int getJumpPage() {
if(pagerTag.getPageCount() == 0) {
return 0 ;
}
return (pagerTag.getPageCount() - 1);
}
}
然后将pager-taglib工程export成一个Java/JAR files,,生成一个新的jar包,这样以后的项目可以直接使用这个修正bug后的jar包