上一篇資料分頁的文章「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: else31: {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: else62: {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: else74: {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: else123: {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: else143: {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: else155: {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: else28: {29: int check = 0;30: if (!int.TryParse(formCollection["CategoryDDL"], out check))31: {32: return View();33: }34: else35: {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表單名稱為 formPagerfunction 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分頁的功能操作。
以上
作者已經移除這則留言。
回覆刪除