关于这个问题,我在网上搜了一下,大多都是引用jqGrid的文档中的filterGrid方法。但这个方法不是真正意义的自定义查询。
这篇文章主要讲述一下真正意义的自定义查询,顺便简单聊聊jqGrid源码中查询的实现方法。
在这个系列的第五篇中(http://blog.youkuaiyun.com/gengv/archive/2010/07/10/5725172.aspx )曾经讲到了如何进行数据查询。其中提到了一种“自定义查询”,这是jqGrid的文档中给出的一种所谓的自定义查询。其实就是把工具条查询,从Grid表格中移出来,然后可以控制输入框的横向或纵向排列,构成一种看似表单查询的查询方式。但是这种方式也有一定的局限性,比如不能以colModel中不存在的列作为查询字段进行查询,不能做到真正意义上的自定义表单布局等等。
昨天,优快云的一位网友gaile给我的博客留言:
还有,假如我想自定义serach弹出框的格式该怎么做呢?
他的问题实际上就是:如何实现自定义的表单查询。正好最近我也在考虑这个问题,昨晚特意查看了一下jqGrid的源码,顺便总结出这篇文章。
1. 查询的实现思路:
通过前面文章的介绍,可以知道jqGrid在查询中主要的任务是向Server发送有关于查询的参数,主要的参数包括search、searchField、searchOper、searchString、filters等(这些参数名可以通过选项进行更改,可以参考之前的文章);Server得到参数后,进行后台数据查询,然后将结果发回客户端;jqGrid根据jsonReader的设置,解析返回的结果并显示到Grid表格中。
大体思路就是这样。既然如此,我们要想实现真正的自定义查询,要解决的无非就是如何把有关查询的参数“塞进”请求里。需要用到两个jqGrid选项:
- postData :可以参考本系列第九篇(http://blog.youkuaiyun.com/gengv/archive/2010/07/14/5735654.aspx);当jqGrid向Server发送请求的时候,总是会将postData中的{name:value,...}对,作为参数一同传递到服务器端。因此我们只需要在发送查询请求的时候,将关于查询的参数放进postData就可以了。
- search :这个jqGrid选项,在jqGrid的官方文档中并没有列出来,但确实存在;它的值可以为true或false,用来控制是否为查询请求,如果为true,则在请求中加入search=true参数;可以使用jqGrid的setGridParam方法修改它。
注1 :search选项传入的这个search=true参数,其参数名称“search”是由jqGrid的prmNames选项中设置的,默认为“_search”,此处是为了Server端java程序中成员变量命名方便,而设置为“search”了(具体细节可以参看本系列第一篇http://blog.youkuaiyun.com/gengv/archive/2010/07/05/5714834.aspx )。
注2 :search选项传入请求的search参数值,优先于postData中的同名参数值。即如果serach选型值为flase,而postData中有个名值对为{search:true},则请求为http://...action?search=false&...&search=true。此时第一个search参数(由search选项控制)将使第二个search参数(postData提供)被忽略。
2. 自定义单条件查询的实现
根据之前文章的介绍,当单条件查询发送请求时,参数应为如下形式:
- nd:1278688214496
- page:1
- rows:15
- search:true//(1)
- searchField:id//(2)
- searchOper:ge//(3)
- searchString:9000//(4)
- sidx:
- sord:asc
其中(1)(2)(3)(4)是我们最关注的。
下面看看如何来实现单条件自定义查询:
HTML部分:
- ...
- <body>
- <h3>
- jqGrid测试09
- </h3>
- <divid="mysearch"></div>
- <br/>
- <div>
- <tableid="gridTable"></table>
- <divid="gridPager"></div>
- </div>
- <br/>
- <div>
- <buttononclick="openSingleSearchDialog()">自定义单条件查询</button>
- <buttononclick="clearSearch()">重置</button>
- </div>
- <divid="singleSearchDialog">
- <tableclass="formTable">
- <thead>
- <tr>
- <th>查询条件</th>
- <th>查询方式</th>
- <th>查询值</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <inputtype="radio"name="searchField"value="id">编码</input><br/>
- <inputtype="radio"name="searchField"value="lastName">姓</input><br/>
- <inputtype="radio"name="searchField"value="firstName">名</input>
- </td>
- <td>
- <selectid="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"id="searchString"></input>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </body>
- ...
HTML中增加了一个自定义的<div>,singleSearchDialog;而且使用radio组来选择查询字段。
再看看javascript部分:
- $(function(){
- $("#gridTable").jqGrid({
- ...
- prmNames:{search:"search"},
- ...
- });
- $("#singleSearchDialog").dialog({//(1)创建自定义查询对话框
- autoOpen:false,
- modal:true,
- resizable:true,
- width:350,
- title:"自定义单条件查询",
- buttons:{
- "查询":singleSearch//(2)在对话框中添加查询按钮
- }
- });
- });
- varopenSingleSearchDialog=function(){
- $("#singleSearchDialog").dialog("open");
- };
- varresetSingleSearchDialog=function(){
- $("radio","#singleSearchDialog").attr("checked",false);
- $(":text","#singleSearchDialog").val("");
- };
- varsingleSearch=function(){
- varsdata={//(3)构建查询需要的参数
- searchField:$(":radio:checked","#singleSearchDialog").val(),
- searchString:$("#searchString","#singleSearchDialog").val(),
- searchOper:$("#searchOper","#singleSearchDialog").val()
- };
- //(4)获得当前postData选项的值
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- //(5)将查询参数融入postData选项对象
- $.extend(postData,sdata);
- $("#gridTable").jqGrid("setGridParam",{
- search:true//(6)将jqGrid的search选项设为true
- }).trigger("reloadGrid",[{page:1}]);//(7)重新载入Grid表格,以使上述设置生效
- $("#singleSearchDialog").dialog("close");
- };
- varclearSearch=function(){
- varsdata={//(8)构建一套空的查询参数
- searchField:"",
- searchString:"",
- searchOper:""
- };
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- $.extend(postData,sdata);//(9)将postData中的查询参数覆盖为空值
- $("#gridTable").jqGrid("setGridParam",{
- search:false//(10)将jqGrid的search选项设为false
- }).trigger("reloadGrid",[{page:1}]);
- resetSingleSearchDialog();
- };
说明:
- (7)处设置完jqGrid参数后,重新载入表格时,jqGrid将会将postData中的参数加进请求参数中,因此将提交查询请求;在显式清除postData中的参数之前,每次查询都会带着这些查询参数。
- (9)处将postData中的关于查询的参数全都覆盖为空值,重新载入表格后,请求中将不再传入“有效”的查询参数,即有查询参数名,但无查询参数值。例如: http://localhost:8085/JqGridTest/jqGridTest/jqGrid05.action?search=false &nd=1279865475529&rows=15&page=2&sidx=&sord=asc&searchField=&searchString=&searchOper=
注意:
不论在查询或者清除查询之后条用trigger("reloadGrid"),都应该带上“[{page:1}]”,以设置page选项为1,即:
如果不加上这个参数的话,那么后续的查询中,page都始终保持之前操作时保存的值,对于后面再进行的查询很能产生副作用。举个例子来说,第一次查询使用查询条件A,如果满足条件的数据有110行,Grid中每页显示20行(即每次从Server最多返回20行),则总共有6页数据;这时翻页,一直翻到第4页;然后使用查询条件B再次进行查询,假设这次查询满足条件的数据总共只有35行,每页显示20行,也即只有2页数据,那么如果page的值还是4的话,则后台就找不到数据了。因此,应该使用trigger("reloadGrid", [{page:1}]);,这样总能保证,即使查询结果只有1页数据,也能被显示出来。
3. 自定义多条件查询的实现
多条件查询与但条件查询的不同之处即在于使用filters参数,代替了searchField,searchOper,searchString参数。其参数形式为:
- filters:{
- "groupOp":"AND",
- "rules":[
- {"field":"id","op":"le","data":"1500"},
- {"field":"lastName","op":"bw","data":"LN-3"},
- {"field":"email","op":"cn","data":"sy"}
- ]
- }
- nd:1278688110637
- page:1
- rows:15
- search:true
- sidx:
- sord:asc
因此具体实现也与上述大同小异,只要加入正确的filters参数就行了。
首先看HTML部分,加入了multipleSearchDialog。
- ...
- <div>
- ...
- <buttononclick="openMultipleSearchDialog()">自定义复杂条件查询</button>
- </div>
- ...
- <divid="multipleSearchDialog">
- <tableclass="formTable">
- <thead>
- <tr>
- <th>查询条件</th>
- <th>查询方式</th>
- <th>查询值</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- ...
再来看javascript部分:
- $(function(){
- $("#gridTable").jqGrid({
- ...
- });
- ...
- $("#multipleSearchDialog").dialog({
- autoOpen:false,
- modal:true,
- resizable:true,
- width:350,
- title:"自定义多条件查询",
- buttons:{
- "查询":multipleSearch
- }
- });
- });
- ...
- varopenMultipleSearchDialog=function(){
- $("#multipleSearchDialog").dialog("open");
- };
- varmultipleSearch=function(){
- varrules="";
- $("tbodytr","#multipleSearchDialog").each(function(i){//(1)从multipleSearchDialog对话框中找到各个查询条件行
- varsearchField=$(".searchField",this).val();//(2)获得查询字段
- varsearchOper=$(".searchOper",this).val();//(3)获得查询方式
- varsearchString=$(".searchString",this).val();//(4)获得查询值
- if(searchField&&searchOper&&searchString){//(5)如果三者皆有值且长度大于0,则将查询条件加入rules字符串
- rules+=',{"field":"'+searchField+'","op":"'+searchOper+'","data":"'+searchString+'"}';
- }
- });
- if(rules){//(6)如果rules不为空,且长度大于0,则去掉开头的逗号
- rules=rules.substring(1);
- }
- //(7)串联好filtersStr字符串
- varfiltersStr='{"groupOp":"AND","rules":['+rules+']}';
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- //(8)将filters参数串加入postData选项
- $.extend(postData,{filters:filtersStr});
- $("#gridTable").jqGrid("setGridParam",{
- search:true//(9)将jqGrid的search选项设为true
- }).trigger("reloadGrid",[{page:1}]);//(10)重新载入Grid表格
- $("#multipleSearchDialog").dialog("close");
- };
OK,这样就实现了自定义多条件查询。同时可以看到,colModel中根本不存在身份证号(idCardNo)这一列,但是在查询中依然可以使用这个字段进行查询。
另一方面,由于自己把握了查询参数的控制权,jqGrid的multipleSearch选项将变得无足轻重了。上面的两个例子,仅作说明之用,从外观上来看,还比较粗糙。但已经从根本上控制住了查询的实现。即使在复杂的页面设计,也不会影响查询的实现了。这与jqGrid官方文档中提供的“自定义查询”,是有本质区别的。
对于页面布局的美化,以及动态添加查询条件行等等,只要基本的CSS+JavaScript知识就能实现,因此不再本文的讨论内容中了。
4. jqGrid源码
jqGrid的源码可以从以下链接得到:
http://github.com/tonytomov/jqGrid
关于查询这部分的代码,可以查看:
http://github.com/tonytomov/jqGrid/blob/master/js/grid.formedit.js
片段1:
- $t.SearchFilter=$("#"+fid).searchFilter(fields,{groupOps:p.groupOps,operators:oprtr,onClose:hideFilter,resetText:p.Reset,searchText:p.Find,windowTitle:p.caption,rulesText:p.rulesText,matchText:p.matchText,onSearch:searchFilters,onReset:resetFilters,stringResult:p.stringResult,ajaxSelectOptions:$.extend({},$.jgrid.ajaxOptions,$t.p.ajaxSelectOptions||{}),clone:p.cloneSearchRowOnAdd});
片段2:
- functionsearchFilters(filters){
- varhasFilters=(filters!==undefined),//(1)
- grid=$("#"+$t.p.id),sdata={};
- if(p.multipleSearch===false){//(2)
- sdata[p.sField]=filters.rules[0].field;
- sdata[p.sValue]=filters.rules[0].data;
- sdata[p.sOper]=filters.rules[0].op;
- }else{
- sdata[p.sFilter]=filters;//(3)
- }
- grid[0].p.search=hasFilters;//(4)
- $.extend(grid[0].p.postData,sdata);//(5)
- grid.trigger("reloadGrid",[{page:1}]);//(6)
- if(p.closeAfterSearch){hideFilter($("#"+fid));}//(7)
- }
- functionresetFilters(op){
- varreload=op&&op.hasOwnProperty("reload")?op.reload:true;
- grid=$("#"+$t.p.id),sdata=[];
- grid[0].p.search=false;
- if(p.multipleSearch===false){
- sdata[p.sField]=sdata[p.sValue]=sdata[p.sOper]="";
- }else{
- sdata[p.sFilter]="";
- }
- $.extend(grid[0].p.postData,sdata);
- if(reload){
- grid.trigger("reloadGrid",[{page:1}]);
- }
- if(p.closeAfterReset){hideFilter($("#"+fid));}
- }
我没有从头到尾通读所有代码,没有时间,也没有那么高水平,呵呵~
但从上面片段1中可以看出来:
在查询对话框中点击“查询”按钮,则执行searchFilters方法;而点击“重置”按钮,则执行resetFilters方法。
再看看searchFilters方法:
- (1)首先判断是否存在filters;
- (2)如果multipleSearch选项为false,则构建以sField、sOper、sValue所对应的参数名为内容的参数(默认的参数名为:searchField、searchOper、searchString);
- (3)如果multipleSearch选项为true,则构建sFilter对应的参数名的参数(默认为filters);
- (4)然后将查询参数置入postData选项;
- (5)如果存在filters,则将jqGrid的search选项设为true;这与$("#gridTable").jqGrid("setGridParam", { search: true });是一样的。
- (6)重新载入表格数据(发送包含查询参数的请求);
- (7)如果closeAfterSearch为true,则查询完毕后,关闭查询对话框。
总体看来,与之前自定义的条件查询实现基本一样,所不同的是,这里把单条件和多条件查询和在一起了。
而resetFilters方法,与之前自定义的实现,思路也基本一样,不再赘述。由于searchFilters和resetFilters是定义在searchGird方法中的函数,因此在外部调用时,还是有点困难的。
附上本文完整前端代码(后台代码可以参考之前的文章):
HTML部分:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" lang="zh-CN"> <head> <title>jqGrid09</title> <link rel="stylesheet" type="text/css" media="screen" href="../css/themes/aero/jquery-ui-1.8.1.custom.css" mce_href="css/themes/aero/jquery-ui-1.8.1.custom.css" /> <link rel="stylesheet" type="text/css" media="screen" href="../css/themes/aero/ui.jqgrid.custom.css" mce_href="css/themes/aero/ui.jqgrid.custom.css" /> <link href="../css/genericFormTable.css" mce_href="css/genericFormTable.css" rel="stylesheet" type="text/css" /> <mce:script type="text/javascript" src="../js/jquery-1.4.2.min.js" mce_src="js/jquery-1.4.2.min.js"></mce:script> <mce:script src="../js/jquery-ui-1.8.1.custom.min.js" mce_src="js/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></mce:script> <mce:script src="../js/i18n/grid.locale-zh-CN.js" mce_src="js/i18n/grid.locale-zh-CN.js" type="text/javascript"></mce:script> <mce:script src="../js/jquery.jqGrid.min.js" mce_src="js/jquery.jqGrid.min.js" type="text/javascript"></mce:script> <mce:script src="js/jqGrid09.js" mce_src="js/jqGrid09.js" type="text/javascript"></mce:script> </head> <body> <h3> jqGrid测试 09 </h3> <div id="mysearch"></div> <br /> <div> <table id="gridTable"></table> <div id="gridPager"></div> </div> <br /> <div> <button onclick="openSingleSearchDialog()">自定义单条件查询</button> <button onclick="clearSearch()">重置</button> <button onclick="openMultipleSearchDialog()">自定义复杂条件查询</button> </div> <div id="singleSearchDialog"> <table class="formTable"> <thead> <tr> <th>查询条件</th> <th>查询方式</th> <th>查询值</th> </tr> </thead> <tbody> <tr> <td> <input type="radio" name="searchField" value="id">编码</input><br/> <input type="radio" name="searchField" value="lastName">姓</input><br/> <input type="radio" name="searchField" value="firstName">名</input> </td> <td> <select id="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> </select> </td> <td> <input type="text" id="searchString"></input> </td> </tr> </tbody> </table> </div> <div id="multipleSearchDialog"> <table class="formTable"> <thead> <tr> <th>查询条件</th> <th>查询方式</th> <th>查询值</th> </tr> </thead> <tbody> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> </tbody> </table> </div> </body> </html>
JavaScript部分:
$(function(){ $("#gridTable").jqGrid({ url: "listContacts.action", datatype: "json", mtype: "GET", height: 350, width: 600, colModel: [ {name:"id",index:"id",label:"编码",width:40,sortable:false}, {name:"lastName",index:"lastName",label:"姓",width:80,sortable:false}, {name:"firstName",index:"firstName",label:"名",width:80,sortable:false}, {name:"email",index:"email",label:"电子邮箱",width:160,sortable:false}, {name:"telNo",index:"telNo",label:"电话",width:120,sortable:false} ], viewrecords: true, rowNum: 15, rowList: [15,50,100], prmNames: {search: "search"}, jsonReader: { root:"gridModel", records: "record", repeatitems : false }, pager: "#gridPager", caption: "联系人列表", hidegrid: false, shrikToFit: true }); $("#singleSearchDialog").dialog({ autoOpen: false, modal: true, resizable: true, width: 350, title: "自定义单条件查询", buttons: { "查询": singleSearch } }); $("#multipleSearchDialog").dialog({ autoOpen: false, modal: true, resizable: true, width: 350, title: "自定义多条件查询", buttons: { "查询": multipleSearch } }); }); var openSingleSearchDialog = function() { $("#singleSearchDialog").dialog("open"); }; var resetSingleSearchDialog = function() { $("radio","#singleSearchDialog").attr("checked", false); $(":text","#singleSearchDialog").val(""); }; var singleSearch = function() { var sdata = { searchField: $(":radio:checked", "#singleSearchDialog").val(), searchString: $("#searchString", "#singleSearchDialog").val(), searchOper: $("#searchOper", "#singleSearchDialog").val() }; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, sdata); $("#gridTable").jqGrid("setGridParam", { search: true }).trigger("reloadGrid", [{page:1}]); $("#singleSearchDialog").dialog("close"); }; var clearSearch = function() { var sdata = { searchField: "", searchString: "", searchOper: "" }; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, sdata); $("#gridTable").jqGrid("setGridParam", { search: false }).trigger("reloadGrid", [{page:1}]); resetSingleSearchDialog(); }; var openMultipleSearchDialog = function() { $("#multipleSearchDialog").dialog("open"); }; var multipleSearch = function() { var rules = ""; $("tbody tr", "#multipleSearchDialog").each(function(i){ var searchField = $(".searchField", this).val(); var searchOper = $(".searchOper", this).val(); var searchString = $(".searchString", this).val(); if(searchField && searchOper && searchString) { rules += ',{"field":"' + searchField + '","op":"' + searchOper + '","data":"' + searchString + '"}'; } }); if(rules) { rules = rules.substring(1); } var filtersStr = '{"groupOp":"AND","rules":[' + rules + ']}'; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, {filters: filtersStr}); $("#gridTable").jqGrid("setGridParam", { search: true }).trigger("reloadGrid", [{page:1}]); $("#multipleSearchDialog").dialog("close"); };
关于这个问题,我在网上搜了一下,大多都是引用jqGrid的文档中的filterGrid方法。但这个方法不是真正意义的自定义查询。
这篇文章主要讲述一下真正意义的自定义查询,顺便简单聊聊jqGrid源码中查询的实现方法。
在这个系列的第五篇中(http://blog.youkuaiyun.com/gengv/archive/2010/07/10/5725172.aspx )曾经讲到了如何进行数据查询。其中提到了一种“自定义查询”,这是jqGrid的文档中给出的一种所谓的自定义查询。其实就是把工具条查询,从Grid表格中移出来,然后可以控制输入框的横向或纵向排列,构成一种看似表单查询的查询方式。但是这种方式也有一定的局限性,比如不能以colModel中不存在的列作为查询字段进行查询,不能做到真正意义上的自定义表单布局等等。
昨天,优快云的一位网友gaile给我的博客留言:
还有,假如我想自定义serach弹出框的格式该怎么做呢?
他的问题实际上就是:如何实现自定义的表单查询。正好最近我也在考虑这个问题,昨晚特意查看了一下jqGrid的源码,顺便总结出这篇文章。
1. 查询的实现思路:
通过前面文章的介绍,可以知道jqGrid在查询中主要的任务是向Server发送有关于查询的参数,主要的参数包括search、searchField、searchOper、searchString、filters等(这些参数名可以通过选项进行更改,可以参考之前的文章);Server得到参数后,进行后台数据查询,然后将结果发回客户端;jqGrid根据jsonReader的设置,解析返回的结果并显示到Grid表格中。
大体思路就是这样。既然如此,我们要想实现真正的自定义查询,要解决的无非就是如何把有关查询的参数“塞进”请求里。需要用到两个jqGrid选项:
- postData :可以参考本系列第九篇(http://blog.youkuaiyun.com/gengv/archive/2010/07/14/5735654.aspx);当jqGrid向Server发送请求的时候,总是会将postData中的{name:value,...}对,作为参数一同传递到服务器端。因此我们只需要在发送查询请求的时候,将关于查询的参数放进postData就可以了。
- search :这个jqGrid选项,在jqGrid的官方文档中并没有列出来,但确实存在;它的值可以为true或false,用来控制是否为查询请求,如果为true,则在请求中加入search=true参数;可以使用jqGrid的setGridParam方法修改它。
注1 :search选项传入的这个search=true参数,其参数名称“search”是由jqGrid的prmNames选项中设置的,默认为“_search”,此处是为了Server端java程序中成员变量命名方便,而设置为“search”了(具体细节可以参看本系列第一篇http://blog.youkuaiyun.com/gengv/archive/2010/07/05/5714834.aspx )。
注2 :search选项传入请求的search参数值,优先于postData中的同名参数值。即如果serach选型值为flase,而postData中有个名值对为{search:true},则请求为http://...action?search=false&...&search=true。此时第一个search参数(由search选项控制)将使第二个search参数(postData提供)被忽略。
2. 自定义单条件查询的实现
根据之前文章的介绍,当单条件查询发送请求时,参数应为如下形式:
- nd:1278688214496
- page:1
- rows:15
- search:true//(1)
- searchField:id//(2)
- searchOper:ge//(3)
- searchString:9000//(4)
- sidx:
- sord:asc
其中(1)(2)(3)(4)是我们最关注的。
下面看看如何来实现单条件自定义查询:
HTML部分:
- ...
- <body>
- <h3>
- jqGrid测试09
- </h3>
- <divid="mysearch"></div>
- <br/>
- <div>
- <tableid="gridTable"></table>
- <divid="gridPager"></div>
- </div>
- <br/>
- <div>
- <buttononclick="openSingleSearchDialog()">自定义单条件查询</button>
- <buttononclick="clearSearch()">重置</button>
- </div>
- <divid="singleSearchDialog">
- <tableclass="formTable">
- <thead>
- <tr>
- <th>查询条件</th>
- <th>查询方式</th>
- <th>查询值</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <inputtype="radio"name="searchField"value="id">编码</input><br/>
- <inputtype="radio"name="searchField"value="lastName">姓</input><br/>
- <inputtype="radio"name="searchField"value="firstName">名</input>
- </td>
- <td>
- <selectid="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"id="searchString"></input>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </body>
- ...
HTML中增加了一个自定义的<div>,singleSearchDialog;而且使用radio组来选择查询字段。
再看看javascript部分:
- $(function(){
- $("#gridTable").jqGrid({
- ...
- prmNames:{search:"search"},
- ...
- });
- $("#singleSearchDialog").dialog({//(1)创建自定义查询对话框
- autoOpen:false,
- modal:true,
- resizable:true,
- width:350,
- title:"自定义单条件查询",
- buttons:{
- "查询":singleSearch//(2)在对话框中添加查询按钮
- }
- });
- });
- varopenSingleSearchDialog=function(){
- $("#singleSearchDialog").dialog("open");
- };
- varresetSingleSearchDialog=function(){
- $("radio","#singleSearchDialog").attr("checked",false);
- $(":text","#singleSearchDialog").val("");
- };
- varsingleSearch=function(){
- varsdata={//(3)构建查询需要的参数
- searchField:$(":radio:checked","#singleSearchDialog").val(),
- searchString:$("#searchString","#singleSearchDialog").val(),
- searchOper:$("#searchOper","#singleSearchDialog").val()
- };
- //(4)获得当前postData选项的值
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- //(5)将查询参数融入postData选项对象
- $.extend(postData,sdata);
- $("#gridTable").jqGrid("setGridParam",{
- search:true//(6)将jqGrid的search选项设为true
- }).trigger("reloadGrid",[{page:1}]);//(7)重新载入Grid表格,以使上述设置生效
- $("#singleSearchDialog").dialog("close");
- };
- varclearSearch=function(){
- varsdata={//(8)构建一套空的查询参数
- searchField:"",
- searchString:"",
- searchOper:""
- };
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- $.extend(postData,sdata);//(9)将postData中的查询参数覆盖为空值
- $("#gridTable").jqGrid("setGridParam",{
- search:false//(10)将jqGrid的search选项设为false
- }).trigger("reloadGrid",[{page:1}]);
- resetSingleSearchDialog();
- };
说明:
- (7)处设置完jqGrid参数后,重新载入表格时,jqGrid将会将postData中的参数加进请求参数中,因此将提交查询请求;在显式清除postData中的参数之前,每次查询都会带着这些查询参数。
- (9)处将postData中的关于查询的参数全都覆盖为空值,重新载入表格后,请求中将不再传入“有效”的查询参数,即有查询参数名,但无查询参数值。例如: http://localhost:8085/JqGridTest/jqGridTest/jqGrid05.action?search=false &nd=1279865475529&rows=15&page=2&sidx=&sord=asc&searchField=&searchString=&searchOper=
注意:
不论在查询或者清除查询之后条用trigger("reloadGrid"),都应该带上“[{page:1}]”,以设置page选项为1,即:
如果不加上这个参数的话,那么后续的查询中,page都始终保持之前操作时保存的值,对于后面再进行的查询很能产生副作用。举个例子来说,第一次查询使用查询条件A,如果满足条件的数据有110行,Grid中每页显示20行(即每次从Server最多返回20行),则总共有6页数据;这时翻页,一直翻到第4页;然后使用查询条件B再次进行查询,假设这次查询满足条件的数据总共只有35行,每页显示20行,也即只有2页数据,那么如果page的值还是4的话,则后台就找不到数据了。因此,应该使用trigger("reloadGrid", [{page:1}]);,这样总能保证,即使查询结果只有1页数据,也能被显示出来。
3. 自定义多条件查询的实现
多条件查询与但条件查询的不同之处即在于使用filters参数,代替了searchField,searchOper,searchString参数。其参数形式为:
- filters:{
- "groupOp":"AND",
- "rules":[
- {"field":"id","op":"le","data":"1500"},
- {"field":"lastName","op":"bw","data":"LN-3"},
- {"field":"email","op":"cn","data":"sy"}
- ]
- }
- nd:1278688110637
- page:1
- rows:15
- search:true
- sidx:
- sord:asc
因此具体实现也与上述大同小异,只要加入正确的filters参数就行了。
首先看HTML部分,加入了multipleSearchDialog。
- ...
- <div>
- ...
- <buttononclick="openMultipleSearchDialog()">自定义复杂条件查询</button>
- </div>
- ...
- <divid="multipleSearchDialog">
- <tableclass="formTable">
- <thead>
- <tr>
- <th>查询条件</th>
- <th>查询方式</th>
- <th>查询值</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- <tr>
- <td>
- <selectclass="searchField">
- <optionvalue="id">编码</option>
- <optionvalue="lastName">姓</option>
- <optionvalue="firstName">名</option>
- </select>
- </td>
- <td>
- <selectclass="searchOper">
- <optionvalue="eq">等于</option>
- <optionvalue="gt">大于</option>
- <optionvalue="lt">小于</option>
- </select>
- </td>
- <td>
- <inputtype="text"class="searchString"></input>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- ...
再来看javascript部分:
- $(function(){
- $("#gridTable").jqGrid({
- ...
- });
- ...
- $("#multipleSearchDialog").dialog({
- autoOpen:false,
- modal:true,
- resizable:true,
- width:350,
- title:"自定义多条件查询",
- buttons:{
- "查询":multipleSearch
- }
- });
- });
- ...
- varopenMultipleSearchDialog=function(){
- $("#multipleSearchDialog").dialog("open");
- };
- varmultipleSearch=function(){
- varrules="";
- $("tbodytr","#multipleSearchDialog").each(function(i){//(1)从multipleSearchDialog对话框中找到各个查询条件行
- varsearchField=$(".searchField",this).val();//(2)获得查询字段
- varsearchOper=$(".searchOper",this).val();//(3)获得查询方式
- varsearchString=$(".searchString",this).val();//(4)获得查询值
- if(searchField&&searchOper&&searchString){//(5)如果三者皆有值且长度大于0,则将查询条件加入rules字符串
- rules+=',{"field":"'+searchField+'","op":"'+searchOper+'","data":"'+searchString+'"}';
- }
- });
- if(rules){//(6)如果rules不为空,且长度大于0,则去掉开头的逗号
- rules=rules.substring(1);
- }
- //(7)串联好filtersStr字符串
- varfiltersStr='{"groupOp":"AND","rules":['+rules+']}';
- varpostData=$("#gridTable").jqGrid("getGridParam","postData");
- //(8)将filters参数串加入postData选项
- $.extend(postData,{filters:filtersStr});
- $("#gridTable").jqGrid("setGridParam",{
- search:true//(9)将jqGrid的search选项设为true
- }).trigger("reloadGrid",[{page:1}]);//(10)重新载入Grid表格
- $("#multipleSearchDialog").dialog("close");
- };
OK,这样就实现了自定义多条件查询。同时可以看到,colModel中根本不存在身份证号(idCardNo)这一列,但是在查询中依然可以使用这个字段进行查询。
另一方面,由于自己把握了查询参数的控制权,jqGrid的multipleSearch选项将变得无足轻重了。上面的两个例子,仅作说明之用,从外观上来看,还比较粗糙。但已经从根本上控制住了查询的实现。即使在复杂的页面设计,也不会影响查询的实现了。这与jqGrid官方文档中提供的“自定义查询”,是有本质区别的。
对于页面布局的美化,以及动态添加查询条件行等等,只要基本的CSS+JavaScript知识就能实现,因此不再本文的讨论内容中了。
4. jqGrid源码
jqGrid的源码可以从以下链接得到:
http://github.com/tonytomov/jqGrid
关于查询这部分的代码,可以查看:
http://github.com/tonytomov/jqGrid/blob/master/js/grid.formedit.js
片段1:
- $t.SearchFilter=$("#"+fid).searchFilter(fields,{groupOps:p.groupOps,operators:oprtr,onClose:hideFilter,resetText:p.Reset,searchText:p.Find,windowTitle:p.caption,rulesText:p.rulesText,matchText:p.matchText,onSearch:searchFilters,onReset:resetFilters,stringResult:p.stringResult,ajaxSelectOptions:$.extend({},$.jgrid.ajaxOptions,$t.p.ajaxSelectOptions||{}),clone:p.cloneSearchRowOnAdd});
片段2:
- functionsearchFilters(filters){
- varhasFilters=(filters!==undefined),//(1)
- grid=$("#"+$t.p.id),sdata={};
- if(p.multipleSearch===false){//(2)
- sdata[p.sField]=filters.rules[0].field;
- sdata[p.sValue]=filters.rules[0].data;
- sdata[p.sOper]=filters.rules[0].op;
- }else{
- sdata[p.sFilter]=filters;//(3)
- }
- grid[0].p.search=hasFilters;//(4)
- $.extend(grid[0].p.postData,sdata);//(5)
- grid.trigger("reloadGrid",[{page:1}]);//(6)
- if(p.closeAfterSearch){hideFilter($("#"+fid));}//(7)
- }
- functionresetFilters(op){
- varreload=op&&op.hasOwnProperty("reload")?op.reload:true;
- grid=$("#"+$t.p.id),sdata=[];
- grid[0].p.search=false;
- if(p.multipleSearch===false){
- sdata[p.sField]=sdata[p.sValue]=sdata[p.sOper]="";
- }else{
- sdata[p.sFilter]="";
- }
- $.extend(grid[0].p.postData,sdata);
- if(reload){
- grid.trigger("reloadGrid",[{page:1}]);
- }
- if(p.closeAfterReset){hideFilter($("#"+fid));}
- }
我没有从头到尾通读所有代码,没有时间,也没有那么高水平,呵呵~
但从上面片段1中可以看出来:
在查询对话框中点击“查询”按钮,则执行searchFilters方法;而点击“重置”按钮,则执行resetFilters方法。
再看看searchFilters方法:
- (1)首先判断是否存在filters;
- (2)如果multipleSearch选项为false,则构建以sField、sOper、sValue所对应的参数名为内容的参数(默认的参数名为:searchField、searchOper、searchString);
- (3)如果multipleSearch选项为true,则构建sFilter对应的参数名的参数(默认为filters);
- (4)然后将查询参数置入postData选项;
- (5)如果存在filters,则将jqGrid的search选项设为true;这与$("#gridTable").jqGrid("setGridParam", { search: true });是一样的。
- (6)重新载入表格数据(发送包含查询参数的请求);
- (7)如果closeAfterSearch为true,则查询完毕后,关闭查询对话框。
总体看来,与之前自定义的条件查询实现基本一样,所不同的是,这里把单条件和多条件查询和在一起了。
而resetFilters方法,与之前自定义的实现,思路也基本一样,不再赘述。由于searchFilters和resetFilters是定义在searchGird方法中的函数,因此在外部调用时,还是有点困难的。
附上本文完整前端代码(后台代码可以参考之前的文章):
HTML部分:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" lang="zh-CN"> <head> <title>jqGrid09</title> <link rel="stylesheet" type="text/css" media="screen" href="../css/themes/aero/jquery-ui-1.8.1.custom.css" mce_href="css/themes/aero/jquery-ui-1.8.1.custom.css" /> <link rel="stylesheet" type="text/css" media="screen" href="../css/themes/aero/ui.jqgrid.custom.css" mce_href="css/themes/aero/ui.jqgrid.custom.css" /> <link href="../css/genericFormTable.css" mce_href="css/genericFormTable.css" rel="stylesheet" type="text/css" /> <mce:script type="text/javascript" src="../js/jquery-1.4.2.min.js" mce_src="js/jquery-1.4.2.min.js"></mce:script> <mce:script src="../js/jquery-ui-1.8.1.custom.min.js" mce_src="js/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></mce:script> <mce:script src="../js/i18n/grid.locale-zh-CN.js" mce_src="js/i18n/grid.locale-zh-CN.js" type="text/javascript"></mce:script> <mce:script src="../js/jquery.jqGrid.min.js" mce_src="js/jquery.jqGrid.min.js" type="text/javascript"></mce:script> <mce:script src="js/jqGrid09.js" mce_src="js/jqGrid09.js" type="text/javascript"></mce:script> </head> <body> <h3> jqGrid测试 09 </h3> <div id="mysearch"></div> <br /> <div> <table id="gridTable"></table> <div id="gridPager"></div> </div> <br /> <div> <button onclick="openSingleSearchDialog()">自定义单条件查询</button> <button onclick="clearSearch()">重置</button> <button onclick="openMultipleSearchDialog()">自定义复杂条件查询</button> </div> <div id="singleSearchDialog"> <table class="formTable"> <thead> <tr> <th>查询条件</th> <th>查询方式</th> <th>查询值</th> </tr> </thead> <tbody> <tr> <td> <input type="radio" name="searchField" value="id">编码</input><br/> <input type="radio" name="searchField" value="lastName">姓</input><br/> <input type="radio" name="searchField" value="firstName">名</input> </td> <td> <select id="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> </select> </td> <td> <input type="text" id="searchString"></input> </td> </tr> </tbody> </table> </div> <div id="multipleSearchDialog"> <table class="formTable"> <thead> <tr> <th>查询条件</th> <th>查询方式</th> <th>查询值</th> </tr> </thead> <tbody> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> <tr> <td> <select class="searchField"> <option value="id">编码</option> <option value="lastName">姓</option> <option value="firstName">名</option> <option value="idCardNo">身份证</option> </select> </td> <td> <select class="searchOper"> <option value="eq">等于</option> <option value="gt">大于</option> <option value="lt">小于</option> <option value="cn">包含</option> <option value="bw">开头</option> </select> </td> <td> <input type="text" class="searchString"></input> </td> </tr> </tbody> </table> </div> </body> </html>
JavaScript部分:
$(function(){ $("#gridTable").jqGrid({ url: "listContacts.action", datatype: "json", mtype: "GET", height: 350, width: 600, colModel: [ {name:"id",index:"id",label:"编码",width:40,sortable:false}, {name:"lastName",index:"lastName",label:"姓",width:80,sortable:false}, {name:"firstName",index:"firstName",label:"名",width:80,sortable:false}, {name:"email",index:"email",label:"电子邮箱",width:160,sortable:false}, {name:"telNo",index:"telNo",label:"电话",width:120,sortable:false} ], viewrecords: true, rowNum: 15, rowList: [15,50,100], prmNames: {search: "search"}, jsonReader: { root:"gridModel", records: "record", repeatitems : false }, pager: "#gridPager", caption: "联系人列表", hidegrid: false, shrikToFit: true }); $("#singleSearchDialog").dialog({ autoOpen: false, modal: true, resizable: true, width: 350, title: "自定义单条件查询", buttons: { "查询": singleSearch } }); $("#multipleSearchDialog").dialog({ autoOpen: false, modal: true, resizable: true, width: 350, title: "自定义多条件查询", buttons: { "查询": multipleSearch } }); }); var openSingleSearchDialog = function() { $("#singleSearchDialog").dialog("open"); }; var resetSingleSearchDialog = function() { $("radio","#singleSearchDialog").attr("checked", false); $(":text","#singleSearchDialog").val(""); }; var singleSearch = function() { var sdata = { searchField: $(":radio:checked", "#singleSearchDialog").val(), searchString: $("#searchString", "#singleSearchDialog").val(), searchOper: $("#searchOper", "#singleSearchDialog").val() }; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, sdata); $("#gridTable").jqGrid("setGridParam", { search: true }).trigger("reloadGrid", [{page:1}]); $("#singleSearchDialog").dialog("close"); }; var clearSearch = function() { var sdata = { searchField: "", searchString: "", searchOper: "" }; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, sdata); $("#gridTable").jqGrid("setGridParam", { search: false }).trigger("reloadGrid", [{page:1}]); resetSingleSearchDialog(); }; var openMultipleSearchDialog = function() { $("#multipleSearchDialog").dialog("open"); }; var multipleSearch = function() { var rules = ""; $("tbody tr", "#multipleSearchDialog").each(function(i){ var searchField = $(".searchField", this).val(); var searchOper = $(".searchOper", this).val(); var searchString = $(".searchString", this).val(); if(searchField && searchOper && searchString) { rules += ',{"field":"' + searchField + '","op":"' + searchOper + '","data":"' + searchString + '"}'; } }); if(rules) { rules = rules.substring(1); } var filtersStr = '{"groupOp":"AND","rules":[' + rules + ']}'; var postData = $("#gridTable").jqGrid("getGridParam", "postData"); $.extend(postData, {filters: filtersStr}); $("#gridTable").jqGrid("setGridParam", { search: true }).trigger("reloadGrid", [{page:1}]); $("#multipleSearchDialog").dialog("close"); };