ASP.NET MVC 4 與 jqGrid 入門實作

這是篇逐步教學,主角是 ASP.NET MVC 4 和 jqGrid,目標是用 jqGrid 呈現資料,並且能夠分頁、排序。

2013-07-23 更新:
  • 用 jQuery 2.0.3 和 jQuery UI 1.10.3 和 jqGrid 4.4.4 重新時做一遍,將 RegisterBundles 方法重新抓圖,以減少練習時出錯的機會。
  • 增加一些注意事項。
  • 增加範例程式下載連結:Mvc4JQGridDemo.zip
前言

Phillip Haack 在 2009 年已經寫過一篇 ASP.NET MVC 搭配使用 jQuery Grid 的教學文章:Using jQuery Grid With ASP.NET MVC。該文的範例程式碼後來由 Tim Davis 翻新成 Visual Studio 2010 + .NET 4 + jQuery 1.4.4 + jqGrid 3.8.2

Davis 提供的範例原始碼,用 Visual Studio 2010 編譯和執行都沒有問題,可是我的 Visual Studio 2012 Update 1 連開啟專案都開不了,出現奇妙的 unsupported 錯誤訊息(八成是需要手動修改專案的組態檔吧)。另一方面,那個範例是用 Web Form 來作為 MVC 的 View。

於是,我用 Visual Studio 2012 + ASP.NET MVC 4 實作一遍,並將過程記錄下來。主要還是想試試 jqGrid。

開發環境

此範例會使用到的工具和技術如下:
  • Visual Studio 2012 Ultimate with Update 1
  • ASP.NET MVC 4
  • ADO.NET Entity Framework (非必要,只要能提供前端資料就行)
  • SQL Server + Northwind 資料庫(非必要,只要能提供前端資料就行)
  • jQuery 1.8.3 2.0.3
  • jQuery UI 1.9.2 1.10.3
  • jQuery jqGrid 4.4.1 4.4.4

整個練習步驟稍嫌冗長,因為是從建立 ASP.NET MVC 4 專案開始、到建立 Entity Framework 資料模型、建立 Controller,然後才談到 jqGrid 的部分。

如果想先看結果,請點這裡:執行結果的畫面截圖。如果已經熟悉 ASP.NET MVC 和 Entity Framework,只想看 jqGrid 的部分,可直接跳到 Step 4 或 Step 5。

Step 1: 建立專案

開啟 Visual Studio 2012,建立新的 ASP.NET MVC 4 Web Application 專案:


範本選擇 Basic,View engine 用預設的 Razor:

專案建立完成後,看一下 Content 和 Scripts 這兩個子目錄裡面有哪些東西:


可以看到,Visual Studio 內附的 jQuery 版本是 1.7.1,jQuery UI 是 1.8.20。稍後會用 NuGet 安裝新版本的套件。

Step 2:建立 MVC 的 M

如果不想使用 Entity Data Model,可直接跳過此步驟。

簡單起見,這裡直接把 Entity Model 建立在 Models 子目錄下:
  1. 在 Solution Explorer 中,專案的 Models 資料夾上點右鍵,選 Add \ ADO.NET Entity Data Model。
  2. 接著輸入 model 名稱:NorthwindModel。
  3. 選擇 Generate from database,建立資料連線,連接至 Northwind 資料庫,並選擇將連線字串儲存至組態檔。
  4. 選擇資料物件時,只挑 Customers 資料表就好,其餘不用。
底下是其中幾個關鍵步驟的截圖:



EF Data Model 建立完成後,先 Build 一下專案,免得待會建立 Controller 時沒有 Model class 可以挑選。

Step 3:建立 MVC 的 C 和 V

至 Solution Explorer,專案底下的 Controllers 資料夾上點右鍵,Add \ Controller...。然後,看圖:


利用此方式建立好 HomeController 之後,Views 資料夾裡面也會產生一個對應的 Home 目錄,其中有幾個對應至各個 action 的顯示頁面。

此時可以先看一下 HomeController.cs,工具已經幫我們產生好基本的程式碼,包括:建立 DbContext(我們的 NorthwindEntities)以及對應至各個 action 的方法。比如說,Index 方法會傳回全部的客戶資料:

View 的部分不是本文重點,暫且略過。

按 F5 以除錯模式執行應用程式,結果如下圖:

嗯,很陽春。接著試試 jqGrid。

Step 4:加入必要的套件

至 Solution Explorer,在專案名稱或其下的 Reference 項目上點右鍵,選擇 Manague NuGet Packages...。接著在對話窗右上方搜尋方塊中輸入「jquery jqgrid」,搜尋結果如下圖:

把圖中框起來的四個套件都安裝了吧!原先已經存在的 jQuery 1.7.x 和 jQuery UI 1.8.20 都會被替換成最新版本,也就是目前的 jQuery 1.8.3 和 jQuery UI 1.9.2。

