Bootstrap's native table components can only meet simple data display and cannot meet more operational requirements. Of course, you can find a bootstrap-based table component called "DataTables-1.10.11", but if you don't know much about the API, it will be painful to use. However, if you choose to use jqGrid, then this tutorial will bring you a solution to this operational table.
1. Effect display
OK, just show this picture. I believe you have fallen in love with the bootstrap version of jqGrid. It is very compatible with bootstrap and is simply perfect. Of course, this requires some changes to the jqGrid of the reason and to encapsulate the components.
2. Resource download
Anyway, I love sharing. You can download the component code of jqGrid from the official website of jqGrid, but it requires some changes after downloading. Then I directly upload the modified jqGrid to git, and you only need to import the provided files to your corresponding project.
In addition, you also need to download a jquery-ui-1.10.0.custom.css , and I will not provide the download address, but I believe you will definitely find it. Even if you use a Baidu girl who frequently has accidents, you can find it.
3. What is this article about
Since I created the QQ group, some students have joined the group in a "incessant stream", but I also found that the first step in the group came to me to ask for demo or project code. I don't like this. Do it yourself, implement the following and transform it, and it will be your own thing. You will obviously not get more help from copying my code completely. I hope that the above students will take the initiative when they are studying.
After saying the above little nonsense, let's get back to the point and talk about what our blog mainly talks about and what is the key to embedding jqGrid in bootstrap. I summarize it as follows:
jqGrid layout scheme in bootstrap jqGrid's own constructive parameters jqGrid's modular jqGrid data operation in bootstrap
It is tentatively divided into the above parts to illustrate, but it must be noted that due to space limitations, only ideas and some code are provided in the blog.
①. The layout plan of jqGrid in bootstrap
<!DOCTYPE html><html lang="zh-CN"><%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%@ include file="/components/common/taglib.jsp"%><%@ include file="/components/common/csslib.jsp"%><head><link type="text/css" rel="stylesheet" href="${ctx}/css/deal/my_pay_list.css" /></head><body> <div> <form id="jqgridForm" role="form" action="${ctx}/deal/dataablePayDealOrdersList" method="post"> <div> <div> <label for="name">Project name: </label> <div> <input type="text" name="name" id="name" placeholder="Please enter the project name" value="" /> </div> </div> </div> <div> <div> <button type="button" id="searchBtn">Search</button> </div> </div> <div> <div> <table id="pageGrid" rel="jqgridForm"></table> <div id="pageGridPager"></div> </div> </form></div><%@ include file="/components/common/jslib.jsp"%><script type="text/javascript" src="${ctx}/js/deal/my_pay_list.js"></script></body></html>The projects of each person vary greatly. In the listed code, we only focus on the jqGrid section:
id="jqgridForm" , here we have a form form with a layer of search conditions for jqGrid, that is, the search part listed on the renderings. When clicking the search button, the query conditions of the form form field are submitted to the controller, and the data obtained is obtained. id="searchBtn" , defines the search button, which will be used later on modularity. <table id="pageGrid" rel="jqgridForm"></table> <div id="pageGridPager"></div> Define the table element of jqGrid and the footer element of jqGrid. The rules used by my project temporarily agrees with this rule, and you can also have your own rules. By specifying the id of the form by rel, it is easier to use table retrieval form. ②, jqGrid's own constructive parameters
Constructing the parameters, I extracted them into my_pay_list.js in ①.
$(function() { var jqOption = { datatype : "xml", mtype : "POST", shrinkToFit : true, viewrecords : false, rownumbers : false, autowidth : true, height : "100%", colNames : [ 'id', 'status', 'project information', 'project status', 'order number', 'project name', 'order time', 'payment amount', 'supported quantity', 'order status', 'operation'], colModel : [ { name : 'id', index : 'id', hidden : true }, { name : 'status', index : 'status', hidden : true }, { name : 'image_str', index : 'image_str', width : 140, resizable : false, sortable : false, formatter : function(cellvalue, options, rowObject) { if (cellvalue == 'Total payment cost:') { return cellvalue; } }, align : 'left' }, { name : 'oper', index : 'oper', width : 90, resizable : false, sortable : false, align : 'center', formatter : function(cellvalue, options, rowObject) { var status = parseInt($(rowObject).find("status").text()); var id = $(rowObject).find("id").text(); if (status == 0) { return '<a target="dialog" href="' + common.ctx + '/deal/initPayg/' + id + '">Go to pay</a>'; } if (status == 1 || status == 3) { return '<a target="_blank" href="' + common.ctx + '/deal/showDealOr/' + id + '">View details</a>'; } if (status == 2) { return '<a target="ajaxTodo" href="' + common.ctx + '/deal/receivder/' + id + '">Confirm the receipt</a>'; } }, } ], xmlReader : { repeatitems : false, root : "PageGrid", row : "map", page : 'page', total : 'total', records : 'records', id : 'ID' }, rowNum : 50, rowList : [ 50, 100, 200, 300 ], pager : "#pageGridPager", footerrow : true, loadError : YUNM.ajaxError, gridComplete : function() { var $form = $("#" + $("#pageGrid").attr("rel")); $.ajax({ type : $form.method || 'POST', url : common.ctx + "/deal/getAllOrded", data : $form.serializeArray(), dataType : "json", cache : false, success : function(json) { $("#pageGrid").footerData("set", { image_str : "Total payment cost:", order_price : json.message }); }, error : YUNM.ajaxError }); if ($.fn.ajaxTodo) { $("a[target=ajaxTodo]", $("#pageGrid")).ajaxTodo(); } // dialog if ($.fn.ajaxTodialog) { $("a[target=dialog]", $("#pageGrid")).ajaxTodialog(); } }, }; initEnv(jqOption);});Students who are not familiar with jqGrid at all recommend reading jqGrid's demo and the official documents of jqGrid first. Of course, for students who are already familiar with jqGrid, doc and demo are definitely a must-read.
There are many attributes listed in the above file. I will not introduce jqGrid too much. The main theme of this article is to introduce how to embed jqGrid into bootstrap. The key point is not to introduce jqGrid. I will only introduce a few key points:
formatter: function(cellvalue, options, rowObject) { , formatters are still very often used, so it is very important to get the value of the corresponding cell. My jqGrid uses the xml (datatype: "xml") data format, so you can find the value of the corresponding deal_id column through $(rowObject).find("deal_id").text() . xmlReader : { repeatitems : false, root : "PageGrid", , pay attention to the parameter values in xmlReader. In the next introduction, the data operation of jqGrid will be introduced in detail, which is related to the background XML data encapsulation. $("#pageGrid").footerData("set", {image_str : "支付总花费:", order_price : json.message}); , Regarding the footerData method, it is also very convenient to use, and the effect can be referenced as the effect. initEnv(jqOption); method. After the page onload, we pass the initialization parameters of jqGrid to the initEnv method. In the subsequent modularization of jqGrid in bootstrap, we will introduce the initEnv method. ③, jqGrid modularity in bootstrap
In ②, we noticed the initEnv method, so this method is built with modular encapsulation work specifically for jqGrid.
initEnv method
function initEnv(jqOption) { $(window).resize(function() { initLayout(); }); initUI(null, jqOption);}In this method, we will see the initLayout method and the initUI method, and the specific content will be introduced later.
initLayout
function initLayout() { $("table[rel=jqgridForm]").each(function() { var rel = $(this).attr("rel"); if (rel) { var $form = $("#" + rel); var tableWidth = $form.width(); $(this).setGridWidth(tableWidth, true); } });}That is, when the window is scaled, we re-draw the width for jqGrid to adapt to bootstrap's responsive layout. The method used is the setGridWidth method of jqGrid.
initUI
function initUI(_box, jqOption) { var $p = $(_box || document); if (jqOption) { YUNM.debug("Initializejqgrid"); var $form = $("#" + $("#pageGrid").attr("rel")); YUNM.debug(YUNM.array2obj($form.serializeArray())); // Initialize var op = $.extend({ url : $form.attr("action"), postData : YUNM.array2obj($form.serializeArray()), }, jqOption); $("#pageGrid").jqGrid(op); // Search button $("#searchBtn", $form).click(function() { $("#pageGrid").jqGrid('setGridParam', { url : $form.attr("action"), page : 1, postData : YUNM.array2obj($form.serializeArray()), }); $("#pageGrid").trigger("reloadGrid"); }); // toolbar, remove the rounded corner of the button $(".btn", $form).each(function() { var $this = $(this); $this.css({ "border-radius" : "0px", "border-bottom" : "0", }); }); }}array2obj : function(array) { var params = $({}); $.each(array, function(i) { var $param = $(this)[0]; params.attr($param.name, $param.value); }); return params[0];},If you have read my previous series of articles, you will not be too unfamiliar with the initUi method. Friends who are familiar with dwz will naturally not be unfamiliar with it. Most of the templates in my project still rely on dwz. Thank you to these seniors.
var $form = $("#" + $("#pageGrid").attr("rel")); Since we associate the form form form condition on jqGrid, we can get the form form object here. When we get the form form object, we will naturally get the value of the search field ( $form.serializeArray() ). After you get the search field value of the form form, you need to do some processing at this time. We know that when jqGrid passes parameters to controller, it is necessary to send paging and sorted related fields (page, rows, sord, sidx). The method used is $("#pageGrid").jqGrid({postData:xxx}); Normally, when we send form forms, we only need to use $form.serializeArray() , but if at this time, just replace xxx with $form.serializeArray() , then the paging and sorted related fields (page, rows, sord, sidx) will not be obtained in controller. This is a conflict. How to deal with it at this time? The solution is to objectify the form form data (array2obj method), and then we send the value of the search field together with the relevant fields of paging and sorting to the controller through var op =$.extend({url:$form.attr("action"),postData:YUNM.array2obj($form.serializeArray()),},jqOption);$("#pageGrid").jqGrid(op); $("#searchBtn", $form).click reloads jqGrid's data by encapsulating the click event. $(".btn", $form).each(function() { The method here will retrieve the button to de-round the corners to make it more suitable for jqGrid, see the renderings. ④, jqGrid's data operation
The data operation part, I think, includes search parameter passing, pagination sort parameter passing, and writing of SQL statements.
Regarding parameter passing, the front-end parameter encapsulation has been introduced in ③. Let’s take a look at how the data is processed in the controller.
First, let’s define PageGrid, which is the data source of xmlReader in jqGrid.
package com.honzh.common.page;import java.util.List;import com.thoughtworks.xstream.annotations.XStreamAlias;@XStreamAlias("pageGrid")@SuppressWarnings("rawtypes")public class PageGrid { private int page; private int total; private int records; private List data; public int getPage() { return this.page; } public void setPage(int page) { this.page = page; } public int getTotal() { return this.total; } public void setTotal(int total) { this.total = total; } public int getRecords() { return this.records; } public void setRecords(int records) { this.records = records; } public List getData() { return this.data; } public void setData(List data) { this.data = data; }}xstream.jar is required in the project and download it yourself.
XStreamComponent.java
package com.honzh.common.page;import org.apache.commons.lang.StringUtils;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.converters.Converter;import com.thoughtworks.xstream.io.xml.DomDriver;import com.thoughtworks.xstream.mapper.DefaultMapper;import com.thoughtworks.xstream.mapper.XStream11XmlFriendlyMapper;public class XStreamComponent { private XStream xstream; public static XStreamComponent newInstance() { XStreamComponent xmlComponent = new XStreamComponent(); xmlComponent.alias(new Class[] { PageGrid.class }); return xmlComponent; } public XStreamComponent() { this.xstream = new XStream(new DomDriver()); } public String toXML(Object obj) { return this.xstream.toXML(obj); } public String toPageXML(Object obj) { registerConverter(new MapCustomConverter(new DefaultMapper(XStream11XmlFriendlyMapper.class.getClassLoader()))); return toXML(obj); } public Object fromPageXML(String xml) { registerConverter(new MapCustomConverter(new DefaultMapper(XStream11XmlFriendlyMapper.class.getClassLoader()))); return fromXML(xml); } public Object fromXML(String xml) { return this.xstream.fromXML(xml); } @SuppressWarnings("rawtypes") public void processAnnotations(Class type) { this.xstream.processAnnotations(type); } @SuppressWarnings("rawtypes") public void processAnnotations(Class[] types) { this.xstream.processAnnotations(types); } @SuppressWarnings("rawtypes") public void alias(String name, Class type) { this.xstream.alias(name, type); } @SuppressWarnings("rawtypes") public void alias(Class[] types) { for (Class type : types) { String className = type.getName(); try { String[] classNames = StringUtils.split(className, "."); this.xstream.alias(classNames[(classNames.length - 1)], type); } catch (Exception ex) { this.xstream.alias(className, type); } } } public void registerConverter(Converter converter) { this.xstream.registerConverter(converter); } @SuppressWarnings("rawtypes") public void useAttributeFor(Class definedIn, String fieldName) { this.xstream.useAttributeFor(definedIn, fieldName); }}The pageGrid is mainly encapsulated as an XML object, and then passed to the front end.
MapCustomConverter.java
package com.honzh.common.page;import java.util.HashMap;import java.util.Hashtable;import java.util.Iterator;import java.util.Map;import com.thoughtworks.xstream.converters.MarshallingContext;import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;import com.thoughtworks.xstream.io.HierarchicalStreamReader;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;import com.thoughtworks.xstream.mapper.Mapper;public class MapCustomConverter extends AbstractCollectionConverter { public MapCustomConverter(Mapper mapper) { super(mapper); } @SuppressWarnings("rawtypes") public boolean canConvert(Class type) { return (type.equals(HashMap.class)) || (type.equals(Hashtable.class)) || (type.getName().equals("java.util.LinkedHashMap")) || (type.getName().equals("sun.font.AttributeMap")); } @SuppressWarnings({ "rawtypes" }) public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Map map = (Map) source; for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); writer.startNode(entry.getKey() == null ? "null" : entry.getKey().toString()); writer.setValue(entry.getValue() == null ? "" : entry.getValue().toString()); writer.endNode(); } } @SuppressWarnings("rawtypes") public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map map = (Map) createCollection(context.getRequiredType()); populateMap(reader, context, map); return map; } @SuppressWarnings({ "rawtypes", "unchecked" }) protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext context, Map map) { while (reader.hasMoreChildren()) { reader.moveDown(); Object key = reader.getNodeName(); Object value = reader.getValue(); map.put(key, value); reader.moveUp(); } }}It mainly converts hashmap retrieved in the database into standard XML format data.
BaseConditionVO.java
package com.honzh.common.persistence;import java.util.HashMap;import java.util.Map;import org.apache.ibatis.session.RowBounds;/** * Parameter setting class during pagination query.<br> * * <P> * 1.PAGE_SHOW_COUNT──Of course, the default page displays 10. <br> * 2.pageNum──Which page. <br> * 3.numPerPage─-How much does a page show? When it is empty, PAGE_SHOW_COUNT is displayed. <br> * 4.totalCount──Total number. totalCount/numPerPage=How many pages<br> * 5.orderField──Sorted columns. <br> * 6.orderDirection──The direction of sorting. * </P> */public class BaseConditionVO { public final static int PAGE_SHOW_COUNT = 50; private int pageNum = 1; private int numPerPage = 0; private long totalCount = 0; private String orderField = ""; private String orderDirection = ""; /** * @Fields ps: Encapsulate parameter types. */ private Map<String, Object> mo = new HashMap<String, Object>(); public int getPageNum() { return pageNum; } public void setPageNum(int pageNum) { this.pageNum = pageNum; } public int getNumPerPage() { return numPerPage > 0 ? numPerPage : PAGE_SHOW_COUNT; } public void setNumPerPage(int numPerPage) { this.numPerPage = numPerPage; } public String getOrderField() { return orderField; } public void setOrderField(String orderField) { this.orderField = orderField; } public String getOrderDirection() { return "desc".equals(orderDirection) ? "desc" : "asc"; } public void setOrderDirection(String orderDirection) { this.orderDirection = orderDirection; } public long getTotalCount() { return totalCount; } public void setTotalCount(long totalCount) { this.totalCount = totalCount; } public int getStartIndex() { int pageNum = this.getPageNum() > 0 ? this.getPageNum() - 1 : 0; return pageNum * this.getNumPerPage(); } public RowBounds createRowBounds() { RowBounds ro = new RowBounds(this.getStartIndex(), this.getNumPerPage()); return ro; } /** * @Title: addParams * @Description: Add query conditions* @param key * @param value */ public void addParams(String key, Object value) { this.getMo().put(key, value); } /** * @Title: getParams * @Description: Get query conditions* @param key * @return */ public Object getParams(String key) { return this.getMo().get(key); } /** * @return the mo */ public Map<String, Object> getMo() { return mo; } /** * @param mo * the mo to set */ public void setMo(Map<String, Object> mo) { this.mo = mo; } @Override public String toString() { return "condition:" + pageNum + "," + numPerPage + "," + totalCount + "," + orderField + "," + orderDirection + "," + mo; }}The query data object for pagination, including paging, sorting, and search fields.
protected BaseConditionVO getBaseConditionVOForTable() { BaseConditionVO vo = new BaseConditionVO(); //Pagination parameters int currentPage = getParaToInt("page"); int sizes = getParaToInt("rows"); String sortOrder = getPara("sord"); String sortCol = getPara("sidx"); vo.setNumPerPage(sizes); vo.setPageNum(currentPage); vo.setOrderField(sortCol); vo.setOrderDirection(sortOrder); return vo; } Convert the parameters passed by jqGrid to BaseConditionVO pagination query object. protected void renderXml(HttpServletResponse res, String xmlResponse) { try { res.setCharacterEncoding("UTF-8"); res.setHeader("Content-type", "text/xml"); PrintWriter out = res.getWriter(); out.print(xmlResponse); if (out != null) { out.close(); } } catch (IOException e) { logger.error(e.getMessage()); logger.error(e.getMessage(), e); } }Writes xml to the output stream.
After defining these basic objects, we will start to obtain and pass data.
@SuppressWarnings("rawtypes") @RequestMapping(value = "datablePayDealOrdersList") public void dataPayDealOrdersList(HttpServletResponse response) { try { logger.debug("get the order I paid"); XStreamComponent xstreamComponent = XStreamComponent.newInstance(); // Get list parameters BaseConditionVO vo = getBaseConditionVOForTable(); vo.addParams("name", getPara("name")); logger.debug("I paid order query" + vo); // The project I created List myDealOrders = dealOrderService.getByIssueUid(vo, vo.createRowBounds()); Long count = dealOrderService.searchIssueTotalCount(vo); String xmlResponse = xstreamComponent.toPageXML(createPageGrid(myDealOrders, vo, count.intValue())); renderXml(response, xmlResponse.replaceAll("__", "_")); } catch (UncategorizedSQLException e) { logger.error(e.getMessage()); logger.error(e.getMessage(), e); renderXml(response, Constants.QUERY_ERROR); } catch (Exception e) { logger.error(e.getMessage()); logger.error(e.getMessage(), e); renderXml(response, Constants.SERVER_ERROR); } }Let's explain in detail:
1. XStreamComponent.newInstance() creates an xml stream object.
2. BaseConditionVO vo = getBaseConditionVOForTable(); Create a pagination query parameter object.
3. vo.addParams("name", getPara("name")); Put the value of the search field into the query object.
4. dealOrderService.getByIssueUid(vo, vo.createRowBounds()); The pagination query method of mybatis is super simple. A friend in the group used to specially create a pagination component of mybatis. I think using the original mybatis query method is more efficient. After that, we will write the corresponding sql writing method of xml in mybatis.
5. renderXml(response, xmlResponse.replaceAll("__", "_")); Write data to the out output stream of jsp.
Finally, let’s introduce how to obtain paging data through mybatis.
mappper.java
package com.honzh.biz.database.mapper;import java.math.BigDecimal;import java.util.HashMap;import java.util.List;import org.apache.ibatis.session.RowBounds;import com.honzh.common.persistence.BaseConditionVO;public interface DealOrderMapper { @SuppressWarnings("rawtypes") List<HashMap> getByIssueUid(BaseConditionVO vo, RowBounds createRowBounds);}The two objects that you want to pass mapper.xml are BaseConditionVO and paging RowBounds. SQL in xml will automatically paging.
mappper.xml
<select id="getByIssueUid" resultType="hashmap" parameterType="map"> select * from daa WHERE is_delete=0 <if test="mo.name != null and mo.name != ''"> and y.name like CONCAT('%','${mo.name}','%') </if> <choose> <when test="orderField != null and orderField !=''"> ORDER BY ${orderField} <if test="orderDirection != null and orderDirection != ''">${orderDirection}</if> </when> <otherwise> order by d.order_time DESC </otherwise> </choose> </select>You can never pay attention to RowBounds, mybatis will automatically encapsulate limit for you. The name of the search domain can be obtained directly through mo.name or. OrderField and orderDirection are also passed.
So far, the entire Bootstrap embedded in jqGrid has ended successfully. OK, make your table awesome!