前言
數據顯示的方式可以通過很多控件來實現,例如服務端的原生GridView,第三方控件ComponentArt、Telerik等,客戶端的flexgrid, extgrid, easyui, jqgrid等等。在這里我要講解的是jqgrid,它也是我最近在項目中嘗試用到的。
我選擇使用jqgrid主要是因為它基於jquery ui,在沒有美工、契合系統主題並且快速完成系統的前提下,我選擇了可以定制theme的jquery ui.
文中涉及到一些其他知識點,也做了較為詳細的說明,希望這些內容能夠對web開發者們有較好的增益作用。
每一個頁面的在線demo我還在配置中,一會兒放出來,可以讓大家有個直觀的感受。
- 一、Json標准數據格式和驗證
- 二、jquery ui theme的使用
- 三、jqgrid的使用方式
-
1. jqgrid的local data使用方式
2. jqgrid的server data之loadonce方式
3. jqgrid的jsonReader
4. jqgird的server data之最終解決方案
在開始項目之前,需要做的一些准備我也會逐步在下面的內容中進行介紹,或者你可以直接在這里下載本文所有demo的源代碼,源代碼中已經為你准備好這些文件,並且這些都是可以運行的。
源代碼下載:KFBlogDemo.zip
數據庫:Northwind(2005,2008 Northwind下載)
一、 Json標准格式和驗證
由於自己在使用jqgrid的過程中曾經使用過不規范的json,導致數據jqgrid一直無法顯示,所以在此有必要簡單講一下json的標准格式和驗證方式。
雖然在js中字符串也可以用單引號來表示,但是json的標准格式是用雙引號把屬性和值給括起來的,注意是屬性和值(當然數值類型的值可以省去雙引號)。下列Json格式中除了最后一條是標准的,前面幾條都是不標准的。
{ 'name' : 'keepfool' } // 錯誤格式,proerty和value應用雙引號 { name: 'keepfool' } // 錯誤格式,property應用雙引號括起來,value應用雙引號 { name: "keepfool" } // 錯誤格式,property應用雙引號括起來 { "name" : 'keepfool' } // 錯誤格式,value應用雙引號 { "name" : "keepfool" } // 標准格式
有時候手動生成json格式的字符串,可能會不符合標准,可以通過下面兩個在線驗證工具執行驗證,我推薦使用第一個,因為它能夠准確的定位出json串出錯的地方,下面的幾個例子也是通過第一個在線工具做驗證的。
例子1(屬性沒帶引號):
{ id: "1", "invdate": "2010-05-24", "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }
例子2(屬性帶單引號):
{ "id": "1", "invdate": '2010-05-24', "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }
例子3(正確):
{ "id": "1", "invdate": "2010-05-24", "name": "test", "note": "note", "tax": "10.00", "total": "2111.00" }

二、jquery ui theme的使用
在文章的開篇我已經提到過jquery ui,它的使用方式我會在下面一步一步列出來,希望對那些還不太會使用jquery ui theme的童鞋們有所幫助。
1. 項目創建
在VS下創建一個Web網站或者Web應用程序,創建一個themes目錄。如下圖:
2. Base Theme的使用
先到http://jqueryui.com/themeroller/下載一個theme,就選那個最基本的灰色調的。
如果你已經熟悉下面的步驟並且對於下面的步驟不感興趣,可以直接跳至關鍵的代碼區域
然后會看到下面這個畫面,甭管左邊有哪些組件,直接download.
把下載得到的jquery-ui-1.8.16.custom.zip文件解壓出來,找到base目錄(\jquery-ui-1.8.16.custom\development-bundle\themes\base),將base目錄復制到themes文件夾下。
找到ui目錄(\jquery-ui-1.8.16.custom\development-bundle\ui\),同樣將其復制到themes目錄下,ui目錄包含三部分:
(1). jquery ui組件
(2). 壓縮后的jquery ui組件(在minified文件夾中)
(3). 本地化語言文件(i18n)
現在基本的准備都已經做好了,下面我們來創建第一個頁面BaseTheme.html,並演示一個手風琴的效果(accordion組件),請看基礎並且關鍵的步驟:
(1). 引用jquery ui css
<link rel="Stylesheet" type="text/css" href="themes/base/jquery.ui.all.css" />
(2). 引用jquery ui js
<script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.accordion.js"></script>
(3). 手風琴html代碼
<div class="demo"> <h1> Base Demo</h1> <div id="accordion"> <h3><a href="#">Section 1</a></h3> <div> <p>A.</p> </div> <h3><a href="#">Section 2</a></h3> <div> <p> B. </p> </div> </div> </div>
(4). 實現jquery 手風琴效果
<script type="text/javascript"> $(function() { $("#accordion").accordion({ collapsible: true }); }); </script>
最終的效果如下圖,當然完整的代碼已經包含在下載文件中了,您也可以直接運行里面的代碼。
3. theme的使用方式
Base Theme的使用方式我們已經使用過了,但是這個灰色調的主題可能並不符合您網站的主題色調,不過幸運的是http://jqueryui.com/themeroller/已經為我們提供了很多種不同色調而且比較美觀的主題,你可以自行去下載。當然如果你覺得某一款主題看起來不錯,但某些樣式還需要一些輕微的改動才能應用到自己的程序中,你可以使用Edit去進行編輯,然后生成自己需要的主題。
在第2步中已經列出了jquery ui的使用方式,如果你想換一個主題使用,方式和上面的代碼步驟是相同的。
以lightness主題為例:
唯一不同之處是對css的引用
<link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css"/>
![SNAGHTML5cf4ff[6] SNAGHTML5cf4ff[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20va2VlcGZvb2wvMjAxMjAxLzIwMTIwMTA2MDk1ODAxNjU0Ni5wbmc=.png)
這里演示的例子都是accordion的,如果你想嘗試其他的jquery ui組件,可以自行更換。但關鍵的兩行代碼是必不可少的,它們是jquery ui組件的基礎:
<script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script>
4. 主題切換的實現
有了前面的基礎,要實現主題切換已經很簡單了,因為我們已經知道jquery theme的關鍵之處——引用jquery.ui.all.css,請看代碼:
步驟I
<link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css" id="theme" >
步驟II
<div> 請選擇主題: <select id="theme_changer"> <option value="themes/base/jquery.ui.all.css">Base Theme</option> <option value="themes/theme_lightness/jquery.ui.all.css" selected="selected">Lightness Theme</option> </select> </div>
步驟III
<script type="text/javascript"> $(document).ready(function() { $('#theme_changer').change(function() { var theme = $(this).find("option:selected").val(); $('#theme').attr('href', theme); }); }); </script>
效果圖:
5. 單個頁面多主題的實現
單個頁面多主題的實現需要在http://jqueryui.com/download頁面下載時多做一個步驟——指定css scope
點開Advanced Theme Settings,指定css scope,一般為css class的命名方式
點擊CSS Scope后面的 按鈕,你會發現css scope是專門針對多主題應用。
下面是完整的代碼:
<!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"> <head> <title>多主題</title> <link rel="shortcut icon" href="/images/favicon.ico" /> <!--應用base主題和lightness主題--> <link rel="Stylesheet" type="text/css" href="themes/base/jquery.ui.all.css" /> <link rel="Stylesheet" type="text/css" href="themes/theme_lightness/jquery.ui.all.css" /> <link rel="Stylesheet" type="text/css" href="themes/demos.css" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.core.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.widget.js"></script> <script type="text/javascript" src="themes/ui/jquery.ui.accordion.js"></script> <script type="text/javascript"> $(function() { $(".accordion").accordion({ collapsible: true }); }); </script> </head> <body> <div class="demo"> <h1> Base Demo</h1> <div class="accordion"> <h3> <a href="#">Section 1</a></h3> <div> <p> A.</p> </div> <h3> <a href="#">Section 2</a></h3> <div> <p> B. </p> </div> </div> <div class="space"> </div> <h1> Lightness Demo</h1> <!--主題中如果包含css scope,需要指定css scope,才能讓主題生效--> <div class="lightness"> <div class="accordion"> <h3> <a href="#">Section 1</a></h3> <div> <p> A.</p> </div> <h3> <a href="#">Section 2</a></h3> <div> <p> B. </p> </div> </div> </div> </div> </body> </html>
效果如下:
三、jqgrid的使用方式
上面的兩大步都是為了使用jqgrid做准備,冗長的敘述和截圖看起來很多,其實知道是jquery ui是怎么回事兒以后就會覺得很簡單,步驟也不多。
jqgrid的項目地址:http://www.trirand.com/blog/,目前最新的版本是4.3的,也是我正在使用的版本。
1. jqgrid的local data使用方式
請看代碼示例1:
<script type="text/javascript"> var mydata = [ { id: "1", invdate: "2010-05-24", name: "test", note: "note", tax: "10.00", total: "2111.00" }, { id: "2", invdate: "2010-05-25", name: "test2", note: "note2", tax: "20.00", total: "320.00" }, { id: "3", invdate: "2007-09-01", name: "test3", note: "note3", tax: "30.00", total: "430.00" }, { id: "4", invdate: "2007-10-04", name: "test", note: "note", tax: "10.00", total: "210.00" }, { id: "5", invdate: "2007-10-05", name: "test2", note: "note2", tax: "20.00", total: "320.00" }, { id: "6", invdate: "2007-09-06", name: "test3", note: "note3", tax: "30.00", total: "430.00" }, { id: "7", invdate: "2007-10-04", name: "test", note: "note", tax: "10.00", total: "210.00" }, { id: "8", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "21.00", total: "320.00" }, { id: "9", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "11", invdate: "2007-10-01", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "12", invdate: "2007-10-02", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "13", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "14", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "15", invdate: "2007-10-05", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "16", invdate: "2007-09-06", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "17", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "18", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "19", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "21", invdate: "2007-10-01", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "22", invdate: "2007-10-02", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "23", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "24", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "25", invdate: "2007-10-05", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "26", invdate: "2007-09-06", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00" }, { id: "27", invdate: "2007-10-04", name: "test", note: "note", amount: "200.00", tax: "10.00", total: "210.00" }, { id: "28", invdate: "2007-10-03", name: "test2", note: "note2", amount: "300.00", tax: "20.00", total: "320.00" }, { id: "29", invdate: "2007-09-01", name: "test3", note: "note3", amount: "400.00", tax: "30.00", total: "430.00"}]; $(document).ready(function() { $("#gridTable").jqGrid({ data: mydata, datatype: "local", height: 150, rowNum: 10, rowList: [10, 20, 30], colNames: ['Inv No', 'Date', 'Client', 'Amount', 'Tax', 'Total', 'Notes'], colModel: [ { name: 'id', index: 'id', width: 60, sorttype: "int" }, { name: 'invdate', index: 'invdate', width: 90, sorttype: "date", formatter: "date" }, { name: 'name', index: 'name', width: 100 }, { name: 'amount', index: 'amount', width: 80, align: "right", sorttype: "float", formatter: "number" }, { name: 'tax', index: 'tax', width: 80, align: "right", sorttype: "float" }, { name: 'total', index: 'total', width: 80, align: "right", sorttype: "float" }, { name: 'note', index: 'note', width: 150, sortable: false } ], sortname: 'id', sortorder: 'desc', pager: "#gridPager", viewrecords: true, caption: "Manipulating Array Data" }); }); </script> <table id="gridTable"> </table> <div id="gridPager"> </div>
jqgrid有幾個較為關鍵的屬性:
- data:指定jqgrid的數據源
- datatype:數據源的格式——local,javascript,function,json,jsonstring,xml。默認的數據源格式為xml
- colNames:列名
- colModels:指定列的屬性,例如name用於關聯數據源,index用於排序時的索引
- sortname和sortorder分別指定jqgrid首次加載時的排序字段和排序方式
- pager:指定分頁容器。
上面這個例子的效果如下圖:
2. jqgrid的server data之loadonce方式
實際的應用中數據都來源於后端,local方式基本不會使用。jqgrid中有一種loadonce的方式,當然后台提供所有的數據,分頁、排序功能都由客戶端自己去實現。數據少的時候用這種方式是最為快速的,畢竟只做一次請求后台除了輸出數據外不需要再寫其他的代碼了。
請看代碼示例:
<script type="text/javascript"> $(document).ready(function() { $("#gridTable").jqGrid({ url : 'data/test.json', datatype: 'json', height: 150, rowNum: 10, rowList: [10, 20, 30], colNames: ['Inv No', 'Date', 'Client', 'Amount', 'Tax', 'Total', 'Notes'], colModel: [ { name: 'id', index: 'id', width: 60, sorttype: "int" }, { name: 'invdate', index: 'invdate', width: 90, sorttype: "date", formatter: "date" }, { name: 'name', index: 'name', width: 100 }, { name: 'amount', index: 'amount', width: 80, align: "right", sorttype: "float", formatter: "number" }, { name: 'tax', index: 'tax', width: 80, align: "right", sorttype: "float" }, { name: 'total', index: 'total', width: 80, align: "right", sorttype: "float" }, { name: 'note', index: 'note', width: 150, sortable: false } ], loadonce: true, sortname: 'id', sortorder: 'desc', pager: "#gridPager", viewrecords: true, caption: "Manipulating Array Data" }); }); </script> <table id="gridTable"> </table> <div id="gridPager"> </div>
這段代碼和local data的形式中不同之處在於:
(1). url:指定了請求的數據源。(test.json文件的內容和第1個例子中的mydata 內容一樣)
(2). datatype:將數據格式改為json,而不是local
(3). loadonce: true(默認情況下為false,顯示指定只加載一次數據)
OK,編譯運行,但是結果卻沒有出現例子1中的效果圖,jqgrid里面完全沒有數據顯示。
頁面LoadOnce.aspx效果:
這時候jqgrid中一個更為關鍵的屬性出現了,jsonReader。既然指定了數據格式為json,那么后端傳過來的數據格式總不能我說什么就是什么吧,{"id":1, "name":"keepfool"}和{1,"keepfool"}都是正確的json數據,當傳到前端時,前端怎么會知道要匹配哪一種形式?jsonReader顧名思義,就是用來讀取json的,當然也要給它定個規矩。
讓我們來看看http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data中默認的json格式是怎么樣的吧
當沒有設置jsonReader屬性時,提供的json格式應該是下面這種形式的:
它讓你指定total,page,records屬性(分別對應總頁數、當前頁、記錄總數);rows屬性,還必須指定id和cell屬性,cell屬性里面僅僅是單元格的值,還不能包括列名。如果按照默認的jsonReader來讀取數據,你可能會寫出如下構造json的代碼:
public class MyOrder : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "application/json"; string sql = "select top 100 OrderID,CustomerID,ShipName,OrderDate from Orders "; DataTable dt = SqlHelper.ExecuteDataset(sql).Tables[0]; IList<object> rowObjects = new List<object>(); // 每頁顯示記錄數 int pageSize = 10; // 記錄總數 int rowCount = dt.Rows.Count; // 列數 int columnCount = dt.Columns.Count; // 總頁數 int pageCount = rowCount%pageSize == 0 ? rowCount/pageSize : rowCount/pageSize + 1; int ID = 1; foreach (DataRow dr in dt.Rows) { var cellValue = new string[columnCount]; for (var i = 0; i < cellValue.Length; i++) { cellValue[i] = dr[i].ToString(); } // 創建row對象 var rowObj = new { id = ID, cell = cellValue }; rowObjects.Add(rowObj); ID++; } var resultObj = new { total = pageCount, // 總頁數 page = 1, // 由於客戶端是loadonce的,page也可以不指定 records = rowCount, // 總記錄數 rows = rowObjects // 數據 }; string json = JsonConvert.SerializeObject(resultObj); context.Response.Write(json); } public bool IsReusable { get { return false; } } }
請求頁面LoadOnceOrders.aspx效果:
但是這里存在一個小bug,下面的分頁按鈕不起作用了,當點擊排序或者選擇每頁顯示記錄數時,分頁按鈕又可以用了。這個bug僅在loadonce為true時會出現。
在http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options頁面中也對loadonce屬性有所說明
3. jqgrid的jsonReader
上面的例子中沒有設置jsonReader屬性,那么就得按照默認的jsonReader提供json數據。上面的json數據會讓我們感覺有一些怪異,rows屬性中的id和cell我們不需要,total應該表示為pagecount, records應該表示為total在語義上更易讀通,而且row中的每一列都應當是鍵值對這種形式。
尋常情況下我們提供的json格式應該是這樣的:
{ "pagecount":10, "pageindex":1, "total":100, "rows":[ { "id":1, "name":"keepfool" }, { "id":2, "name":"pai" } ... ] }
pagecount表示總頁數,pageindex表示當前頁,total表示總記錄數,rows表示記錄。既然這樣,我們的jsonReader就得改寫,而且后台也應當提供這樣的數據。
LoadOnceOrder2.aspx頁面的jsonReader(其他設置和LoadOnceOrders.aspx頁面一致)
jsonReader: { root: "rows", page: "pageindex", total: "pagecount", records: "total", repeatitems: false, id: "0" }
請注意這里的repeatitems屬性,當repeatitems=false時,jqgrid會根據返回的json數據搜索列名,這個列名對應colModel中的名字
為LoadOnceOrder2.aspx頁面提供Json數據的MyOrder2.ashx的http處理程序
public class MyOrder2 : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "application/json"; string sql = "select top 100 OrderID,CustomerID,ShipName,OrderDate from Orders "; DataTable dt = SqlHelper.ExecuteDataset(sql).Tables[0]; // 每頁顯示記錄數 int pageSize = 10; // 記錄總數 int rowCount = dt.Rows.Count; // 總頁數 int pageCount = rowCount % pageSize == 0 ? rowCount / pageSize : rowCount / pageSize + 1; var resultObj = new DataResult { // 總頁數 PageCount = pageCount, // 當前頁 PageIndex = 1, // 總記錄數 Total = rowCount, // 數據 Data = dt }; string resultJson = JsonHelper.Serialize(resultObj); context.Response.Write(resultJson); } public bool IsReusable { get { return false; } } }
DataResult類的代碼很簡單:
/// <summary> /// 數據結果 /// </summary> public class DataResult { /// <summary> /// 總記錄數 /// </summary> public int Total { get; set; } /// <summary> /// 總頁數 /// </summary> public int PageCount { get; set; } /// <summary> /// 頁碼 /// </summary> public int PageIndex { get; set; } /// <summary> /// 數據 /// </summary> public DataTable Data { get; set; } }
JsonHelper類是通過JSON.NET實現對象序列化和反序列化的一個幫助類,這里我用它來的Serialize方法序列化DataResult對象,它的代碼我就不貼出來了,在下載的源代碼中已經包含了。
下面再介紹一種jsonReader用法,這種用法是函數形式的,也是我個人最推崇這種用法:
jsonReader: { repeatitems: false, root: function(obj) { return obj.rows; }, page: function(obj) { return obj.pageindex; }, total: function(obj) { return obj.pagecount; }, records: function(obj) { return obj.total; } }
觀察一下上面所提供的尋常json寫法,你會發現json數據對象里面包含rows, pageindex, pagecount,total屬性,而后台輸出到前端的function函數中得到的是obj對象,那么用obj.rows, obj.pageindex, obj.pagecount, obj.total就會覺得一目了然。
4. jqgird的server data之最終解決方案
上面的分頁和排序都因為loadonce=true屬性而導致客戶端幫我們做了大部分的事情,但是loadonce存在bug,當數據量很大時,一次性加載有所數據不僅速度太慢,如果傳輸的json數據超過4MB時會拋出異常(可以在web.config中設置大小)。
要解決這個問題,我們只有通過服務端來操控分頁數據,而不是一次性丟給客戶端所有的數據,也就是說客戶端請求哪一頁的數據服務端就提供那一頁的幾條數據給客戶端。
到了這里,我要介紹最后一個jqgrid很重要的屬性——prmNames
在http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options中對prmNames的介紹很詳細,我們只用看標記部分,prmNames是發起請求時發送到服務端的參數數組。
我們現在只關心4個參數的設定,page,rows,sort和order,不用它的默認設置,我們自己將prmNames設置為如下的形式:
prmNames: { page: 'PageNumber', rows: 'PageSize', sort: 'OrderBy', order: 'Sort' }那么一個發送到服務端的請求可能是這樣的 mydata.ashx?PageNumber=1&PageSize=10&OrderBy=OrderID&Sort=desc
注意這里的rows,由於我們每一頁的分頁數據都是向服務器發送請求取得的,所以這里的rows對應為PageSize
在此之前我們先看看我們最終的效果圖:
①.排序 ②.分頁 ③. 記錄這三個地方的功能都是正常的無誤的。
我還是按照step by step的方式來介紹這個實現過程。
(1) 分頁存儲過程
分頁數據通過執行存儲過程來得到,以獲取訂單為例(數據庫為Northwind):
create proc SP_GetOrdersByPage ( @PageSize int, @PageIndex int, @Where nvarchar(1000), @OrderBy nvarchar(50), @Sort varchar(4), @Total int output ) as declare @startRow int,@endRow int --起始行索引 select @startRow = @PageSize*(@PageIndex -1) + 1 --結束行索引 select @endRow = @startRow + @PageSize - 1 --查詢結果集sql declare @query_sql nvarchar(2000) --查詢記錄數sql declare @count_sql nvarchar(2000) set @query_sql = 'select OrderID,CustomerID,ShipName,OrderDate from (select row_number() over(order by ' + @OrderBy + ' ' + @Sort +' ) as rowid, OrderID,CustomerID,ShipName,OrderDate from Orders where 1 = 1 ' + @Where + ') as Results where rowid >='+ str(@startRow) + ' and rowid <=' + str(@endRow) set @count_sql = 'select @Total = count(OrderID) from Orders where 1 = 1' + @Where print @query_sql exec sp_executesql @count_sql,N'@Total int output', @Total output exec sp_executesql @query_sql go declare @Total int exec SP_GetOrdersByPage 10,2,'','OrderID','desc',@Total output
(2) PagingParameters類
既然有了客戶端提供的prmNames參數,那么后台總得有個東西來接收它們吧。那好,在這兒我們定義一個PagingParameters類,用來管理分頁參數。現在PagingParameters類已經擁有4個屬性了:PageNumber, PageSize, OrderBy, Sort。
這些參數還不夠,存儲過程中的@Where參數和@Total參數總需要吧,那么再加兩個屬性Where,Total。但是這些參數還不夠,存儲過程SP_GetOrdersByPage是對Order進行分頁的,如果我要對Category分頁,那是不是該提供一個SP_GetCategoriesByPage這種存儲過程呢?所以這兒還需要一個參數ProcedureName。
稍作整理后,PagingParameters類的代碼如下:
/// <summary> /// 分頁參數 /// </summary> public class PagingParameters { /// <summary> /// 頁碼 /// </summary> public int PageIndex { get; set; } /// <summary> /// 每頁顯示個數 /// </summary> public int PageSize { get; set; } /// <summary> /// 排序字段 /// </summary> public string OrderBy { get; set; } /// <summary> /// 排序方式 /// </summary> public string Sort { get; set; } /// <summary> /// 查詢條件 /// </summary> public string Where { get; set; } /// <summary> /// 存儲過程名稱 /// </summary> public string ProcedureName { get; set; } /// <summary> /// 總記錄數,為輸出參數 /// </summary> public int Total { get { if (_parameters.Count > 0) { return (int)_parameters[5].Value; } return 0; } } private IList<SqlParameter> _parameters = new List<SqlParameter>(); /// <summary> /// 存儲過程參數 /// </summary> /// <returns></returns> public SqlParameter[] ProcedureParameters { get { if(_parameters.Count == 0) { _parameters.Add(new SqlParameter("PageIndex", PageIndex)); _parameters.Add(new SqlParameter("PageSize", PageSize)); _parameters.Add(new SqlParameter("Where", Where)); _parameters.Add(new SqlParameter("OrderBy", OrderBy)); _parameters.Add(new SqlParameter("Sort", Sort)); var outputTotal = new SqlParameter { Direction = ParameterDirection.Output, ParameterName = "Total", DbType = DbType.Int32, Size = 4 }; _parameters.Add(outputTotal); } return _parameters.ToArray(); } } }
(3) 抽象的http 分頁處理程序類
如果ajax分頁用到的場合不止一處,就干脆寫個抽象的http處理程序吧,代碼我就不解釋了,看注釋應該很容易弄明白的。
/// <summary> /// 抽象分頁http處理程序類 /// </summary> public abstract class PagingHandler : IHttpHandler { public virtual void ProcessRequest(HttpContext context) { var parameters = new PagingParameters { // 接收PageIndex參數 PageIndex = int.Parse(context.Request["PageIndex"]), // 接收PageSize參數 PageSize = int.Parse(context.Request["PageSize"]), // 接收OrderBy參數 OrderBy = context.Request["OrderBy"], // 接收Sort參數 Sort = context.Request["Sort"], // 存儲過程名稱 ProcedureName = ProcName, // Where條件 Where = string.Empty }; this.PagingParameters = parameters; context.Response.ContentType = "application/json"; context.Response.Write(GetJsonResult()); } /// <summary> /// 獲取輸出到客戶端的json /// </summary> public virtual string GetJsonResult() { DataSet ds = SqlHelper.ExecuteDataset(PagingParameters.ProcedureName, PagingParameters.ProcedureParameters); DataTable table = ds.Tables[0]; // 存儲過程已經執行完畢,可以獲取到輸出參數Total的值 int total = PagingParameters.Total; // 計算總頁數 int pageCount = total%PagingParameters.PageSize == 0? total/PagingParameters.PageSize: total/PagingParameters.PageSize + 1; var dataResult = new DataResult { Data = table, PageIndex = PagingParameters.PageIndex, PageCount = pageCount, Total = total }; // 序列化DataResult對象 string json = JsonHelper.Serialize(dataResult); return json; } protected PagingParameters PagingParameters { get; set; } public bool IsReusable { get { return false; } } /// <summary> /// 必須初始化的屬性:存儲過程名稱 /// </summary> public abstract string ProcName { get; } }
(4) MyOrder3.ashx服務端http分頁處理程序
創建一個MyOrder3.ashx http處理程序,讓它繼承PagingHandler類。現在要做的很簡單了,PagingHandler類已經幫我們做了大部分的事情,我們只需要實現抽象屬性ProcName就可以了。
public class MyOrder3 : PagingHandler { public override string ProcName { get { return "SP_GetOrdersByPage"; } } }
(5) 前端代碼ServerPaging.aspx頁面
<script type="text/javascript"> $(document).ready(function() { $("#gridTable").jqGrid({ url: 'handlers/myorder3.ashx', datatype: 'json', height: 250, rowNum: 10, rowList: [10, 20, 30], colNames: ['訂單ID', '客戶ID', '送貨人', '下單時間'], colModel: [ { name: 'OrderID', index: 'OrderID', width: 100 }, { name: 'CustomerID', index: 'CustomerID', width: 100 }, { name: 'ShipName', index: 'ShipName', width: 160 }, { name: 'OrderDate', index: 'OrderDate', width: 160 } ], jsonReader: { repeatitems: false, root: function(obj) { return obj.rows; }, page: function(obj) { return obj.pageindex; }, total: function(obj) { return obj.pagecount; }, records: function(obj) { return obj.total; } }, prmNames: { page: 'PageIndex', rows: 'PageSize', sort: 'OrderBy', order: 'Sort' }, loadonce: false, sortname: 'OrderID', sortorder: 'desc', pager: "#gridPager", viewrecords: true, caption: "Manipulating Array Data" }); }); </script> <table id="gridTable"> </table> <div id="gridPager"> </div>
(6) jqgrid最大高度+高度自適應的實現
在我的另外一篇文章Web布局中的幾種寬高自適應中,我沒有貼出實現jqgrid寬高自適應的代碼,在這里一並貼出吧。
<script type="text/javascript"> $(document).ready(function() { function content_resize() { var grid = $("#gridTable"); var h = $(window).height() - grid.offset().top - 50; $('.ui-jqgrid-bdiv').css("height", h); } $(window).wresize(content_resize); content_resize(); }); </script>
加上這一段腳本后,最終的效果看起來就更為不錯了。