上一篇資料分頁的文章「ASP.NET MVC - 資料分頁(2) 自訂分頁功能列 MvcSimplePager」最後有說到,
當表單查詢遇到資料分頁時,用原本的資料分頁方式會出現問題,
因為資料分頁於跳頁時是利用HyperLink將頁數資料以Get傳回後端處理,查詢條件就無法一併送到後端去處理,
為了達到這個需求,所以就把原本的MvcSimplePostPager做了一些修改,
讓前端的分頁功能列可以將查詢表單上的條件一起傳回後端,送回前端的資料一樣是維持查詢條件,
所以這篇文章就是說明如何製作一個使用「Post」方式的資料分頁方式。
因為上篇文章所做出來的分頁功能叫做「MvcSimplePager」,
這次主要是要做一個用Post方式將查詢條件與頁數送回後端的分頁功能,所以就叫做「MvcSimplePostPager」。
另外這個MvcSimplePostPager除了ASP.NET MVC的程式需要撰寫外,
在前端部分也會需要用到jQuery,以達到將查詢條件、頁數使用Post送到後端的操作。
一、PagerHelper.cs
在原本已經建立SimplePager()方法的PagerHelper.cs裡面在去增加兩個方法,方法名稱都是 MvcSimplePostPager,
第一個是預設方法,為預設不顯示頁數連結,
第二個則是主要實作的方法,可以指定是否顯示頁數連結的bool參數。
MvcSimplePostPager的方法參數比MvcSimplePager多了一個 currentPage,
這是因為原本MvcSimplePager是以HyperLink方式將頁數以URI傳送回後端,
可以使用「html.ViewContext.HttpContext.Request.QueryString」去取得URI裡面要指定跳頁的頁數資料,
而使用Post的話,則需要在前端將要跳頁的頁數給放到表單中的一個Hidden Field中,讓這個參數傳回到後端,
所以後端的MvcSimplePostPager方法要多個參數來接前端所傳過來的頁數。
1: #region MvcSimplePostPager2:3: /// <summary>
4: /// Pagers the specified HTML using POST.
5: /// </summary>
6: /// <param name="html">The HTML.</param>
7: /// <param name="pageSize">Size of the page.</param>
8: /// <param name="totalCount">The total count.</param>
9: /// <returns></returns>
10: public static string MvcSimplePostPager(this HtmlHelper html, int currentPage, int pageSize, int totalCount)11: {12: return MvcSimplePostPager(html, currentPage, pageSize, totalCount, false);13: }14:15: /// <summary>
16: /// Pagers the specified HTML using POST.
17: /// </summary>
18: /// <param name="html">The HTML.</param>
19: /// <param name="currentPage">The current page.</param>
20: /// <param name="pageSize">Size of the page.</param>
21: /// <param name="totalCount">The total count.</param>
22: /// <param name="showNumberLink">if set to <c>true</c> [show number link].</param>
23: /// <returns></returns>
24: public static string MvcSimplePostPager(this HtmlHelper html, int currentPage, int pageSize, int totalCount, bool showNumberLink)25: {26: if (totalCount == 0)
27: {28: return string.Empty;29: }30: else
31: {32: currentPage++;33:34: //總頁數
35: var totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1);36:37: var _renderPager = new StringBuilder();
38:39: if (currentPage <= 0)
40: {41: currentPage = 1;42: }43:44: string emptyAtagFormat = "<a href=\"#\" style=\"cursor:pointer;\">{0}</a>";45:46: if (totalPages == 1)
47: {48: _renderPager.AppendFormat(emptyAtagFormat, "第一頁");
49: _renderPager.AppendFormat(emptyAtagFormat, "上一頁");
50: _renderPager.AppendFormat(emptyAtagFormat, "下一頁");
51: _renderPager.AppendFormat(emptyAtagFormat, "最後一頁");
52: }53: else if (totalPages > 1)54: {55: #region 處理首頁連接56:57: if (currentPage != 1)
58: {59: _renderPager.Append("<a class=\"PostPager first-page\">第一頁</a>");
60: }61: else
62: {63: _renderPager.AppendFormat(emptyAtagFormat, "第一頁");
64: }65: #endregion66:67: #region 處理上一頁的連接68:69: if (currentPage > 1)
70: {71: _renderPager.AppendFormat("<a class=\"PostPager previous-page\" value=\"{0}\">上一頁</a>", currentPage - 1);
72: }73: else
74: {75: _renderPager.AppendFormat(emptyAtagFormat, "上一頁");
76: }77: #endregion78:79: #region 顯示頁數連結80: if (showNumberLink)
81: {82: var pageCount = (int)Math.Ceiling(totalCount / (double)pageSize);83: const int nrOfPagesToDisplay = 10;84:85: var start = 1;86: var end = pageCount;87:88: if (pageCount > nrOfPagesToDisplay)
89: {90: var middle = (int)Math.Ceiling(nrOfPagesToDisplay / 2d) - 1;
91: var below = (currentPage - middle);92: var above = (currentPage + middle);93:94: if (below < 4)
95: {96: above = nrOfPagesToDisplay;97: below = 1;98: }99: else if (above > (pageCount - 4))100: {101: above = pageCount;102: below = (pageCount - nrOfPagesToDisplay);103: }104:105: start = below;106: end = above;107: }108:109: if (start > 3)
110: {111: _renderPager.AppendFormat("<a class=\"PostPager number-page\" value=\"{0}\">{0}</a>", "1");112: _renderPager.AppendFormat("<a class=\"PostPager number-page\" value=\"{0}\">{0}</a>", "2");113: _renderPager.Append("...");
114: }115:116: for (var i = start; i <= end; i++)
117: {118: if (i == currentPage || (currentPage <= 0 && i == 0))
119: {120: _renderPager.AppendFormat("<span class=\"current\">{0}</span>", i);
121: }122: else
123: {124: _renderPager.AppendFormat("<a class=\"PostPager number-page\" value=\"{0}\">{0}</a>", i.ToString());
125: }126: }127: if (end < (pageCount - 3))
128: {129: _renderPager.AppendFormat("...");
130: _renderPager.AppendFormat("<a class=\"PostPager number-page\" value=\"{0}\">{0}</a>", (pageCount - 1).ToString());
131: _renderPager.AppendFormat("<a class=\"PostPager number-page\" value=\"{0}\">{0}</a>", pageCount.ToString());
132: }133: }134: #endregion135:136: #region 處理下一頁的連結137:138: if (currentPage < totalPages)
139: {140: _renderPager.AppendFormat("<a class=\"PostPager next-page\" value=\"{0}\">下一頁</a>", currentPage + 1);
141: }142: else
143: {144: _renderPager.AppendFormat(emptyAtagFormat, "下一頁");
145: }146:147: #endregion148:149: #region 最後一頁150: if (currentPage != totalPages)
151: {152: _renderPager.AppendFormat("<a class=\"PostPager last-page\" value=\"{0}\">最後一頁</a>", totalPages);
153: }154: else
155: {156: _renderPager.AppendFormat(emptyAtagFormat, "最後一頁");
157: }158: #endregion159: }160:161: // 目前頁數/總頁數
162: _renderPager.AppendFormat(" 第 {0} 頁 / 共 {1} 頁 共 {2} 筆", currentPage, totalPages, totalCount);
163:164: return _renderPager.ToString();
165: }166: }167:168: #endregion
在上面的程式中,雖然每個操作跳頁的element還是一樣使用HyperLink,但仔細看看,每個HyperLink並沒有指定href,
而且每個HyperLink都有指定class「PostPager」以及幾個不同的class
「first-page」「previous-page」「next-page」「last-page」「number-page」
這幾個class是為了要給前端的jQuery操作所使用的,稍後在說明前端網頁的處理時會再仔細的解釋。
二、Controller/Action
在Controller的Action部分就會有所不同了,首先一開始的頁面是還沒有任何查詢條件,所以也沒有資料可以顯示,
如下圖:
先直接看程式的部份:
1: #region PageMethod32: /// <summary>
3: /// Pages the method3.
4: /// </summary>
5: /// <param name="page">The page.</param>
6: /// <returns></returns>
7: public ActionResult PageMethod3(int? page)8: {9: return PageMethod3(page, new FormCollection());10: }11:12: /// <summary>
13: /// Pages the method3.
14: /// </summary>
15: /// <param name="page">The page.</param>
16: /// <param name="formCollection">The form collection.</param>
17: /// <returns></returns>
18: [HttpPost]19: public ActionResult PageMethod3(int? page, FormCollection formCollection)20: {21: ViewData["CategoryDDL"] = categoryService.GenerateCategoryDDL(formCollection["CategoryDDL"] ?? string.Empty);22:23: if (formCollection.AllKeys.Length.Equals(0))
24: {25: return View();
26: }27: else
28: {29: int check = 0;
30: if (!int.TryParse(formCollection["CategoryDDL"], out check))31: {32: return View();
33: }34: else
35: {36: int categoryId = int.Parse(formCollection["CategoryDDL"]);37: var result = this.productService.GetCollectionBy(categoryId);
38:39: int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
40: ViewData["CurrentPage"] = currentPageIndex;
41: return View(result.ToPagedList(currentPageIndex, 5));
42: }43: }44: }45: #endregion
建立兩個同名方法,第一個方法是一開始進入頁面所執行的Action,而第二個方法則是實作的部份,主要的運作都是在第二個方法中,
一開始進入頁面是進入到第一個Action,因為沒有任何查詢條件,所以直接return第二個Action的內容,而FormCollection則是建立一個instance傳入,
第二個Action一開始就是先處理查詢表單所要顯示的Category下拉選單,當FormCollection裡面有包含Category下拉選單的選取值時,
將FormCollection[“CategoryDDL”]的值傳給產生下拉選單Html Tag的程式,如此一來產生的下拉選單就可以保留選取狀態,
再來就是當FormCollection裡面有包含前端送出的CurrentPage資料時,再加上FormCollection[“CategoryDDL”]資料,這樣就可以取得分頁資料。
另外我們還必須將CurrentPage資料放到ViewData中,讓前端頁面使用,產生分頁功能列時才能夠知道目前的顯示頁數。
這邊處補充一下有關產生下拉選單的程式部分,
1: /// <summary>
2: /// Generates the category DDL.
3: /// </summary>
4: /// <param name="selectedValue">The selected value.</param>
5: /// <returns></returns>
6: public string GenerateCategoryDDL(string selectedValue)7: {8: string tagIdName = "CategoryDDL";9:10: Dictionary<string, string> optionData = new Dictionary<string, string>();11:12: var query = this.GetCollection();
13: foreach (var item in query)14: {15: if (optionData.Where(x => x.Key.Equals(item.ToString())).Count().Equals(0))
16: {17: optionData.Add(item.ToString(), item.CategoryID.ToString());18: }19: }20:21: string _html = DropDownListHelper.GetDropdownList(tagIdName, tagIdName, optionData, null, selectedValue, true, null);22: return _html;
23: }24:
三、前端頁面
查詢表單部分:
<div id="formArea" style="width: 90%; margin-left:auto; margin-right:auto;"><% using(Html.BeginForm("PageMethod3", "Home", FormMethod.Post, new { id = "formPager" })) { %>Category:<%= ViewData["CategoryDDL"] %><input type="button" id="ButtonSearch" value="List" /><% } %></div>
要注意到表單的傳送方法,一定要使用Post,並且一定要給表單一個Element ID,這樣接下來的前端Script才能藉由ID取得表單。
前端頁面放置分頁功能列的部份就有了一些改變,先來看程式的內容:
<div id="pager" class="pager"><%
if (Model != null){int currentPage = ViewData["CurrentPage"] == null ? 0 : int.Parse(ViewData["CurrentPage"].ToString());Response.Write(Html.MvcSimplePostPager(currentPage, Model.PageSize, Model.TotalItemCount, true));}%>
</div>
這邊要去判斷Model是否還有資料,沒有資料就不需要顯示分頁功能列,而有資料時才顯示,
在Controller的Action中有將CurrentPage資料給放到ViewData裡,而MvcSimplePostPager方法就會使用到這個CurrentPage資料。
四、前端Script
<script language="javascript" type="text/javascript"><!--$(document).ready(function (){$('#table1 tr:odd').css('background-color', '#F5FBFC');PostPager();});$('#ButtonSearch').click(function ()
{var categoryId = $.trim($('#CategoryDDL option:selected').val());
if (categoryId.length == 0){alert('Please select Category.');
return false;}else
{$('#formPager').submit();}});//================== Post方式做換頁 =========================
//將Pager上面的連結項目的行為分離出來
//使用class方式做行為判別與動作執行
function PostPager()
{$('.PostPager').each(function (i, item)
{$(item).attr('href', '#');});$('.PostPager.first-page').click(function () { GoToPage(1); });
$('.PostPager.previous-page,.next-page,.last-page,.number-page').click(function ()
{GoToPage($(this).attr('value'));
});}//預設的Form表單名稱為 formPager
function GoToPage(page)
{var targetFormID = '#formPager';
if ($(targetFormID).size() > 0)
{$('<input>').attr({ type: 'hidden', id: 'page', name: 'page', value: page }).appendTo($(targetFormID));
$(targetFormID).submit();}}//================== Post方式做換頁 =========================
--></script>
在document於載入完成後就執行 PostPager(),這個function就是綁定分頁功能列上面每個連結的click事件,
當click事件觸發時,就將連結element裡的value放到一個動態產生的hidden field中,這個hidden field再附加到查詢表單裡,最後再submit查詢表單,
如此一來當表單送出時就同時把分頁的頁數以post傳送到後端去了。
來看看執行的結果:
查詢表單於選擇下拉表單送出後所顯示的畫面
點擊分頁功能列的「2」或是「下一頁」送出後的畫面,Category的下拉選單仍然保持剛才的選取項目,
下方的表格也確實顯示分頁資料,而網址列部分也沒有出現page的參數。
最後一頁
好的,最後完成了以Post方式來操作的分頁功能,並且在傳送分頁後仍然可以保持Post送出前的查詢條件狀態,
有些人應該會覺得這樣的分頁好像還少了些什麼?!
應該說還少了AJAX的操作,因為ASP.NET MVC的一大特色就是前端比ASP.NET WebForm更容易與jQuery結合,
所以下一篇就來介紹如何用MvcSimplePager以及ASP.NET MVC的Html.RenderPartial()來達成AJAX分頁的功能操作。
以上
作者已經移除這則留言。
回覆刪除