SSM+jquery封装的ajax实现前后台分页的异步刷新
目录
1.什么是分页
2.需求
3.思路分析,变量介绍
4.逻辑操作步骤
5.具体实例,代码分析
6.总结
什么是分页?
分页,顾名思义就是将所有数据按页码分组分别显示在属于自己的当前页内,当数据很多的时候,只在一页里看比较杂乱,所以用到分页,使显示更清晰有条理。场景如下图(不是很美观,主要看功能❤):
需求:
1、点击全部分类,将全部分类里的图书分页
2、点击全部分类下面的其它类型,将其他类型中显示的图书分页
3、点击1,2,3,4,5等等页码显示每页中的数据
4、点击哪个页码,哪个页码变成红色
5、点击上一页,跳到上一页展示的界面,点击下一页跳到下一页显示的界面
6、实现图书信息与页码的局部刷新
思路分析,变量介绍:
前台需要展示的是一页一页的数据,每一页包含的信息有两个:当前页码(currentPage
)、每一页显示的数据的条数(pageSize
)。其他的事情前台不用关心,它的目的只是展示数据。而数据的来源在后台,前台告诉后台需要哪一页的数据以及这一页有多少条数据,pageSize
是可以设定的,所以currentPage
是前台和后台交互的核心。那么后台接收到前台的currentPage
,要去识别它,根据这个参数到数据库取得数据返回给前台。
当点击全部分类时根据前端传过来的当前页,去数据库中获取每页显示的数据,再利用count(*)获取书总行数来计算总页数,再将book,bookList,page
传递到前端,特别注意:传递book主要是用book中cid的属性,这里的book类中的cid属性为空,虽说我们没有用到,但是要给设个值book.setCid="0"
,因为要做为book.jsp显示图书界面的a链接中的点击事件的方法里的参数传递,为空会报错,为什么要作为参数呢?因为不论是全部分类,还是下面的三个分类,都共用一个界面book.jsp中的页码的<div>
,所以当点击a链接触发点击事件,将数据传递到ajax内,ajax参数返回到控制器的ajax.do方法内,如果是下面的三个分类触发点击事件进来的,此时就需要cid的值去查询cid为1,2,3的图书信息,而如果是全部分类触发点击进来的,就不需要通过cid查图书,就默认设置cid为0作为参数,这样就不会报错了。
当点击全部分类的下面三个类型,在一张表category中,表中的cid属性与book表中的cid属性外键连接,所以当点击下面的链接时要根据传过来的cid与当前页去数据中查询图书的信息,再传递到前端界面,这里注意要将book,bookList,page 一起传递到前端,此时点击三个分类的哪个a链接,再触发点击事件,ajax都将对应的cid传回到控制层的ajax.do方法内,然后根据cid去数据库中查出对应的所有图书信息与总行数放到BookPage包装类里,通过@Response
注解一起再返回return
到ajax的success里进行拼接,完成异步刷新。
因为不论是全部分类触发点击事件,还是下面属于同一张表的三个分类触发的点击事件都会进入到同一个控制器的ajax.do方法内,此时就要在进入之前拼一个参数,判断一下,当这个参数为0,证明为全部分类触发,调用mapper.xml文件中查询所有书的那个方法,当这个参数为1,证明为全部分类下面的三个分类触发,调用mapper.xml文件中根据cid查询所有书那个方法。0与1在left.jsp中a链接后面作为路由参数传递到控制器。
属性介绍:
①、Page类中的属性解析:
1、先说说currentPage
,currentPage
的值怎么设置?当然是后台告诉前台一共有多少页(totalPage
),然后前台要展示的数据就在0~totalPage
页之间,当前的处于哪一页就是currentPage
的值;
2、rows
(数据库中记录总数),从数据库中查出的记录总数,通过sql指令中的count(*)
查询。
3、totalPage
(总页数),计算公式为:当从数据库中的查出的记录总数(rows
)为偶数时, totalPage
(页数) =
数据库中该表的记录总数(rows
)/pageSize
(每一页记录数) ;当从数据库中的查出的记录总数(rows
)为奇数时, totalPage
(页数) =
数据库中该表的记录总数(rows)/pageSize
(每一页记录数) +1
;
4、pageSize
,每一页记录数,这很好理解,每一页要展示多少数据,自己字自定义类中设定。
5、begin
(每页的数据的起始下标),起始下标begin
计算公式:begin = (currentPage-1)*pageSize
。这个变量是为了给数据库中执行分页的sql语句limit使用,limit使用规则就是 limt begin,pageSize
;
6、 id,用来判断是哪个分类触发的点击事件进入的ajax.do方法内,再对数据库进行对应操作(接收left.js传过来的定义的固定值,0与1,0为全部分类,1为下面的三个分类)
②、BookPage类属性介绍:
因为我们要用ajax实现异步刷新的功能,所以books放书的<div>
与页码的<div>
两个部分要实现局部刷选,就需要将两处的信息传回到ajax的success内,因为不可以同时返回两个类的值,所有要定一个BookPage的包装类,将两个类放到一起,在传到前端ajax的界面,然后分别获取books与page的数据进行两个<div>
的拼接重写,覆盖起初的两块<div>
,以达到局部刷新的目的;
private List<Book> books;//存储每页的图书信息
private Page page; // 存储页码以及前台所需数据
逻辑操作步骤
1、创建page类,用来存储页面传递的当前页码、sql语句limit中起始下标等等;
2、创建BookPage类,存储book信息与page信息
3、Mapper.xml中 根据起始下标和size查询数据、查询总行数,返回Service层,再返回到controller层;
4、控制器controller层中执行Mapper两个方法,将list与page对象传递到页面;
5、前端设置上一页逻辑,判断当前页是否为第一页,如果是则点击上一页不跳转;
6、for
循环,次数为totalPage
从1
开始到totalPage
结束 显示每页页码;
7、判断下一页逻辑,如果当前页为totalPage
则点击不跳转;
8、判断当点击那一页,将那一页的页码颜色设为红色red
,其他不用设置颜色;
9.为页码与上一下和下一页的链接设置相同的点击事件,方法参数为当前页码;(currentPage
),返回到控制层新的方法里,获取books
与page
的数据赋值给BookPage
的对象,通过@Response
注解传回到ajax的success
界面;
10.在ajax的success
里分别获取两个对象的数据,然后拼接出显示图书与页码的两个div,再将原先的覆盖掉;
具体实例,代码分析
①、创建Page类:
package com.xlj.book.Page;
/**
* Created by 小林 on 2018/11/20.
*/
public class Page {
private int currentPage = 1;//当前页
private int pageSize = 2; // 每页显示条数
private int totalPage; //总页数
private int rows; // 数据总条数
private int begin; //起始下标
private int id;// 接收传过来的参数,判断是点击全部分类进controller的ajax.do还是同属一张表中的三个类型点击进controller的ajax.do
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalPage() {
//计算总页数,根据数据总条数是奇还是偶判断
if (rows%pageSize == 0){
totalPage=rows/pageSize;
}else {
totalPage=rows/pageSize+1;
}
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getRows() {
return rows;
}
public void setRows(int rows) {
this.rows = rows;
}
public int getBegin() {
// 计算起始下标
begin = (currentPage-1)*pageSize;
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
@Override
public String toString() {
return "Page{" +
"currentPage=" + currentPage +
", pageSize=" + pageSize +
", totalPage=" + totalPage +
", rows=" + rows +
", begin=" + begin +
", id=" + id +
'}';
}
}
②、创建BookPage类:
package com.xlj.book.Page;
import com.xlj.po.Book;
import java.util.List;
/**
* Created by 小林 on 2018/11/20.
*/
public class BookPage {
private List<Book> books; //存每页显示的图书信息
private Page page; // 存页码的信息
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
}
③、Mapper.xml文件:(从数据库中获取书与page中的rows数据)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xlj.mapper.BookMapper" >
<!--分页-->
<!--全部分类触发,获取每页的图书信息-->
<select id="findAllBooks" resultType="com.xlj.po.Book" parameterType="com.xlj.book.Page.Page">
SELECT * FROM book limit #{begin},#{pageSize};
</select>
<!--一张表中的三个分类触发,获取对应表中每页的数据-->
<select id="findByCategory" parameterType="com.xlj.po.Book" resultType="com.xlj.po.Book">
SELECT * FROM book WHERE cid = #{cid} limit #{page.begin},#{page.pageSize};
</select>
<!--全部分类查询总行数-->
<select id="findRows" resultType="int">
select COUNT(*) from book;
</select>
<!--一张表中的三个分类获取对应分类的总行数-->
<select id="findByRows" resultType="int" parameterType="java.lang.String">
SELECT COUNT(*) FROM book WHERE cid =#{cid};
</select>
</mapper>
由于我们采用 Mapper 代理加载 xxxMapper.xml 文件,这里我们重复一下 Mapper 代理所需的条件,接口和xml文件必须满足以下几个条件:
1、接口必须要和 xml 文件同名且在同一个包下,也就是说 xml 文件中的namespace是接口的全类名
2、接口中的方法名和xml 文件中定义的 id 一致
3、接口输入参数类型要和xml 中定义的 parameterType 一致
4、接口返回数据类型要和xml 中定义的 resultType 一致
返回Service,再返回的Controller的分层技术步骤省略;
④、controller层中add方法里:(项目中有全部分类功能,点击进入控制器book/all.do中all方法中),(我在web.xml文件中设置了浏览器拦截规则为.do形式,springmvc框架知识,这里不细说)
@Autowired
private BookServiceImpl bookService;
@RequestMapping("all")
public String all(Model model, Page page){
List<Book> bookList = bookService.findAll(page);
int rows = bookService.findRows();
page.setRows(rows);
model.addAttribute("bookList",bookList);
Book book = new Book();
book.setCid("0");
model.addAttribute("page",page)
return "book/list";
}
⑤前端界面接收page与bookList遍历bookList铺在界面上,代码如下:(里面三个choose判断就是上一页,下一页,与显示每页包括点击哪页,哪页页码变成的代码逻辑实现,很简单,一看就懂)
<c:forEach var="books" items="${bookList}">
<div style="float: left" id="icon">
<a href="${pageContext.request.contextPath}/book/detail.do?bid=${books.bid}"><img src="../../../${books.image}" border="0"/></a>
<br/>
// 项目中另一个分类功能模块,同样查询图书
<a href="${pageContext.request.contextPath}/book/detail.do?bid=${books.bid}">${books.bname}</a>
</div>
</c:forEach>
<div class="pagination" id="pageId" style="position: absolute;left: 80px;top: 200px;">
<c:choose>
//当为第一页就就不会继续跳
<c:when test="${page.currentPage==1}">
<a href="javascript:void(0);">上一页</a>
</c:when>
// 否则跳到上一页(ajax实现)
<c:otherwise>
<a href="javascript:void(0);" onclick="pageAction(${page.currentPage-1},${book.cid})">上一页</a>
</c:otherwise>
</c:choose>
<c:forEach begin="1" end="${page.totalPage}" var="p">
<c:choose>
//点击哪个页码就给他加个红色字体样式
<c:when test="${p==page.currentPage}">
<a href="javascript:void(0);" onclick="pageAction(${p},${book.cid})" style="color: red;">${p}</a>
</c:when>
<c:otherwise>
<a href="javascript:void(0);" onclick="pageAction(${p},${book.cid})">${p}</a>
</c:otherwise>
</c:choose>
</c:forEach>
<c:choose>
<c:when test="${page.currentPage==page.totalPage}">
<a href="javascript:void(0);">下一页</a>
</c:when>
<c:otherwise>
<a href="javascript:void(0);" onclick="pageAction(${page.currentPage+1},${book.cid})">下一页</a>
</c:otherwise>
</c:choose>
⑥、触发点击事件,进入pageAction方法中,ajax的success之前代码:
function pageAction(p,a) {
alert(p+'+'+a);
var str ="currentPage="+p+"&cid="+a;
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/book/ajax.do?id=${page.id}',
data:str,
success:function (data) {
console.log(成功');
}
)}
}
⑦进入到ajax.do内,ajax.do代码:
@RequestMapping("ajax")
@ResponseBody
public BookPage ajax(Page page,Book book){
// page中的id判断是点击全部分类进来的,还是点击同属一个表的其他分类进来的
if (page.getId() == 0) { // 说明全部分类进来
// 直接查询每页显示的图书信息
List<Book> bookList = bookService.findAll(page);
for (Book b : bookList) {
// 可有可无,因为全部分类中不需要查询其他类型的书,所以返回的cid对查询没有影响,只要进入这个判断就Ok了。但给设个cid为0防止参数报错!
b.setCid("0");
}
int rows = bookService.findRows();
page.setRows(rows);
// 特别注意:包装类里的属性要给设上值
BookPage bookPage = new BookPage();
bookPage.setBooks(bookList);
bookPage.setPage(page);
return bookPage;
}else { 否则:点击全部分类下面的分类点击进来的
book.setPage(page);
// 根据传过来的cid(用book接收的),去数据库中查询对应分类中的每页显示的图书信息
List<Book> bookList = bookService.findByCategory(book);
int rows = bookService.findByRows(book.getCid());
page.setRows(rows);
// 注意包装类的属性要给设上值
BookPage bookPage = new BookPage();
bookPage.setBooks(bookList);
bookPage.setPage(page);
return bookPage;
}
}
⑧、返回到的ajax内的success里(异步刷新,拼接图书信息的div与页码的div),ajax里完整的代码如下:
function pageAction(p,a) {
alert(p+'+'+a);
var str ="currentPage="+p+"&cid="+a;
$.ajax({
type:'post',
url:'${pageContext.request.contextPath}/book/ajax.do?id=${page.id}',
data:str,
success:function (data) {
//console.log(data);
//页面数据处理
var result = data.books;
//console.log(result.length);
var l = '';
// 在外面定义一个0,是为了将循环得到的obj.cid赋值给他,相当于计数器,作为后面的拼接页码里的点击事件的方法里的参数使用
var a =0;
for (var i=0;i<result.length;i++){
var obj = result[i];
a = obj.cid;
l += '<div style="float: left;" id="icon">';
l += '<a href="${pageContext.request.contextPath}/book/detail.do?bid='+obj.bid+'">' + '<img src="../../../'+obj.image+'" border="0"/>' + '</a>';
l += '<br/>';
l +='<a href="${pageContext.request.contextPath}/book/detail.do?bid='+obj.bid+'">'+obj.bname+'</a>';
l +='</div>'
}
//jquery获取div标签对象,将它下面a链接的子标签删掉
$("#icon>a").remove();
// 再用拼接好的替换
$("#icon").html(l);
// 页码处理
var page = data.page;
// console.log(page.totalPage);
console.log(a);
var l1 = '';
if (page.currentPage == 1){
l1 += '<a href="javascript:void(0);">&上一页</a>';
}else {
l1 += '<a href="javascript:void(0);" onclick="pageAction('+(page.currentPage-1)+','+a+')">&上一页</a>';
}
l1 +=' ';
for(var i=1;i<page.totalPage+1;i++) {
if (i == page.currentPage) {
l1 += '<a href="javascript:void(0);" onclick="pageAction(' + i +','+a+ ')" style="color: red">&' + i + '</a>';
} else {
l1 += '<a href="javascript:void(0);" onclick="pageAction(' + i +','+a+ ')">&' + i + '</a>';
}
}
l1 +=' ';
if (page.currentPage == page.totalPage){
l1 += '<a href="javascript:void(0);">下一页&</a>';
}else {
l1 += '<a href="javascript:void(0);" onclick="pageAction('+(page.currentPage+1)+','+a+')">下一页&</a>';
}
// 同上
$("#pageId>a").remove();
$("#pageId").html(l1);
}
})
}
结果如图:
我在ajax的success里拼接的代里前面拼了一个&,这样就能判断我拼接的代码是否被实现了,如图显示,说明实现局部刷新了.
总结:
分页,前端传过来一个当前页,后台就能通过这个值来从数据库中获取到每页要显示的数据与计算出总页数,再传递给前端,前端通过各种判断将数据铺在界面上,分页显示出来,特别注意:ajax实现异步刷线要设置点击事件,点击触发方法传参,参数为当前页,返回到后台再去数据库中查数据,后台将数据返回到ajax的success内,拼接替换掉显示数据的代码块与分页的代码块,完成ajax异步刷新功能!!!!