去年刚出从学校里走出来,由于大学教育理论与实际的脱节,我也不例外成了其牺牲品之一。在上一家公司的时候看到他们将分页 和 用户权限限制 这样的常用的功能封装成 标签,使用起来十分的方便。虽然实际的开发中,像我们这种刚进去的小罗罗只需要会调用就好了,但是自己的好奇心使然,总是想如果我要自己大件框架要自己实现这这些功能的封装,我要怎么办呢?所以,我必须去把它们学会。由于公司的那套框架封装的东西很多,层层叠叠的,每次去深究都看得我头昏脑胀的,其实主要还是自己以前在学校贪玩好耍,学艺不精,呵呵呵!这样反反复复的,第一年过去了,我还是没有真正的弄懂它们,真的是说来惭愧得很啦!
由于一遍一遍的看,在加上网上的资料实在少得可怜,没有哪个 好心人愿意分享一些正儿八经的正餐,通常大多是一些残羹冷炙的边角料。一遍一遍的看,再加上网上资料的搜索,终于是看懂了公司的那个分页标签的实现原理与细节。
原来的公司,用的是SSM (struts2.X + spring3.X + myBatis)的封装框架。所以那个分页标签就是实用的struts2的标签开发。xx.tld的标签类继承的是struts2 的标签类 而不是我们 通常直接看到的 TagSupport 或BodyTagSupport .标签页面的美化展现就使用 freemarker 的技术,在freemarker中使用css、jquery 那是一件多么幸福的事情啊。个人觉得这样才是所谓的mvc的开发思想嘛。类里面写逻辑,封装Pager 常用分页信息到 request 或 parameter 中去,再在 freemarker 中做 “首页” “尾页” 以及 页码数字 和 它们的样式美化的展现。到此,我想要看到的结果似乎已经看到了,所谓的分页技术我似乎就已经学会了。
然而,我是否真的学会了这个功能呢?假如,我用的不是 struts2 来做控制层又该怎么解决呢?比如现在比较流行好用的 spring mvc 也是很好的控制层啊。
随着这问题的抛出,我的思绪似乎又回到了零起点。我在网上寻寻觅觅,寻寻觅觅,可就是看不到我想要的mvc模式的分页标签功能实现。搜到的资料几乎都是在 xx.tld 对应的标签类中 既书写逻辑,又做 页码数字 与 样式的打印,一大堆的 双引号的 转义字符……,要在java类中实现 css 样式定位 与 javascript 的动作控制,那是一件多么痛苦的事情啊。简直忍无可忍。(给个例子瞧瞧)
public class PagerTag extends TagSupport {
private String value = "pb";// 存放数据实体的名字
public void setValue(String value) {
this.value = value;
}
@Override
public int doStartTag() throws JspException {
JspWriter out = pageContext.getOut();
String outStr = makeString();
try {
out.write(outStr);
} catch (IOException e) {
e.printStackTrace();
}
return SKIP_BODY;
}
private String makeString() {
// 获取到存放在request中的数据实体
Object p = pageContext.getRequest().getAttribute(
value);
PageBean pageBean = (PageBean) pageContext.getRequest().getAttribute(value);
StringBuffer sb = new StringBuffer();
if (pageBean != null && pageBean.getItems() != null
&& pageBean.getItems().size() > 0) {
sb.append("共" + pageBean.getCurPage() + " / "
+ pageBean.getPageNum() + "页");
//首页 上一页
if (pageBean.getCurPage() > 1) {
sb.append("<a href='?page=1'>首页</a>");
sb.append("<a href='?page=" + (pageBean.getCurPage()-1) + "'>上一页</a>");
}
//循环显示中间页码,这里也可控制最大只显示多少页码
for (int i = 1; i < pageBean.getPageNum() + 1; i++) {
if (i == pageBean.getCurPage()) {
sb.append(" " + i + " ");
} else {
sb.append("<a href='?page=" + i + "'> " + i + " </a>");
}
}
//下一页 末页
if(pageBean.getCurPage() != pageBean.getPageNum()){
sb.append("<a href='?page=" + (pageBean.getCurPage() + 1) + "'>下一页</a>");
sb.append("<a href='?page=" + pageBean.getPageNum() + "'>末页</a>");
}
}
return sb.toString();
}
}
看着都痛苦,简直要吐血的节奏啊,反正我是忍受不了这样的开发的,而且做出来的标签展现很丑的。
在没有看到它的日子里,我的生命里暗淡无光,总以为自己再也迈不开这个坎儿了。就在我几竟绝望的时候,我在自己买的那本spring企业开发实战一书中的一个例子中得到了灵感,找到了答案。从此这个分页功能实现的难度,在我面前变得荡然无存。哈哈哈,那一刻我心里简直爆发着灿烂的原子弹是的喜悦巨浪。那种喜悦与成就干难以被抑制啊。
下面进入正题!
对于jsp自定义标签的实现,大多数人知道xx.tld这种方法,其实还有一种方法的 xx.tag 实现。即:
1.xx.tld + 标签类 来实现;
2.xx.tag 文件来实现。
这两种方式都有各自的优缺点。第一种方法中因为有java类,任何复杂的逻辑,对它而言都是小儿科;第二种方法中,因为xx.tag文件就像jsp 中的include的使用,所以在xx.tag中就可以像实用jsp那样,可以写html 、css 、jquery,所以对于任何 酷炫的前端展现对它而言都是庖丁解牛的游刃有余。
分析一下便知道,分页标签重在于前端数字展现的人性化,复杂的逻辑也是集中在前端展现的逻辑,这完全可以用jquery 来轻轻松松地搞定的嘛。所以,就我个人而言,分页标签的实现最好的方法应该是 采用 xx.tag 的方式来实现,最最重要的事情是,这种方法是通用的,他不依赖于任何框架技术(如,常用的 struts2 和 spring). 有了它,分页标签的实现从此将变得是一次幸福愉快的开发之旅;有了它,分页标签的开发才真正是 美观、简单、快速高效 的代名词。
下面进入 xx.tag 分页标签开发的实际开发步骤!
xx.tag标签的开发只需要两个步骤:
1、pageBar.tag展示文件的创建与编辑;(放置路径:WEB-INF/tags/ )
2、使用标签页面的实用。(这里用的是usrListPaged.jsp)
下面贴出具体代码:
1、pageBar.tag
<%@ tag pageEncoding="utf-8" %>
<!-- 声明JSTL标签,以便在本标签中使用 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 定义了两个标签属性 -->
<%@ attribute name="formID" required="true" rtexprvalue="true" description="搜索条件提交的表单ID" %>
<%@ attribute name="isShowTotalPages" required="false" rtexprvalue="true" type="java.lang.Boolean" description="是否显示总页数" %>
<%@ attribute name="isShowJumpToButton" required="false" rtexprvalue="true" type="java.lang.Boolean" description="是否显示跳转按钮" %>
<div id="div_pager">
<a οnclick="getLink(1)">首页</a>
<a οnclick="getLink(${customPage.currentPage -1})"><<上一页</a>
<%-- <c:forEach var="pageNo" begin="1" end="${customPage.totalPages}" step="1">
<c:choose>
<c:when test="${pageNo == customPage.currentPage }">
<span class="curr"><c:out value="${pageNo }"></c:out></span>
</c:when>
<c:otherwise>
<a οnclick="getLink(${pageNo })"><c:out value="${pageNo }"></c:out></a>
</c:otherwise>
</c:choose>
</c:forEach> --%>
<label id="pagerNO"></label>
<a οnclick="getLink(${customPage.currentPage +1})">下一页>></a>
<a οnclick="getLink(${customPage.totalPages})">尾页</a>
<c:if test="${isShowTotalPages == null || isShowTotalPages != false }">
<label id="totalPage">共${customPage.totalPages}页</label><label id="totalRecords">/${customPage.totalRows}条数据</label>
</c:if>
<c:if test="${isShowJumpToButton == null || isShowJumpToButton != false }">
<label id="go_page_wrap" class="normalsize">转到
<span style="margin:0px 1px;padding:0px;">
<input type="text" id="btn_go_input" maxlength="5" value="${customPage.currentPage}" />
<input type="button" id="btn_go" value="确定"/>
</span>页
</label>
</c:if>
</div>
<script type="text/javascript">
$(function(){
//当前页码
var currentPage = ${customPage.currentPage};
//总页码
var totalPages = ${customPage.totalPages};
//总数据条数
var totalRows = ${customPage.totalRows};
var pageHtml = '';
var dot = '<span style="FONT-WEIGHT: normal;">...</span>';
//--------function in input text to jump into the page-------------//
$("#btn_go_input").focus(function(){
var $btn_go = $("#btn_go");
$btn_go.css("display","inline");
});
$("#btn_go_input").blur(function(){
validateInputPageNO()
});
$("#btn_go").click(function(){
validateInputPageNO();
});
//----------------------------//
// 1.总页数不足9页,就全部显示
if(totalPages < 9){
for(var i=1; i<totalPages; i++){
if(currentPage == i){
pageHtml += '<span class="curr">'+ i +'</span>';
}else{
pageHtml += '<a onClick="getLink('+ i +')" title="第'+ i +'页">'+ i +'</a>';
}
}// end for loop
}else{
//2.总页数 9页及以上
if(currentPage <= 5){ // 2.1 当前页码小于 6, 1到7页全显示,最后面用 '...' 代替
for(var i=1; i<=7; i++){
if(currentPage == i){
pageHtml += '<span class="curr">'+ i +'</span>';
}else{
pageHtml += '<a onClick="getLink('+ i +')" title="第'+ i +'页">'+ i +'</a>';
}
}// end for loop
pageHtml += dot;
}else{
// 2.2当前页码为6及以上
pageHtml += '<a onClick="getLink(1)" title="第1页">1</a>';
pageHtml += '<a onClick="getLink(2)" title="第2页">2</a>';
pageHtml += dot;
var begin = currentPage - 2;
var end = currentPage + 2;
if(end > totalPages){ // 计算出来的end页码超过总页数,end取尾页
end = totalPages;
begin = end -4;
if(currentPage - begin < 2){
begin = begin - 1;
}
}else if(end + 1 == totalPages){
end = totalPages;
}//end else if
for(var i=begin; i<=end; i++){
if(currentPage == i){
pageHtml += '<span class="curr">'+ i +'</span>';
}else{
pageHtml += '<a onClick="getLink('+ i +')" title="第'+ i +'页">'+ i +'</a>';
}
}// end for loop
if(end != totalPages){
pageHtml += dot;
}//end if
}//end else 2.2当前页码为6及以上
}//end more than 8 pages
$("#pagerNO").html(pageHtml);
});
// jump to the page
function getLink(n){
var $form = $("#${formID}");
var jdf_currentPage = '<input type="hidden" name="jdf_currentPage" value="'+n+'"/>';
$form.append(jdf_currentPage);
$form.submit();
}
// validdate the page .NO in input
function validateInputPageNO(){
var str_page = $("#btn_go_input").val();
if(isNaN(str_page)){
$("#btn_go_input").val(1);
return;
}
var n = parseInt(str_page);
// jump to the page
getLink(n);
}
</script>
<style>
<!--
#div_pager{
clear:both;
height:30px;
line-height:30px;
margin-top:20px;
color:black;
font-size:12px;
}
#div_pager a{
padding:4px 8px;
margin:10px 3px;
border:1px solid #FF6600;
background-color:#FFF;
color:#FF6600;
text-decoration:none;
FONT-WEIGHT: bold;
}
#div_pager span{
padding:4px 8px;
margin:10px 3px;
font-size:14px;
FONT-WEIGHT: bold;
}
#div_pager span.disabled{
padding:4px 8px;
margin:10px 3px;
border:1px solid #DFDFDF;
background-color:#FFF;
color:#DFDFDF;
}
#div_pager span.curr{
padding:4px 8px;
margin:10px 3px;
border:1px solid #FF6600;
background-color:#FF6600;
color:#FFF;
}
#div_pager a:hover{
background-color:#FFEEE5;
border:1px solid #FF6600;
cursor:pointer;
FONT-WEIGHT: bold;
}
#btn_go_input{
width:42px;
height:25px;
line-height:25px;
text-align:center;
}
#btn_go{
margin:0px;
padding:0px;
width:40px;
height:20px;
line-height:20px;
padding:0px;
font-family:arial,宋体,sans-serif;
text-align:center;
border:0px;
background-color:#0063DC;
color:#FFF;
display:none;
}
-->
</style>
此页面需要注意这样几点:
1)、页面实用到了jquery,所以在引用此标签的jsp页面中一定要引入一个jquery包;
2)、也面中的customPage对象是一个Pager 对象,里面封装了常用页面处理信息(总记录数、总页数、当前页等)
这个对象需要封装到request 或者 各自使用框架的 类似的对象上,只要你的jsp 页面中能够访问到 pager对象就行;
3)、标签中使用到的属性,在xx.tag中可以通过 ${} 取得值,比如我这里要在pageBar.tag中取得jsp 页面中
<pager:pageBar formID="user_loadUserList" isShowTotalPages="false" isShowJumpToButton="true"/>
formID、 isShowTotalPages 和 isShowJumpToButton的值, 那就用了${} 来取值的。
2、usrListPaged.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@taglib prefix="pager" tagdir="/WEB-INF/tags" %>
<%
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
request.setAttribute("path", basePath);
%>
<html>
<head>
<base href="<%=basePath%>">
<title>用户创建成功</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
<script type="text/javascript" src="${path}/js/commons/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="${path}/js/commons/jquery.json-2.4.js"></script>
<style type="text/css">
body{
font-size: 12pt;
margin: 0;
padding: 0;
}
.button{
margin:0;
padding-left:5px;
padding-right:5px;
height: 30px;
line-height: 30px;
border: 2px solid black;
background-color: yellow;
}
</style>
</head>
<body>
<form action="${path }/user/loadUserList.shtml"id="user_loadUserList">
用户名:<input type="text" id="userName" name="userName" maxlength="20" value="${filter.userName }"/>
<input type="submit" class="button" value="提交"/>
</form>
<br>
<table id="friends" width="700" border="1" cellspacing="0" cellpadding="0" >
<caption style="margin-bottom:10px;">查看所有好友信息</caption>
<thead>
<tr bgcolor="yellow" height="30">
<th width="30%">用户名</th>
<th width="30%">密码</th>
<th width="40%">真是姓名</th>
</tr>
</thead>
<tbody>
<c:if test="${userList != null}">
<c:forEach items="${userList}" var="user">
<tr height="30">
<td>${user.userName }</td>
<td>${user.password }</td>
<td>${user.realName }</td>
</tr>
</c:forEach>
</c:if>
<c:if test="${userList == null}">
<tr height="30">
<td colspan="3" align="center">没有数据</td>
</tr>
</c:if>
</tbody>
</table>
<pager:pageBarformID="user_loadUserList" isShowTotalPages="false" isShowJumpToButton="true"/>
</body>
<script type="text/javascript">
$(function(){
//alert("aaaaaaaaaaaaaaaa");
});
</script>
</html>
分页功能的总结: 前端分页 提交给后台 Java类的关键参数就是 当前的搜索条件、当前页号。所以,我的标签里面是给标签处理文件提供 formID 而不是 url地址。后台代码在封装时也要满足这样一条准则,使用你标签的用户只需关注他自己的业务需求,其他的一切分页工作都由你来封装实现。
也许说多了大家也不太相信,好吧,下面给大家秀一秀最终效果吧。
哈哈,就扯到这里吧,感觉如何呢?