在ASP.NET MVC 4中使用Kendo UI Grid
22 |
之前寫過用ASP.NET WebForm作為AJAX式資料源的Kendo UI Grid範例,最近計劃在一個小專案試用ASP.NET MVC 4 RC,面對的第一個需求又是"以清單呈現查詢結果",就來看看如何用ASP.NET MVC 4 RC滿足Kendo UI Grid的需求吧!
記得前一次用ashx寫資料端,花了不少功夫處理分頁及排序,而且還沒實做Filter過濾功能。但在ASP.NET MVC上,要整合Kendo UI Grid,則有很酷的方便選擇 -- KendoGridBinder!!
以下是我實做Kendo UI Grid + ASP.NET MVC 4的過程:
- 建立一個ASP.NET MVC 4專案
- 使用NuGet安裝KendoUIWeb及KendoGridBinder
- 借用上回的SimMemberInfo Model類別 ,放在Model目錄下:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Web;
namespace KendoGridMvc.Models
{
//模擬資料物件
public class SimMemberInfo
{
public string UserNo; //會員編號
public string UserName; //會員名稱
public DateTime RegDate; //註冊日期
public int Points; //累積點數
//模疑資料來源
public static List<SimMemberInfo> SimuDataStore = null;
static SimMemberInfo()
{
Random rnd = new Random();
//借用具名顏色名稱來產生隨機資料
string[] colorNames = typeof(Color)
.GetProperties(BindingFlags.Static | BindingFlags.Public)
.Select(o => o.Name).ToArray();
SimuDataStore =
colorNames
.Select(cn => new SimMemberInfo()
{
UserNo = string.Format("C{0:00000}", rnd.Next(99999)),
UserName = cn,
RegDate = DateTime.Today.AddDays(-rnd.Next(1000)),
Points = rnd.Next(9999)
}).ToList();
}
}
}
- 要引用Kendo UI,需要載入必要的JS及CSS,此時昨天介紹過的ASP.NET MVC打包壓縮功能馬上派上用場! 編輯App_Start/BundleConfig.cs,加入以下程式:
bundles.Add(new ScriptBundle("~/bundles/kendoUI").Include(
"~/Scripts/kendo/2012.1.322/kendo.web.min.js"
));
//經實測,SytleBundle virtualPath參數使用"2012.1.322"會有問題,故向上搬移一層
//將/Content/kendo/2012.1.322的內容搬至Content/kendo下
bundles.Add(new StyleBundle("~/Content/kendo/css").Include(
"~/Content/kendo/kendo.common.min.css",
"~/Content/kendo/kendo.blueopal.min.css"
));
PS: 此處有一個眉角:由於CSS檔路徑會被當成引用圖檔的基準,原本Kendo UI的.css及圖檔被放在~/Content/kendo/2012.1.322/下,理論上StyleBundle應設成"~/Content/kendo/2012.1.322/css”,才能引導瀏覽器到該目錄下取用圖檔。不幸地,我發現StyleBundle的virtualPath參數出現2012.1.322時,會導致Styles.Render("~/Content/kendo/2012.1.322/css”)時傳回HTTP 404錯誤~ 為克服問題,我決定將2012.1.322目錄的內容向上搬一層,直接放在~/Content/keno目錄下,並將virtualPath設成"~/Content/kendo/css",這樣就能避開問題。
[2014-10-13更新]關於此問題的深入探討。 - 為了省去每個View都要加掛Kendo UI JS及CSS的麻煩,我索性將它們加在~/Views/Shared/_Layout.cshtml中:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/themes/base/css", "~/Content/css",
"~/Content/kendo/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery", "~/bundles/kendoUI")
@RenderSection("scripts", required: false)
</head>
<body>
@RenderBody()
</body>
</html>
- 網頁Index.cshtml的Client端做法,則跟上回WebForm AJAX範例幾乎完全相同:
@section Scripts
{
<style>
body { font-size: 9pt; }
#dvGrid { width: 500px; }
span.hi-lite { color: red; }
#dvGrid th.k-header { text-align: center; }
</style>
<script>
$(function () {
//建立資料來源物件
var dataSrc = new kendo.data.DataSource({
transport: {
read: {
//以下其實就是$.ajax的參數
type: "POST",
url: "/Home/Grid",
dataType: "json",
data: {
//額外傳至後方的參數
keywd: function () {
return $("#tKeyword").val();
}
}
}
},
schema: {
//取出資料陣列
data: function (d) { return d.data; },
//取出資料總筆數(計算頁數用)
total: function (d) { return d.total; }
},
pageSize: 10,
serverPaging: true,
serverSorting: true
});
//JSON日期轉換
var dateRegExp = /^\/Date\((.*?)\)\/$/;
window.toDate = function (value) {
var date = dateRegExp.exec(value);
return new Date(parseInt(date[1]));
}
$("#dvGrid").kendoGrid({
dataSource: dataSrc,
columns: [
{ field: "UserNo", title: "會員編號" },
{ field: "UserName", title: "會員名稱",
template: '#= "<span class=\\"u-name\\">" + UserName + "</span>" #'
},
{ field: "RegDate", title: "加入日期",
template: '#= kendo.toString(toDate(RegDate), "yyyy/MM/dd")#'
},
{ field: "Points", title: "累積點數" },
],
sortable: true,
pageable: true,
dataBound: function () {
//AJAX資料Bind完成後觸發
var kw = $("#tKeyword").val();
//若有設關鍵字,做Highlight處理
if (kw.length > 0) {
var re = new RegExp(kw, "g");
$(".u-name").each(function () {
var $td = $(this);
$td.html($td.text()
.replace(re, "<span class='hi-lite'>$&</span>"));
});
}
}
});
//按下查詢鈕
$("#bQuery").click(function () {
//要求資料來源重新讀取(並指定切至第一頁)
dataSrc.read({ page: 1, skip: 0 });
//Grid重新顯示資料
$("#dvGrid").data("kendoGrid").refresh();
});
});
</script>
}
<div style="padding: 10px;">
關鍵字:
<input id="tKeyword" /><input type="button" value="查詢" id="bQuery" />
</div>
<div id="dvGrid">
</div>
- 最後來到重頭戲,負責以AJAX方式傳回資料的HomeController.cs的Grid() Action:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using KendoGridBinder;
using KendoGridMvc.Models;
namespace KendoGridMvc.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public JsonResult Grid(KendoGridRequest request, string keywd)
{
var result = SimMemberInfo.SimuDataStore.Where(o =>
string.IsNullOrEmpty(keywd) || o.UserName.Contains(keywd));
return Json(new KendoGrid<SimMemberInfo>(request, result));
}
}
}
是的,感謝Ryan Whitmire及Jose Ball的佛心與巧思,只要return Json(new KendoGrid<T>(KendoGridRequest, IEnumerable<T>)),餘下的換頁、排序,甚至欄位過濾功能,就都交給KendoGridBinder全權處理囉!
實際測試,換頁、排序功能一切正常,有了Kendo UI助陣,ASP.NET MVC之路順利多了~
Comments
# by linsf
請問「2.使用NuGet安裝KendoUIWeb及KendoGridBinder」這畫面要從那裡叫出來,謝謝。
# by Jeffrey
to linsf, 在Solution Explorer的指定專案按右鍵,選單中會有個"Manage NuGet Packages"(前題是VS要先安裝NuGet Package Manager擴充套件),參考: http://docs.nuget.org/docs/start-here/Managing-NuGet-Packages-Using-The-Dialog
# by Aeson
我遇到一个很严重的问题,我照着您的步骤做,在BundleConfig.cs那边加入那些代码,可是遇到一些问题,具体的问题在这里 http://www.kendoui.com/forums/mvc/general-discussions/vs2012-rtm-mvc4-kendo-bundle-not-rendering.aspx,所以我照着方法做在BundleConfig.cs那里加入了 // Clear all items from the default ignore list to allow minified CSS and JavaScript files to be included in debug mode bundles.IgnoreList.Clear(); // Add back the default ignore list rules sans the ones which affect minified files and debug mode bundles.IgnoreList.Ignore("*.intellisense.js"); bundles.IgnoreList.Ignore("*-vsdoc.js"); bundles.IgnoreList.Ignore("*.debug.js", OptimizationMode.WhenEnabled); ,问题是解决了可是马上又遇到了另一个一个问题,我从chrome的develop tools里看到以下这些error, Uncaught TypeError: Cannot call method 'ready' of undefined jquery:1 i.fn.i.init jquery:1 u jquery:1 n jquery:1 i.sub jquery:1 (anonymous function) kendojs:1 (anonymous function) kendojs:1 找了很久都解决不到,请问有人有遇过这样的问题吗?请问有人能帮我吗?
# by AndyYou
想請問 為何直接用 DbContext 子類別實體回傳 .ToList() 資料都會失敗。 例如: public JsonResult Grid(KendoGridRequest request) { List<Person> result = db.People.ToList(); var grid = new KendoGrid<Person>(request, result); return Json(grid); } 但是自己 new 一個全新的物件沒問題
# by Jeffrey
to AndyYou, 有失敗的詳細訊息嗎?
# by AndyYou
這邊是我測試的範例: Controller : http://goo.gl/yvvoK Action : Grid 裡面有三種測試 只有 ToList()有錯 而錯誤訊息是在js http://cl.ly/image/090y0Y2h2q0p View 中的 JS 我有參考原作在比對一下 http://goo.gl/WgpPg Model 是這個 http://goo.gl/IF09N 下圖則是我用監看式比對 應該只有差一點點吧 http://cl.ly/image/2S0b3Y1Q3P27
# by Jeffrey
to AndyYou, 由 http://cl.ly/image/090y0Y2h2q0p 判斷,最方下的/Daily/Grid傳回了一個HTTP 500,代表Server端程式發生Exception,訊息文字內容有16K,其中應該有進一步的線索,可以看一下當時傳回的錯誤訊息找方向。
# by lin
照你步驟做,出現錯誤,有無完整程式碼可下載 ? 0x800a1391 - JavaScript 執行階段錯誤: 'kendo' 未經定義
# by Jeffrey
to lin, kendo未經定義應是kendo.web.min.js未正確載入所致,建議用IE Dev Tool檢查一下。我猜一個可能原因是目前NuGet下載到的KendoUIWeb已更新至2013.2.716版,故BundleConfig.cs程式的~/Scripts/kendo/2012.1.322/kendo.web.min.js也要調整。
# by Shen
Hi,黑大,感謝您的這篇文章,我跑一個table來接是正常的,但遇到有複雜關聯的table就會出錯,不知道您有沒有遇到過EF循環參考的問題?我試過將JsonResult改為JSON.NET的回傳方式(可忽略循環參考),但JSON.NET似乎沒辦法解析KendoGridBinder產生的Data
# by Jeffrey
to Shen, 關於"JSON.NET似乎沒辦法解析KendoGridBinder產生的Data",可否再多提供一些錯誤細節?
# by Shen
hi,黑大,謝謝您的回覆,後來已經解決了,一樣是使用JSON.NET自訂Result即可解決循環參考的問題(我想是下午鬼打牆了...) 謝謝您 ^^
# by Tim
Hi,黑暗大前輩,我正在使用KendoUI的Pop Windows Create功能,後端是用ActionResult接,我想問的是ActionResult能直接轉換class,而不用類似Request["id"]去取得資料嗎?? js: create: { type: "POST", url: "/Service/CreateStaff", dataType: "json", } Controller [AcceptVerbs(HttpVerbs.Post)] public ActionResult CreateAdditional(Staff _staff) { //_staff都是Null,改用string或int等欄位接收就能接得到... return Content(""); }
# by Jeffrey
to Tim, 沒有很懂你的意思。ActionResult是Controller Action方法回傳的型別,不是用來接收前端送入的資料,應該不會跟Request["id"]放在一起比較。
# by Tim
不好意思描述不太清楚@@ 我想要的功能是類似官網的Gird pop window - add new record http://demos.kendoui.com/web/grid/editing-popup.html 照他的Sample Code在前端設定 create: { type: "POST", url: "/Service/CreateStaff", //Controller接收Ajax的Function dataType: "json", } 而後端其實就是接收Ajax post [AcceptVerbs(HttpVerbs.Post)] public ActionResult CreateStaff(Staff _staff) { //_staff class都是Null return Content(""); } 改用欄位指定就可以,但欄位太多挺麻煩的 [AcceptVerbs(HttpVerbs.Post)] public ActionResult CreateStaff(string StaffName) { return Content(""); } 我想讓後端可以接收自訂的class,應該是要在前端將新增的資料(object)做JSON.stringify,但我找不到KendoUI在哪下手 不知道黑大用KendoUI做Ajax Post新增時,都是用什麼方式?謝謝
# by Jeffrey
to Tim, 我在工作專案上通常會使用編輯UI透過自訂模組處理CRUD作業,倒沒用過KendoUI內建的編輯更新功能。由你的描述,應是KendoUI送出的資料格式無法靠Model Binder自動轉成MVC Action的輸入參數,我想透過一點客製技巧就可克服,這個議題挺有趣,容我找時間研究再寫範例。
# by Jeffrey
to Tim, 依我實測,Gird Pop Window會將編輯物件的所有屬性以POST Form形式傳至後端,理論上ASP.NET MVC可自動將其轉成你所定義成Staff _staff參數物件,但有個前題: Staff的StaffName要定義成屬性(有get; set;那種),我範例中的SimMemberInfo只寫成public string UserName;是不可行的,要改成public string UserName { get; set; },你看看是否問題出在這裡?
# by Ricky
Dear 黑暗大 , 可以請教您兩個問題嗎 , 謝謝 不知道 KendoGridBinder這個plugin,能不能搭配Web API用呢? 我嘗試過將後端改成以下code...好像jQuery都無法呼叫到... transport: { read: { data: { keyword: $('#txtFilter').val() }, type: "POST", url: "/Customer/GetCustomers", //url: "/api/Customer", dataType: "json" } }, public HttpResponseMessage Get(KendoGridRequest request, string keyword) { var result = CustomerService.GetCustomers(keyword); var jsonData = new KendoGrid<CustomerViewModel>(request, result); return Request.CreateResponse(HttpStatusCode.OK, jsonData); } 另外就是KendoUI open source好像要停止支援了...不知道黑暗大有什麼替代的Grid Freamwork嗎 謝謝您 ^^
# by Jeffrey
to Ricky, 基於WebAPI與MVC Action的行為差異,我想沒法直接在WebAPI裡用KendoGridBinder,但Telerik有篇Blog介紹過KendoGrid與WebAPI的整合方式: http://blogs.telerik.com/kendoui/posts/12-11-29/the_facts_on_using_kendo_ui_with_asp_net_webapi 公司之前就已買了Telerik元件授權,主要是經過評估,確定「花錢買元件避免大量腦細胞死亡」很划算! XD 我在工作上對Grid的依賴度極高,Telerik Grid元件提供的彈性與擴充性,能相抗衡的幾乎都是付費元件,以前KendoGrid屬於開源算是賺到,現在只能算回歸正常,仔細算算還是很值得,也不太會想花時間去尋找替代的免費方案。以上是我的經驗,與你分享。
# by Ricky
謝謝您耐心地回答 :)
# by Justin
想請問黑大前輩, 關於樓上Tim的問題,我也是感到困惑, 而且我是兩種方法都抓不到資料, 我資料來源於資料庫,目前要實作更新部分, 卡在KendoUI使用修改按鈕按下儲存的時候不知該如何下手, 後端是如何取得前端送來的資料呢? 根據 http://demos.telerik.com/aspnet-mvc/grid/editing-popup 與 http://demos.telerik.com/kendo-ui/grid/editing-popup 的方法嘗試失敗..
# by Jeffrey
to Justin, 嘗試失敗是指依照 http://demos.telerik.com/aspnet-mvc/grid/editing-popup 提供的GridController.EditingPopup_Update() 範例測試不成功嗎?