Content 目錄底下會多出一些東西:

Step 5:打包 JavaScript 和 CSS 

打包,就是  ASP.NET 的 bundling 功能。在此範例中亦非必要,只是順便加進來練習罷了。

修改 App_Start 目錄下的 BundleConfig.cs,把先前新加入的 JavaScript 檔案連同 CSS 都一併綑起來。這裡只用畫面截圖顯示有變動的程式碼(如下圖),詳情可參考亞斯狼的 ASP.Net MVC中的Bundle 或黑暗執行緒的 ASP.NET MVC 4 RC的JS/CSS打包壓縮功能


請注意,如果你照著上圖中方框標示的區域來輸入程式碼,在輸入檔案的路徑和名稱時請務必一邊檢查自己的專案裡面是否真有符合的路徑和檔案名稱。比如說,有的 jQuery 版本的檔名是 jquery.jqGrid.src.js,有的則是 jquery.jqGrid.js。這個部分沒弄好,到後面執行程式時,可是會完全看不到資料的。
此外,在設定欲打包的檔案時,*.min.js 的檔案會被自動略過,故請勿打包 *.min.js 的檔案。

在伺服器端設定好要打包的檔案之後,前端網頁也得加入相應的程式碼才行。我們可以在所有網頁共用的 Views\Shared\_Layout.cshtml 中加入引用 JavaScript 的指令,像這樣:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Styles.Render("~/Content/themes/base/css")    
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryui")
    @Scripts.Render("~/bundles/jqueryjqgrid")
</head>
<body>
    @RenderBody()

    @RenderSection("scripts", required: false)
</body>
</html>

實務上,並非每個網頁都會用到 jqGrid,像上面的範例那樣在共用的 _Layout.cshtml 中引用全部的 JavaScript 檔案,並不是很好的作法。看到 </body> 結束前有個 @RenderSection("scripts", required: false) 嗎?這行敘述的用意是產生一個名為 "scripts" 的內容區塊,第二個參數帶入 false 表示若指定的區塊不存在也沒關係。這就是說,我們也可以利用這個預先挖好的坑:"scripts" 區塊,在各自的 View 當中利用 @section 來定義該區塊的內容,以便各個頁面載入自己需要的 JavaScript。詳情參見這篇文章,或這篇

Step 6:增加動作方法

HomeController 類別中既有的動作方法都保留不動,再新增一個方法:

public ActionResult JQGrid()
{
    return View();
}

注意此方法並沒有傳回任何資料,就只是預設的 view 而已。這是因為,網頁所需之資料將由它自己稍後以 Ajax 呼叫的方式向伺服器端取得。

動作方法寫好之後,接著要產生對應的 View:在此方法上點右鍵,選 Add View,然後看圖操作:


這樣會在 Views\Home\ 底下建立一個 JQGrid.cshtml,裡面僅短短數行程式碼。把它改成這樣(請注意第一行引用的模型類別,其命名空間必須符合你自己的專案名稱的命名空間。否則會出現編譯錯誤):

@model IEnumerable<Mvc4jqGrid.Models.Customer>

@{
    ViewBag.Title = "JQGrid";
}

<script type="text/javascript">

    jQuery(document).ready(function () {
        var grid = jQuery('#grid');
        grid.jqGrid({
            url: '/Home/GetCustomers/',
            datatype: 'json',
            jsonReader: {
                repeatitems: false
            },
            mtype: 'GET',
            colModel: [
                { name: 'CustomerID', label: '編號', width: 40, align: 'left' },
                { name: 'CompanyName', label: '公司名稱', width: 40, align: 'left' },
                { name: 'ContactName', label: '聯絡人', width: 40, align: 'left' },
                { name: 'ContactTitle', label: '職稱', width: 40, align: 'left' },
                { name: 'Address', label: '地址', width: 40, align: 'left' },
                { name: 'City', label: '城市', width: 20, align: 'left' }
            ],
            pager: '#pager',
            width: 660,
            height: 'auto',
            rowNum: 10,
            rowList: [5, 10, 20, 50],
            sortname: 'CustomerID',
            sortorder: "desc",
            viewrecords: true,
            caption: 'My first grid'
        });
    });
</script>

<h2>JQGrid</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table id="grid">
</table>
<div id="pager">
</div>


簡單地說,此網頁載入時會對伺服器發出 Ajax 呼叫,目標 URI 是 Home/GetCustomers/。因此,稍後還要回到 HomeController.cs 當中把這個函式補上。

