原文出处:http://blog.darkthread.net/post-2011-12-31-kendo-ui-grid.aspx
AJAX式資料清單的新選擇-Kendo UI Grid
這幾年在專案中,針對表格式資料的呈現,我多已摒棄PostBack寫法,改用AJAX動態提取方式處理換頁、排列、重新查詢等資料查詢需求,如此可避免傳統PostBack時畫面會閃一下的缺點,提供使用者較流暢的操作感受。
前陣子寫過一篇筆記:Telerik RadGrid AJAX更新範例,介紹的便是應用Telerik的RadControls for ASP.NET AJAX元件庫,實現純AJAX式資料來源的GridView。不過,該元件基於ASP.NET AJAX Client Library並非jQuery,加上元件為ASP.NET控制項,不能安插於純HTML網頁中,限制較多;之前還介紹過另一套Telerik Extensions for ASP.NET MVC,是以jQuery為基礎打造的Client-Side Library,但它主要設計在ASP.NET MVC cshtml中被呼叫,若要在非ASP.NET MVC環境中使用,需要花功夫自己包Plug-In。(是的,這事兒我也幹了...)
本月初(2011/12),Telerik推出了Kendo UIFramework,一套以HTML5 + jQuery打造的精緻UI元件組,依循如Telerik RadControls for ASP.NET的傳統,照例網羅了日期選擇器、頁籤、選單、Grid、TreeView... 等網頁開發會用到的大小控制項,一方面善用HTML5 + CSS3的威力,另一方面繼續保持跨平台相容(跨IE,Firefox,Chrome,Safari,Opera等瀏覽器,但HTML5支援度較差的IE7/IE8也OK,還支援WP7,iOS及Android等行動裝置),看起來是純Client-Side元件的一項不錯選擇。
在授權上,Kendo UI跟Extensions for ASP.NET MVC一樣採雙授權方式: 商業版本可享有技術支援,每月更新,並可包在商品中發行販售;GPL授權則提供每季更新的開放源碼,若只應用在自己開發的網站中,不會再將網站程式本體作為商品販售散佈,理論上均可自由取得及修改,即使網路對外營運也在合法授權範圍內。(網站引用GPL元件授權議題可參見上回討論)
從此,要在網站專案中實現AJAX式的GridView,又多了一項選擇。這裡就依著上回Telerik RadGrid AJAX更新範例的規格,改用Kendo UI Grid來實做驗證一番! 如下圖,分頁、排序、關鍵字查詢功能都不是問題,看來足以滿足專案的基本需求。
Grid1.htm程式碼如下:
head>@H_502_30@ title>Grid Lab 1</>
<!--In the header of your page,paste the following for Kendo styles-->
@H_502_30@ link href="../styles/kendo.common.min.css" rel="stylesheet" type="text/css" />
="../styles/kendo.default.min.css" />@H_502_30@
<!--Then paste the following for Kendo scripts-->
@H_502_30@ script src="../js/jquery.min.js" ="text/javascript"></script>
<script src="../js/kendo.all.min.js" type="text/javascript"></script>@H_502_30@ <style>
body { font-size: 9pt; }@H_502_30@ #dvGrid { width: 500px; }
span.hi-lite { color: red; }@H_502_30@ #dvGrid th.k-header { text-align: center }
</style>@H_502_30@ <script>
$(function () {
@H_502_30@ //建立資料來源物件
var dataSrc = new kendo.data.DataSource({@H_502_30@ transport: {
read: {@H_502_30@ //以下其實就是$.ajax的參數
type: "POST",
@H_502_30@ url: "JsonDataSrc.ashx",
dataType: "json",
@H_502_30@ data: {
//額外傳至後方的參數
@H_502_30@ keywd: function () {
return $("#tKeyword").val();@H_502_30@ }
}@H_502_30@ }
},@H_502_30@ schema: {
//取出資料陣列
@H_502_30@ data: function (d) { return d.Data; },
//取出資料總筆數(計算頁數用)
@H_502_30@ total: return d.TotalCount; }
},@H_502_30@ pageSize: 10,
serverPaging: true,
@H_502_30@ serverSorting: true
});@H_502_30@ //JSON日期轉換
var dateRegExp = /^\/Date\((.*?)\)\/$/;
@H_502_30@ window.toDate = function (value) {
var date = dateRegExp.exec(value);
@H_502_30@ return new Date(parseInt(date[1]));
}@H_502_30@
$("#dvGrid").kendoGrid({
@H_502_30@ dataSource: dataSrc,
columns: [@H_502_30@ { field: "UserNo",title: "會員編號" },
{ field: "UserName",128)">"會員名稱",
@H_502_30@template: '#= "<span class=\\"u-name\\">" + UserName + "</span>" #'
},@H_502_30@ { field: "RegDate",128)">"加入日期",
template: '#= kendo.toString(toDate(RegDate),"yyyy/MM/dd")#'
@H_502_30@ },128)">"Points",128)">"累積點數" },
@H_502_30@ ],
sortable: 502_30@ pageable: dataBound: function () {
@H_502_30@ //AJAX資料Bind完成後觸發
var kw = $("#tKeyword").val();@H_502_30@ //若有設關鍵字,做Highlight處理
if (kw.length > 0) {
@H_502_30@ var re = new RegExp(kw,"g");
$(".u-name").each(function () {@H_502_30@ var $td = $(this);
$td.html($td.text()@H_502_30@ .replace(re,128)">"<span class='hi-lite'>$&</span>"));
});@H_502_30@ }
}@H_502_30@ });
//按下查詢鈕
@H_502_30@ $("#bQuery").click(function () {
//要求資料來源重新讀取(並指定切至第一頁)
@H_502_30@ dataSrc.read({ page: 1,skip: 0 });
//Grid重新顯示資料 2013-07-19更正,以下可省略
@H_502_30@ //});@H_502_30@ });
>
@H_502_30@bodydiv style="padding: 10px;">
關鍵字: input id="tKeyword" /><="button" value="查詢" ="bQuery" />@H_502_30@div="dvGrid">
後端我寫了一個JsonDataSrc.ashx來負責資料供給,其中程式邏輯與上回RadGrid版本幾乎完全相同。(只要能正確傳回JSON即可,要改寫成PHP、Ruby應該也不會是難事)
using System.Web;
@H_502_30@using System.Web.Script.Serialization;
using System.Collections.Generic;
@H_502_30@using System.Drawing;
using System.Reflection;
@H_502_30@using System.Linq;
@H_502_30@public class JsonDataSrc : IHttpHandler {
@H_502_30@ //模擬資料物件
class SimMemberInfo
@H_502_30@ {
string UserNo; //會員編號@H_502_30@ string UserName; //會員名稱
public DateTime RegDate; //註冊日期@H_502_30@ int Points; //累積點數
}@H_502_30@ static List<SimMemberInfo> _SimuDataStore = null;
//結果物件
@H_502_30@ class ResultData
{@H_502_30@ object Data;
int TotalCount;
@H_502_30@ }
@H_502_30@ void ProcessRequest (HttpContext context) {
if (_SimuDataStore == null)@H_502_30@ {
Random rnd = new Random();
@H_502_30@ //借用具名顏色名稱來產生隨機資料
string[] colorNames = typeof(Color)@H_502_30@ .GetProperties(BindingFlags.Static | BindingFlags.Public)
.Select(o => o.Name).ToArray();@H_502_30@ _SimuDataStore =
colorNames@H_502_30@ .Select(cn => new SimMemberInfo()
{@H_502_30@ UserNo = string.Format("C{0:00000}",rnd.Next(99999)),
UserName = cn,@H_502_30@ RegDate = DateTime.Today.AddDays(-rnd.Next(1000)),
Points = rnd.Next(9999)@H_502_30@ }).ToList();
}@H_502_30@ string keywd = context.Request["keywd"];
string sortField = context.Request["sort[0][field]"];@H_502_30@ string sortDir = context.Request["sort[0][dir]"]; @H_502_30@ //指定關鍵字時,使用Contains()對UserName進行比對
var res = _SimuDataStore.Where(o =>@H_502_30@ string.IsNullOrEmpty(keywd) || o.UserName.Contains(keywd));
if (!string.IsNullOrEmpty(sortField))@H_502_30@ {
//宣告一個函數可傳回SimMemberInfo之指定屬性值用於依動態欄位排序
@H_502_30@ Func<SimMemberInfo,string,255)">string> GetColString =
(o,c) =>@H_502_30@ {
switch (c)
@H_502_30@ {
case "UserNo": return o.UserNo;@H_502_30@ "UserName": return o.UserName;
"RegDate": return o.RegDate.ToString("yyyyMMdd");@H_502_30@ "Points": return o.Points.ToString();
default: throw new ArgumentException();@H_502_30@ }
};@H_502_30@ if (sortDir == "asc")
res = res.OrderBy(o => GetColString(o,sortField));@H_502_30@ else
res = res.OrderByDescending(o => GetColString(o,sortField));@H_502_30@ }
@H_502_30@
JavaScriptSerializer jss = new JavaScriptSerializer();
@H_502_30@ context.Response.ContentType = "text/plain";
int pageSize = 10,take = 10,skip = 0;
@H_502_30@ int.TryParse(context.Request["pageSize"],255)">out pageSize);
"take"],255)">out take);
@H_502_30@ "skip"],255)">out skip);
var paged = res.Skip(skip).Take(take);@H_502_30@ context.Response.Write(jss.Serialize(new ResultData()
{@H_502_30@ Data = paged.ToList(),
TotalCount = res.Count()@H_502_30@ }));
}@H_502_30@
bool IsReusable {
@H_502_30@ get {
false;
@H_502_30@ }
}@H_502_30@
}