網頁

2012年3月12日 星期一

ASP.NET MVC 3 分頁 - 使用jQuery.Templates + MvcSimplePostPager


上一篇文章「ASP.NET MVC 3 分頁 - 使用jQuery.Templates + jQuery Pagination Plugin」裡,

我們使用了「jQuery Pagination Plugin」這個jQuery Plugin來做為前端頁面Pager的解決方案,

然而這個 Pager 所提供的UI介面是有些陽春,

image

假如想要增加一些資料在上面讓使用者可以得到更多資訊的話,就要去更改「jquery.pagination.js」程式內容,

但其實在之前介紹「資料分頁」的時候,我們就已經建立了 MvcSimplePager 與 MvcSimplePostPager ,

這兩種Pager就已經提供了相當充足的分頁資訊,

接下來就會介紹如何以前端的jQuery Ajax的方式與jQuery Templates來完成分頁的需求功能。

 



進入主題前,先回顧一下之前的MvcSimplePager與MvcSimplePostPager,

 

MvcSimplePager

ASP.NET MVC - 資料分頁(2) 自訂分頁功能列 MvcSimplePager

這個Pager是單純使用Http Get的方式將分頁的Index傳送到後端後再去產生Pager以及顯示分頁的資料內容,

比較適合不需要做分類選擇而且不必帶其他參數的分頁資料顯示。

image

 

MvcSimplePostPager

ASP.NET MVC - 資料分頁(3) 自訂分頁功能列 MvcSimplePostPager

為了要保持表單上的分類選擇狀態以及顯示該分類的分頁資料,所以原本Http Get方式的Pager就無法沿用,

所以就以原本MvcSimplePager的Html.Helper架構再設計一個可供使用Http Post方式的Pager,

前端再搭配jQuery的事件來完成在使用Form的情境下的狀態保持及資料分頁的功能。

image

ASP.NET MVC - 資料分頁(4) MvcSimplePostPager + AJAX

MvcSimplePostPager搭配使用jQuery Ajax來完成需求功能,Pager以及分頁資料是後端輸出的部分檢視,

所以每一次的分頁除了分類的下拉選單不會重新Render外,其他的地方都會重新Render,

而這些變化都是以AJAX來完成,所以不會有畫面Reload的情況發生。

image

 

而這一次我們要做的分頁功能,每一個分類的分頁資料都還是維持使用jQuery.Templates方式來顯示分頁資料,

Pager的部份則會使用MvcSimplePostPager,因為MvcSimplePostPager上面的每個A元素都沒有另外指定事件,

所以當動態輸出到前端頁面時,就可以動態的去綁定前端處理事件,

不過這一次的作法會有所不同,之前所假設的情境是資料來源是同一個網站,而這一次我們必須假設資料來源有可能是其他的網站,

廢話不多說,就直接進入程式實作說明。

 


Controller

Action「PageMethod2」:此次分頁功能的主要進入頁面。

public ActionResult PageMethod2()
{
    ViewData["CategoryDDL"] = categoryService.GenerateCategoryDDL(null);
    return View();
}

 

Action「PagerContent」:接收前端所傳來的pageIndex, pageSize, totalItemCount參數資料,以PartialView回傳Pager內容。

public ActionResult PagerContent(int? page, int? pageSize, int? totalItemCount)
{
    int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
    ViewData["CurrentPage"] = currentPageIndex;
 
    int page_size = pageSize.HasValue ? pageSize.Value : 10;
    ViewData["PageSize"] = page_size;
 
    int totalCount = totalItemCount.HasValue ? totalItemCount.Value : 0;
    ViewData["TotalItemCount"] = totalCount;
 
    return PartialView();
}

 

Action「ProductCount」:取得分類產品的總數。

[HttpPost]
public JsonResult ProductCount(string categoryId)
{
    Dictionary<string, object> jo = new Dictionary<string, object>();
 
    if (string.IsNullOrWhiteSpace(categoryId))
    {
        jo.Add("Msg", "Please Select a Category");
        return this.Json(jo);
    }
 
    int id = 0;
    bool parseResult = int.TryParse(categoryId, out id);
    if (!parseResult)
    {
        jo.Add("Msg", "Wrong Category ID");
        return this.Json(jo);
    }
 
    var result = this.productService.GetCollectionBy(id);
    jo.Add("count", result.Count());
    return this.Json(jo);
}

 