注意 jsonReader 那個小區塊。由於稍後實作的 GetCustomers 方法所傳回的 JSON 字串會採用「帶名稱的資料格式」(例如 "CustomerID":"A001"),所以必須指定 jsonReader 的 repeatitems 參數為 false,否則程式執行時就只會看到一個空 grid(有欄位標題,沒有資料)。
摘自 jqGrid 說明文件
The repeatitems element tells jqGrid that the information for the data in the row is repeatable - i.e. the elements have the same tag cell described in cell element. Setting this option to false instructs jqGrid to search elements in the json data by name. This is the name from colModel or the name described with the jsonmap option in colModel.

Step 7:實作 GetCustomers 方法

在 HomeController 類別中加入以下程式碼:

/// <summary>
/// This method will be called via Ajax in Views/JQGrid.cshtml 
/// </summary>
/// <returns></returns>
public JsonResult GetCustomers(string _search, int? page, int? rows, string sord)
{
    var customers = db.Customers.ToList();

    int pageSize = rows.HasValue? rows.Value : 10;
    int pageNum = page.HasValue? page.Value : 1;
    int totalRecords = customers.Count;
    int totalPages = (int)Math.Ceiling((float)totalRecords / (float)pageSize);

    var jsonData = new
    {
        total = totalPages,
        page = pageNum,
        records = totalRecords,
        rows = customers.Skip((pageNum-1)*pageSize).Take(pageSize)
    };

    return Json(jsonData, JsonRequestBehavior.AllowGet);
}

此方法的幾個傳入參數,都是前端網頁的 jqGrid 呼叫所傳遞過來的(其實還有別的參數,可用 Fiddler 觀察之,或查 jqGrid 文件),主要用來處理分頁邏輯。由於參數名稱與前端網頁 jqGrid 送出的 HTTP request 的參數名稱相同,ASP.NET MVC 會自動幫我們配對並設定正確的參數值,所以這些參數的名稱不要亂改。但如果你先不想處理分頁,把這些參數全部刪除也是 OK 的。

其中的 jsonData 匿名型別包含四個屬性:total(總頁數)、page(目前要顯示第幾頁)、records(資料筆數)、rows(每頁顯示幾筆)。先知道這樣就好,更完整的說明請參考 jqGrid 官方文件

Step 8:Run and Pray

按 F5,亦即以除錯模式執行應用程式,然後把瀏覽器的網址列後面加上「Home/JQGrid」,然後雙手合十,看看結果是否如下圖:


如果 HomeController 的 GetCustomers 方法有提供正確格式的 JSON 資料,而且處理分頁的邏輯沒有寫錯,網頁應該能順利呈現一個美觀的 jqGrid,下方的換頁按鈕也能夠正確換頁。右上角的黑色圓鈕可收合/展開這個 grid。

問題排除

若整個網頁空蕩蕩的,連欄位標題都沒有,請檢查 step 5 是否有漏掉什麼。你可以利用瀏覽器的檢視原始碼功能來確認網頁是否有加入所有必要的 JavaScript 和 CSS,尤其是 jquery-*.js、jquery-ui-*.js、以及 jquery.jqgrid.js。參考以下範例:

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>JQGrid</title>
    <link href="/Content/site.css" rel="stylesheet"/>
    <link href="/Content/jquery.jqGrid/ui.jqgrid.css" rel="stylesheet"/>

    <link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet"/>
    <link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet"/>

    <script src="/Scripts/modernizr-2.6.2.js"></script>

    <script src="/Scripts/jquery-2.0.3.js"></script>

    <script src="/Scripts/jquery-ui-1.10.3.js"></script>

    <script src="/Scripts/jquery.jqGrid.js"></script>
<script src="/Scripts/i18n/grid.locale-tw.js"></script>

</head>

有出現欄位標題,卻沒顯示任何資料,那有可能是 GetCustomers 方法所傳回的 JSON 資料不符合 jqGrid 的規定。請檢查 step 7 和 step 8,並且利用瀏覽器來查看伺服器端傳回的資料內容,例如在網址列輸入:
http://主機名稱/Home/GetCustomers?_search=&page=1&rows=10&sord=desc

小結

這個實作練習只用到了 jqGrid 的一點點基本功能:呈現多筆資料並提供分頁和排序。往後可以繼續補強,完成 CRUD 這四種基本操作。另外有人針對 jqGrid 寫了 HTML 輔助類別,讓我們用 C# 語法來產生前端網頁所需要的 jqGrid 指令碼,例如 jqSuite(jqGrid 官方網站提供的商業套件)、Lib.Web.MvcjqGrid HTML HelperMvcJqGird 等等。這類輔助工具,我覺得還是在熟悉 jqGrid 之後再來考慮是否採用,會比較好。

除了 jqGrid,還有幾款 grid 套件似乎也是不錯的選擇,例如:jQuery DataTablesKendo UI grid 和 SlickGrid。改天有空試試。

下載完整範例原始碼:Mvc4JQGridDemo.zip

續集:切換 jQuery UI 的佈景主題

延伸閱讀

Post Comments

技術提供:Blogger.