Ajax方式分页加载列表实现
在前面:
最近需要用到这个功能,所以这几天一直在研究这个,目前大致功能已实现,后续需要完善,但需要的功能点已完成,记录下;
1、分页功能引入bootstrap的分页插件:
<script type="text/javascript" src="3rd/jquery/jquery.js"></script> <script type="text/javascript" src="3rd/bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="3rd/bootstrap/js/bootstrap-paginator.min.js"></script>
2、Ajax没有使用jquery的,而是找了一个方便调试的代码,主要看重支持跨域:
(function(window,undefined) { function ajax(options) { //编码数据 function setData() { //设置对象的遍码 function setObjData(data, parentName) { function encodeData(name, value, parentName) { var items = []; name = parentName === undefined ? name : parentName + "[" + name + "]"; if (typeof value === "object" && value !== null) { items = items.concat(setObjData(value, name)); } else { name = encodeURIComponent(name); value = encodeURIComponent(value); items.push(name + "=" + value); } return items; } var arr = [],value; if (Object.prototype.toString.call(data) == '[object Array]') { for (var i = 0, len = data.length; i < len; i++) { value = data[i]; arr = arr.concat(encodeData( typeof value == "object"?i:"", value, parentName)); } } else if (Object.prototype.toString.call(data) == '[object Object]') { for (var key in data) { value = data[key]; arr = arr.concat(encodeData(key, value, parentName)); } } return arr; }; //设置字符串的遍码,字符串的格式为:a=1&b=2; function setStrData(data) { var arr = data.split("&"); for (var i = 0, len = arr.length; i < len; i++) { name = encodeURIComponent(arr[i].split("=")[0]); value = encodeURIComponent(arr[i].split("=")[1]); arr[i] = name + "=" + value; } return arr; } if (data) { if (typeof data === "string") { data = setStrData(data); } else if (typeof data === "object") { data = setObjData(data); } data = data.join("&").replace("/%20/g", "+"); //若是使用get方法或JSONP,则手动添加到URL中 if (type === "get" || dataType === "jsonp") { url += url.indexOf("?") > -1 ? (url.indexOf("=") > -1 ? "&" + data : data) : "?" + data; } } } // JSONP function createJsonp() { var script = document.createElement("script"), timeName = new Date().getTime() + Math.round(Math.random() * 1000), callback = "JSONP_" + timeName; window[callback] = function(data) { clearTimeout(timeout_flag); document.body.removeChild(script); success(data); } script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback; script.type = "text/javascript"; document.body.appendChild(script); setTime(callback, script); } //设置请求超时 function setTime(callback, script) { if (timeOut !== undefined) { timeout_flag = setTimeout(function() { if (dataType === "jsonp") { delete window[callback]; document.body.removeChild(script); } else { timeout_bool = true; xhr && xhr.abort(); } console.log("timeout"); }, timeOut); } } // XHR function createXHR() { //由于IE6的XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。 //所以创建XHR对象,需要在这里做兼容处理。 function getXHR() { if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else { //遍历IE中不同版本的ActiveX对象 var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"]; for (var i = 0; i < versions.length; i++) { try { var version = versions[i] + ".XMLHTTP"; return new ActiveXObject(version); } catch (e) {} } } } //创建对象。 xhr = getXHR(); xhr.open(type, url, async); //设置请求头 if (type === "post" && !contentType) { //若是post提交,则设置content-Type 为application/x-www-four-urlencoded xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); } else if (contentType) { xhr.setRequestHeader("Content-Type", contentType); } //添加监听 xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (timeOut !== undefined) { //由于执行abort()方法后,有可能触发onreadystatechange事件, //所以设置一个timeout_bool标识,来忽略中止触发的事件。 if (timeout_bool) { return; } clearTimeout(timeout_flag); } if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { success(xhr.responseText); } else { error(xhr.status, xhr.statusText); } } }; //发送请求 xhr.send(type === "get" ? null : data); setTime(); //请求超时 } var url = options.url || "", //请求的链接 type = (options.type || "get").toLowerCase(), //请求的方法,默认为get data = options.data || null, //请求的数据 contentType = options.contentType || "", //请求头 dataType = options.dataType || "", //请求的类型 async = options.async === undefined ? true : options.async, //是否异步,默认为true. timeOut = options.timeOut, //超时时间。 before = options.before || function() {}, //发送之前执行的函数 error = options.error || function() {}, //错误执行的函数 success = options.success || function() {}; //请求成功的回调函数 var timeout_bool = false, //是否请求超时 timeout_flag = null, //超时标识 xhr = null; //xhr对角 setData(); before(); if (dataType === "jsonp") { createJsonp(); } else { createXHR(); } } window.ajax = ajax; })(window);
3、主要使用方法:
var options = { bootstrapMajorVersion: 3, currentPage: 1, //当前页 numberOfPages: 5, //显示的页数 totalPages: 20, //总页数 itemTexts: function(type, page, current) { switch(type) { case "first": return "首页"; case "prev": return "上一页"; case "next": return "下一页"; case "last": return "末页"; case "page": return page; } }, onPageClicked: function(e, originalEvent, type, page) { console.log('ni dian le '+page); var data = {pageNo: page,pageSize: 10}; searchModule.query(data); } }; var searchModule = { query: function(data) { console.log('你点了query'); console.log('查询条件:'+JSON.stringify(data)) ajax({ type: "post", url: "/asset/search_jsonp.do", dataType: "jsonp", data: data, timeOut: 15000, before: function() { console.log("before"); $('#pagination').bootstrapPaginator(options); }, success: function(response) { // console.log(response); var items = response.articles; var html = ''; for(index in items){ var item = items[index]; // console.log(item) html+='<li class="row no-margin ng-scope" onclick="'+searchModule.click(item)+'"\ role="button" tabindex="0"><div class="pull-left item-left">\ <img class="search-thumb ng-scope" style="background: #202334 url('+item.artFrame+') no-repeat center;background-size: contain;">\ </div><div class="item-intro"><div class="row no-margin item-top">\ <div class="col-md-12 title-box ng-binding">'+item.basicTitle+'</div></div>\ <div class="row no-margin-right item-bottom">\ <div class="col-md-3 no-padding icon-gray">\ <i class="glyphicon glyphicon-user "></i>\ <span class="ng-binding">'+item.articleAuthor+'</span>\ </div></div></div>\ <div class="pull-right item-right ng-scope" >\ <div class="ng-binding">\ <i class="glyphicon glyphicon-facetime-video"></i>' +item.dateline+'</div><div style="margin-top:25px;" class="ng-binding">\ <i class="fa fa-clock-o"></i>'+item.dateline+'</div></div></li>'; } $(".topicList").html(html); }, error: function() { console.log("error"); } }); }, click: function(item) { console.log('你点了click'); }, moreCtrl: function() { if($('.more').hasClass('isShow')) { $('.more').removeClass('isShow'); $('#arrowsmall').removeClass('fa-sort-desc'); $('#arrowsmall').addClass('fa-sort-asc'); } else { $('.more').addClass('isShow'); $('#arrowsmall').removeClass('fa-sort-asc'); $('#arrowsmall').addClass('fa-sort-desc'); } }, sortFunc: function(type) { if(type == 0) { $('.sort ul li:first').addClass('active') $('.sort ul li:last').removeClass('active') } else { $('.sort ul li:last').addClass('active') $('.sort ul li:first').removeClass('active') } }, }; $(document).ready(function() { var data = {pageNo: 1,pageSize: 10}; searchModule.query(data); });
查询所有--》初始化分页插件--》分页功能实现;
4.后台代码:
由于jsonp请求只支持get请求,所以在原方法的基础上包一层,开放给jsonp方式访问,并保留原方法;
import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.alibaba.fastjson.JSONObject; @RestController @RequestMapping("/asset") public class AssetRS { public static final Logger logger = LoggerFactory.getLogger(AssetRS.class); private String tableName; private Set<String> columnIds; @Autowired private ISearchAssetBiz searchAssetBiz; @ResponseBody @RequestMapping(value = "/search",method={RequestMethod.POST,RequestMethod.GET}) public SearchAssetInfo seachAsset(HttpServletRequest request, @RequestBody SearchPageInfo page){ int pageSize = page.getPageSize(); int pageNo = page.getPageNo(); //聚类查询字段 String fieldGroup = ""; StringBuffer groups = new StringBuffer(""); List<String> fields = page.getFields(); if(fields != null){ for (String field : fields) { groups.append(",").append(field); } fieldGroup = groups.substring(1); } List<String> ids = new ArrayList<>(); ids.addAll(columnIds); int appId = page.getAppId()==null?1:Integer.valueOf(page.getAppId()); int count = searchAssetBiz.getSearchAssetCount(tableName,page.getWhereMap(),appId,ids); //分页信息 SearchPageInfo searchPage = new SearchPageInfo(); searchPage.setCount(count); searchPage.setPageNo(pageNo); searchPage.setPageSize(pageSize); List<MdiyAttrInfo> list = searchAssetBiz.getSearchGroup(tableName, page.getWhereMap(), appId, ids, StringUtils.hasText(fieldGroup) ? fieldGroup : null); for (MdiyAttrInfo mdiyAttrInfo : list) { mdiyAttrInfo.getCategory(); //去重 } List<MdiyAttrInfo> assets = searchAssetBiz.querySearchList(tableName, page.getWhereMap(), appId, ids, pageNo, pageSize, page.getSortMap()); SearchAssetInfo searchAssetInfo = new SearchAssetInfo(); searchAssetInfo.setArticles(assets); searchAssetInfo.setPage(searchPage); return searchAssetInfo; } @ResponseBody @RequestMapping(value = "/search_jsonp",method={RequestMethod.GET}) public void seachAssetJsonp(HttpServletRequest request, HttpServletResponse response, @RequestParam(required=false) int pageNo, @RequestParam(required=false) int pageSize, @RequestParam(required=false) String basictitle, @RequestParam(required=false) String Category, @RequestParam(required=false) String tag, @RequestParam(required=false) String startDate, @RequestParam(required=false) String endDate, @RequestParam(required=false) String sortField, @RequestParam(required=false) String sort){ SearchPageInfo page = new SearchPageInfo(); page.setPageNo(pageNo); page.setPageSize(pageSize); Map<String,String> whereMap = new HashMap<>(); if(StringUtils.hasText(basictitle)){ whereMap.put("basictitle", basictitle); page.setWhereMap(whereMap); } Map<String, String> sortMap = new HashMap<>(); if(StringUtils.hasText(sortField)){ sortMap.put(sortField, sort); page.setSortMap(sortMap); } List<String> list = new ArrayList<>(); list.add("Category"); list.add("Tags"); page.setFields(list); String callback = (String)request.getParameter("callback"); SearchAssetInfo info = seachAsset(request, page); String retStr = callback + "(" + JSONObject.toJSONString(info)+")"; try { response.getWriter().print(retStr); } catch (IOException e) { e.printStackTrace(); } } }
需要注意下,jsonp请求的返回值格式,直接返回json时,前台会报错的;
String retStr = callback + "(" + JSONObject.toJSONString(info)+")"; try { response.getWriter().print(retStr); } catch (IOException e) { e.printStackTrace(); }
目前的功能还不完善,后续更新吧!
添加聚类查询后的代码:
var options = { bootstrapMajorVersion: 3, currentPage: 1, //当前页 numberOfPages: 5, //显示的页数 totalPages: 2, //总页数 itemTexts: function(type, page, current) { switch(type) { case "first": return "first"; case "prev": return "pre"; case "next": return "next"; case "last": return "last"; case "page": return page; } }, onPageClicked: function(e, originalEvent, type, page) { console.log('ni dian le '+page); var data = {pageNo: page,pageSize: 10}; data.basictitle = $("#sTilte").val(); if($("#selectA").text()!=undefined){ data.category = $("#selectA").text(); } if($("#selectB").text()!=undefined){ data.member = $("#selectB").text(); } if($("#selectC").text()!=undefined){ data.tag = $("#selectC").text(); } if($("#selectD").text()!=undefined){ data.startDate = $("#selectD").text(); } data.sortField = $(".sort.active").text(); searchModule.query(data); } }; var searchModule = { items:[], queryBefore:function(){ var data = {pageNo: 1,pageSize: 10}; data.basictitle = $("#sTilte").val(); if($("#selectA").text()!=undefined){ data.category = $("#selectA").text(); } if($("#selectB").text()!=undefined){ data.member = $("#selectB").text(); } if($("#selectC").text()!=undefined){ data.tag = $("#selectC").text(); } if($("#selectD").text()!=undefined){ data.startDate = $("#selectD").text(); } data.sortField = $(".sort.active").text(); console.log('你点了queryBefore'+JSON.stringify(data)); searchModule.query(data); }, query: function(data) { console.log('查询条件:'+JSON.stringify(data)) ajax({ type: "post", url: "http://10.7.6.7:8880/ms-mcms/cms/asset/search_jsonp.do", dataType: "jsonp", data: data, timeOut: 15000, before: function() { console.log("before"); }, success: function(response) { // console.log(response); //分页 var pageinfo = response.page; options.currentPage = pageinfo.pageNo; options.totalPages = pageinfo.pageCount>0?pageinfo.pageCount:1; $('#pagination').bootstrapPaginator(options); //列表 items = response.articles; var html = ''; for(index in items){ var item = items[index]; // console.log(item) html+='<li class="row no-margin ng-scope" onclick="searchModule.click('+index+')"\ role="button" tabindex="0"><div class="pull-left item-left">\ <img class="search-thumb ng-scope" style="background: #202334 url('+item.artFrame+') no-repeat center;background-size: contain;">\ </div><div class="item-intro"><div class="row no-margin item-top">\ <div class="col-md-12 title-box ng-binding">'+item.basicTitle+'</div></div>\ <div class="row no-margin-right item-bottom">\ <div class="col-md-3 no-padding icon-gray">\ <i class="glyphicon glyphicon-user "></i>\ <span class="ng-binding">'+item.articleAuthor+'</span>\ </div></div></div>\ <div class="pull-right item-right ng-scope" >\ <div class="ng-binding">\ <i class="glyphicon glyphicon-facetime-video"></i>' +item.dateline+'</div><div style="margin-top:25px;" class="ng-binding">\ <i class="fa fa-clock-o"></i>'+item.dateline+'</div></div></li>'; } $(".topicList").html(html); searchModule.click(0); //聚类 var map = response.map; var categorys = map.Category; var tags = map.Tag; var html = ''; if(tags!=null&&tags!=undefined){ html = '<dt>Category:</dt>'; for(j = 0; j < tags.length; j++) { html+='<dd><a>'+tags[j]+'</a></dd>'; } $("#select3").html(html); } if(categorys != null && categorys!=undefined){ html = '<dt>Category:</dt>'; for(i = 0; i < categorys.length; i++) { html+='<dd><a>'+categorys[i]+'</a></dd>'; } $("#select1").html(html); } searchModule.bindClick(); }, error: function() { console.log("error"); } }); }, bindClick:function(){ $("#select1 dd").click(function () { $(this).addClass("selected").siblings().removeClass("selected"); if ($(this).hasClass("select-all")) { $("#selectA").remove(); } else { var copyThisA = $(this).clone(); if ($("#selectA").length > 0) { $("#selectA a").html($(this).text()); } else { $(".select-result dl").append(copyThisA.attr("id", "selectA")); } $("#selectA").on("click", function () { $(this).remove(); //$("#select1 .select-all").addClass("selected").siblings().removeClass("selected"); $("#select1>dd").removeClass("selected"); searchModule.queryBefore(); }); } }); $("#select2 dd").click(function () { $(this).addClass("selected").siblings().removeClass("selected"); if ($(this).hasClass("select-all")) { $("#selectB").remove(); } else { var copyThisB = $(this).clone(); if ($("#selectB").length > 0) { $("#selectB a").html($(this).text()); } else { $(".select-result dl").append(copyThisB.attr("id", "selectB")); } $("#selectB").on("click", function () { $(this).remove(); //$("#select2 .select-all").addClass("selected").siblings().removeClass("selected"); $("#select2>dd").removeClass("selected"); searchModule.queryBefore(); }); } }); $("#select3 dd").click(function () { $(this).addClass("selected").siblings().removeClass("selected"); if ($(this).hasClass("select-all")) { $("#selectC").remove(); } else { var copyThisC = $(this).clone(); if ($("#selectC").length > 0) { $("#selectC a").html($(this).text()); } else { $(".select-result dl").append(copyThisC.attr("id", "selectC")); } $("#selectC").on("click", function () { $(this).remove(); //$("#select3 .select-all").addClass("selected").siblings().removeClass("selected"); $("#select3>dd").removeClass("selected"); searchModule.queryBefore(); }); } }); $("#select4 dd").click(function () { $(this).addClass("selected").siblings().removeClass("selected"); if ($(this).hasClass("select-all")) { $("#selectD").remove(); } else { var copyThisD = $(this).clone(); if ($("#selectD").length > 0) { $("#selectD a").html($(this).text()); } else { $(".select-result dl").append(copyThisD.attr("id", "selectD")); } $("#selectD").on("click", function () { $(this).remove(); //$("#select4 .select-all").addClass("selected").siblings().removeClass("selected"); $("#select4>dd").removeClass("selected"); searchModule.queryBefore(); }); } }); $(".select dd").on("click", function () { if ($(".select-result dd").length > 1) { $(".select-no").hide(); } else { $(".select-no").show(); } searchModule.queryBefore(); }); }, click: function(index) { var item = items[index]; //console.log('你点了click'+JSON.stringify(item)); var html = ''; html+='<video controls="" width="100%" controlslist="nodownload" src="'+item.artVideo+'"></video>\ <div class="detail-module">\ <div class="detail-title ng-binding" onclick="searchModule.clickUrl(\''+item.articleUrl+'\')" role="button" tabindex="0">'+item.basicTitle+'</div>\ <a class="btn btn-primary btn-download ng-scope" onclick="searchModule.downLoad(\''+item.artVideo+'\')">DOWNLOAD</a>\ </div>\ <div class="detail-module">\ <ul class="row">\ <li class="col-md-12">\ <label>Summary</label>:\ <span class="ng-binding">'+item.basicDescription+'</span>\ </li>\ <li class="col-md-6">\ <label>Author</label>:\ <span class="ng-binding">'+item.articleAuthor+'</span>\ </li>\ <li class="col-md-6">\ <label>Dateline</label>:\ <span class="ng-binding">'+item.dateline+'</span>\ </li>\ <li class="col-md-12">\ <label>Location</label>:\ <span class="ng-binding">'+item.location+'</span>\ </li>\ <li class="col-md-12 ng-scope">\ <label>Restrictions</label>:\ <span class="ng-binding">'+item.restrictio+'</span>\ </li>\ <li class="col-md-12">\ <label>Category</label>:\ <span>\ <span class="categoryItem ng-binding ng-scope">'+item.category+'</span>\ </span>\ </li>\ <li class="col-md-6">\ <label>Tags</label>:\ <span>\ <i class="categoryItem ng-binding ng-scope">'+item.tags+'</i>\ </span>\ </li>\ </ul>\ </div>\ <div class="detail-module ng-binding" >'+item.articleContent+'</div>'; $('#v-detail').html(html); }, moreCtrl: function() { if($('.more').hasClass('isShow')) { $('.more').removeClass('isShow'); $('#arrowsmall').removeClass('fa-sort-desc'); $('#arrowsmall').addClass('fa-sort-asc'); } else { $('.more').addClass('isShow'); $('#arrowsmall').removeClass('fa-sort-asc'); $('#arrowsmall').addClass('fa-sort-desc'); } }, sortFunc: function(type) { if(type == 0) { $('.sort ul li:first').addClass('active') $('.sort ul li:last').removeClass('active') } else { $('.sort ul li:last').addClass('active') $('.sort ul li:first').removeClass('active') } }, downLoad:function(url){ console.log('你点了下载按钮:'+url); }, clickUrl:function(url){ console.log('你点了详情按钮:'+url); }, }; $(document).ready(function() { var data = {pageNo: 1,pageSize: 10}; searchModule.query(data); });
*{margin:0;padding:0;list-style-type:none;} a,img{border:0;} .select{padding:5px 5px;border:#ddd 1px solid;border-radius:4px; margin-top:5px;font-size:12px} .select li{list-style:none;padding:10px 0 5px 70px;border:#ddd 1px solid;} .select .select-list{border-bottom:#eee 1px dashed} .select dl{zoom:1;position:relative;line-height:24px;} .select dl:after{content:" ";display:block;clear:both;height:0;overflow:hidden} .select dt{width:100px;margin-bottom:5px;position:absolute;top:0;left:-100px;text-align:right;color:#666;height:24px;line-height:24px} .select dd{float:left;display:inline;margin:0 0 5px 5px;} .select a{display:inline-block;white-space:nowrap;height:24px;padding:0 10px;text-decoration:none;color:#039;border-radius:2px;} .select a:hover{color:#f60;background-color:#f3edc2} .select .selected a{color:#fff;background-color:#f60} .select-result dt{font-weight:bold; margin-top: -1px;margin-left: 5px;} .select-no{color:#999} .select .select-result a{padding-right:20px;background:#f60 url("close.gif") right 9px no-repeat} .select .select-result a:hover{background-position:right -15px} .select-result{ padding-bottom: 20px !important; }
<div class="condition"> <div class="text-center single-box"> <div class="search-box"> <i class="glyphicon glyphicon-search" onclick="searchModule.queryBefore()" role="button" tabindex="0"></i> <input type="text" placeholder="Please input what you want to inquire about" id="sTilte" onkeyup="searchModule.queryBefore()" class="ng-pristine ng-untouched ng-valid ng-empty" aria-invalid="false"> </div> <a href="javascript:;" onclick="searchModule.moreCtrl()"> Advanced<i id='arrowsmall' class="fa fa-sort-desc"></i> </a> </div> <div class="more isShow"> <ul class="select"> <li class="select-list"> <dl id="select1"> <dt>Category:</dt> <dd><a>Politics</a></dd> </dl> </li> <li class="select-list"> <dl id="select2"> <dt>Members:</dt> <dd class=""><a>cctv</a></dd> </dl> </li> <li class="select-list"> <dl id="select3"> <dt>Tag:</dt> <dd class=""><a>Cooperation</a></dd> </dl> </li> <li class="select-list"> <dl id="select4"> <dt>Dateline:</dt> <dd class=""><a>today</a></dd> <dd class=""><a>week</a></dd> <dd class=""><a>month</a></dd> </dl> </li> <li class="select-result"> <dl> <dt>current:</dt> <dd class="select-no"></dd> </li> </ul> </div> </div>