Action「PageContent」:取得指定分類以及指定分頁的產品資料,回傳JSON格式的資料。

[HttpPost]
public JsonResult PageContent(int? page, string categoryId)
{
    Dictionary<string, string> jo = new Dictionary<string, string>();
 
    if (string.IsNullOrWhiteSpace(categoryId))
    {
        jo.Add("Msg", "Please Select a Category");
        return this.Json(jo);
    }
 
    int id = 0;
    bool parseResult = int.TryParse(categoryId, out id);
 
    if (!parseResult)
    {
        jo.Add("Msg", "Wrong Category ID");
        return this.Json(jo);
    }
 
    var result = this.productService.GetCollectionBy(id);
 
    int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
    ViewData["CurrentPage"] = currentPageIndex;
    var dataPaged = result.ToPagedList(currentPageIndex, 5);
 
    List<Dictionary<string, object>> ja = new List<Dictionary<string, object>>();
 
    foreach (var item in dataPaged)
    {
        Dictionary<string, object> product = new Dictionary<string, object>();
 
        product.Add("ProductID", item.ProductID);
        product.Add("ProductName", item.ProductName);
        product.Add("SupplierID", item.SupplierID);
        product.Add("CategoryID", item.CategoryID);
        product.Add("QuantityPerUnit", item.QuantityPerUnit);
        product.Add("UnitPrice", String.Format("{0:F}", item.UnitPrice));
        product.Add("UnitsInStock", item.UnitsInStock);
        product.Add("UnitsOnOrder", item.UnitsOnOrder);
        product.Add("ReorderLevel", item.ReorderLevel);
        product.Add("Discontinued", item.Discontinued);
 
        ja.Add(product);
    }
 
    return this.Json(ja);
}  

 

View

PagerContent.cshtml:產出Pager的內容,這邊沒有任何的Model資料。

@using MVC3_Page.Helpers;
 
<div id="pager" class="pager">
    @if (ViewData["CurrentPage"] != null && ViewData["PageSize"] != null && ViewData["TotalItemCount"] != null)
    { 
        int currentPage = int.Parse(ViewData["CurrentPage"].ToString());
        int pageSize = int.Parse(ViewData["PageSize"].ToString());
        int totalItemCount = int.Parse(ViewData["TotalItemCount"].ToString());
        @Html.Raw(Html.MvcSimplePostPager(currentPage, pageSize, totalItemCount, true)); 
    } 
</div>

 

PageMehtod2.cshtml:其中ID是PagerWrapperTop的DIV,為放置Pager的容器。

@{
    ViewBag.Title = "PageMethod2";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
 
<div id="TableContent" class="div_default">
 
    <h2>PageMethod2</h2>
 
    <div id="formArea" style="width: 90%; margin-left: auto; margin-right: auto;">
        Category:@Html.Raw(ViewData["CategoryDDL"].ToString())
        <input type="button" id="ButtonSearch" value="List" />
    </div>
    <hr />
    <p></p>
    <div id="Information" style="display: none; width: 90%; margin-left: auto; margin-right: auto; background-color: #fffeee;">
        Category:<span id="CategoryName" style="font-weight: bold;"></span>
        The number of products:<span id="ProductNumber" style="font-weight: bold;"></span>
        <p></p>
    </div>
    
    <div id="PagerWrapperTop" class="PagerWrapper" style="text-align:left; : 90%; margin-left: auto; margin-right: auto;">
    </div>
    <br style="clear:both;" />
 
    <table id="table1" class="grid" style="width: 90%; margin-left:auto; margin-right:auto; background-color: #fffeee;">
        <thead>
            <tr>
                <th>
                    ProductID
                </th>
                <th>
                    ProductName
                </th>
                <th>
                    SupplierID
                </th>
                <th>
                    CategoryID
                </th>
                <th>
                    QuantityPerUnit
                </th>
                <th>
                    UnitPrice
                </th>
                <th>
                    UnitsInStock
                </th>
                <th>
                    UnitsOnOrder
                </th>
                <th>
                    ReorderLevel
                </th>
                <th>
                    Discontinued
                </th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
    <p></p>
</div>

 

PageMethod2.cshtml 分頁資料的Templates內容:

<script id="TemplateProduct" type="text/x-jquery-tmpl">
    <tr id="tr_${ProductID}">
        <td>
            ${ProductID}
        </td>
        <td>
            ${ProductName}
        </td>
        <td>
            ${SupplierID}
        </td>
        <td>
            ${CategoryID}
        </td>
        <td>
            ${QuantityPerUnit}
        </td>
        <td>
            ${UnitPrice}
        </td>
        <td>
            ${UnitsInStock}
        </td>
        <td>
            ${UnitsOnOrder}
        </td>
        <td>
            ${ReorderLevel}
        </td>
        <td>
            ${Discontinued}
        </td>
    </tr>
</script>

 

PageMehtod2.cshtml JavaScript

開頭:

var pageSize = 5;
var totalCount = 0;
var selected_Category_ID = '';
var selected_Category_Text = '';
 
$(document).ready(function ()
{
    $('#CategoryDDL option:eq(0)').attr('selected', true);
    $('#ButtonSearch').click(function () { ButtonSearchEventHandler() });
});

ButtonSearchEventHandler:

取得分類的產品總數以及該分類的第一頁分頁資料內容。

function ButtonSearchEventHandler()
{
    this.selected_Category_ID = $.trim($('#CategoryDDL option:selected').val());
    this.selected_Category_Text = $.trim($('#CategoryDDL option:selected').text());
 
    if (selected_Category_ID.length == 0)
    {
        alert('Please select a Category.');
        return false;
    }
    else
    {
        //取得分類的產品總數
        CategoryProductCount(selected_Category_ID);
 
        //取得產品分頁內容
        GetCategoryProducts();
    }
}

CategoryProductCount(取得分類的產品總數):

function CategoryProductCount(categoryId)
{
    var count = 0;
 
    if (categoryId.length == 0)
    {
        return count;
    }
    else
    {
        $.ajax({
            url: '@Url.Action("ProductCount", "Home")',
            data: { categoryId: categoryId },
            type: 'post',
            async: false,
            cache: false,
            dataType: 'json',
            success: function (data)
            {
                if (!data.Msg)
                {
                    count = parseInt(data.count, 10);
                    totalCount = count;
                }
            }
        });
    }
    return false;
}

GetCategoryProducts(取得產品的分頁資料內容):

先取得分頁的資料並顯示於頁面上之後,再去處理Pager的顯示內容以及Javascript事件綁定。

function GetCategoryProducts(pageIndex)
{
    if (selected_Category_ID.length == 0 || selected_Category_Text.length == 0)
    {
        alert('Please select a Category.');
        return false;
    }
    else
    {
        if (pageIndex == undefined || pageIndex.length == 0)
        {
            pageIndex = 1;
        }
 
        $.ajax({
            url: '@Url.Action("PageContent", "Home")',
            data: { page: pageIndex, categoryId: parseInt(selected_Category_ID, 10) },
            type: 'post',
            async: false,
            cache: false,
            dataType: 'json',
            success: function (data)
            {
                $('#table1 tbody').empty();
                $('#CategoryName').empty();
                $('#ProductNumber').empty();
 
                if (data.Msg)
                {
                    $('#Information').hide();
                    alert(data.Msg);
                }
                else
                {
                    $.each(data, function (i)
                    {
                        var item =
                        {
                            ProductID: data[i]["ProductID"],
                            ProductName: data[i]["ProProductNameductID"],
                            SupplierID: data[i]["SupplierID"],
                            CategoryID: data[i]["CategoryID"],
                            QuantityPerUnit: data[i]["QuantityPerUnit"],
                            UnitPrice: data[i]["UnitPrice"],
                            UnitsInStock: data[i]["UnitsInStock"],
                            UnitsOnOrder: data[i]["UnitsOnOrder"],
                            ReorderLevel: data[i]["ReorderLevel"],
                            Discontinued: data[i]["Discontinued"]
                        };
 
                        $('#table1 tbody').append($('#TemplateProduct').tmpl(item));
                    });
 
                    $('#table1 tbody tr:odd').css('background-color', '#F5FBFC')
 
                    $('#ProductNumber').html(totalCount);
                    $('#CategoryName').html(selected_Category_Text);
                    $('#Information').show();
 
                    //PageBar
                    PagerContent_Init(pageIndex)
                }
                return false;
            }
        });
    }
}

 

PagerContent_Init(顯示Pager內容):

向後端的Action「PagerContent」取得PagerContent.cshtml的部分檢視內容。

function PagerContent_Init(pageIndex)
{
    $.ajax(
    {
        url: '@Url.Action("PagerContent", "Home")',
        data: { page: pageIndex, pageSize: pageSize, totalItemCount: totalCount },
        type: 'post',
        async: false,
        cache: false,
        dataType: 'html',
        success: function (data)
        {
            $('#PagerWrapperTop').html(data);
            Pager_Setting();
        }
    });
}

 

Pager_Setting(Pager內容的事件綁定):

//將Pager上面的連結項目的行為分離出來
//使用class方式做行為判別與動作執行
function Pager_Setting()
{
    $('.PostPager').each(function (i, item)
    {
        $(item).css('cursor', 'pointer');
    });
    $('.PostPager.first-page').click(function () { GoToPage(1, selected_Category_ID); });
    $('.PostPager.previous-page,.next-page,.last-page,.number-page').click(function ()
    {
        GoToPage($(this).attr('value'), selected_Category_ID);
    });
}
 
function GoToPage(pageIndex, selectedCategoryID)
{
    if (selectedCategoryID.length == 0 || selectedCategoryID == 0)
    {
        alert('Please select a Category.');
        return false;
    }
    else
    {
        if ($.trim($('#CategoryDDL option:selected').val()) != selectedCategoryID.toString())
        {
            $('#CategoryDDL option[value=' + selectedCategoryID + ']').attr('selected', true);
        }
 
        GetCategoryProducts(pageIndex);
    }
}

 

最後完成的分頁功能:

ASP.NET MVC 3 + jQuery Templates + MvcSimplePostPager

 


其實有關於資料分頁的處理有相當多種,必須依照你的需求來決定什麼樣的方式比較適合專案,

之前我做某一個MVC專案時,因為分頁的功能比較晚開發,所以一開始只是以MvcPaging的基本樣式來完成分頁,

一開始這種單純以Http Get(以連結的方式使用網址加上參數)的分頁還可以應付基本的分頁需求,

但是當系統功能加上表單也有了分類資料等類似的輸入條件時,就必須要想辦法讓頁面可以有分頁功能又要可以保持表單狀態,

一開始還會使用Session的方式來保持頁面表單的條件狀態,雖然可以達到同樣的結果,但是此舉卻遭到專案另一個團隊成員的「嫌棄」,

說什麼用Session很LOW、很笨、很耗記憶體之類的,於是請他就此需要來開發一套符合他要求的分頁套件,

我想大家都應該知道,當專案的團隊成員有這麼一個要挑剔又愛抱怨什麼功能不足的人,這個人十之八九會是個「嘴砲王」!

只會在那邊出張嘴,真正叫他做點事來看看卻什麼也拿不出來,唉~遇到這樣的團隊成員時,就是顯出其他團隊成員的真正價值的時候,

在當時就依據MvcPaging的原形而衍生出可以與Form表單整合的分頁,不過那時候的PostPager是在有限時間下倉促完成的,

以致於之後會再另外設計為「MvcSimplePager」以及「MvcSimplePostPager」,

之前與朋友聚會的時候,聽到在資料來源是外部網站時也可以利用jQuery.Templates來做分頁,所以就發展出jQuery.Templates相關的三篇文章,

之後還會就jQuery.Templates來說明以往在專案開發時的其他應用,如果各位不嫌棄的話就小小期待一下吧!

 

以上

沒有留言:

張貼留言