# MVC 搭配 Ajax 實作搜尋分頁功能 ( X.PagedList )
###### tags: `實作功能`
## 功能展示

---
## 使用情況
資料量大,卻不想要一次資料全部撈完造成效能問題,**PagedList 會分頁撈取資料,而不是全部撈完才分頁。**
---
## 安裝
以前是 ~~[PagedList](https://github.com/troygoode/PagedList)~~,已經停止更新。
目前使用 [X.PagedList](https://github.com/dncuug/X.PagedList)
### Bootstrap3
直接裝 X.PagedList.Mvc 會自己幫你裝好其他的
### Bootstrap4

1. 裝 X.PagedList.Mvc ( 會自己幫你裝好 X.PagedList )
2. 裝 [X.PagedList.Mvc.Bootstrap4](https://github.com/lettucebo/X.PagedList.Mvc.Bootstrap4)
※這時的 X.PagedList.Mvc 最新版為 v7.9.0。會裝舊版的 X.PagedList.Mvc 是因為使用 X.PagedList.Mvc.Bootstrap4 的搭配使用會有問題 ( 對應不到 PagedList 的 PagedListRenderOptionsBase ),不知道會不會更新修掉。
---
## Model
```
public class SearchFilterViewModel
{
public string Name { get; set; }
public string Email { get; set; }
public string NationCode { get; set; }
public string CellPhone { get; set; }
}
```
---
## View
### Index
```
@{
ViewBag.Title = "會員列表";
}
<!DOCTYPE html>
<head>
@section styles{
}
</head>
<body>
@section scripts{
<script>
function Search() {
// 若 hash 沒資料,預設取第 1 頁
// hash = #,瀏覽器解讀為位置識別符號
// # 是用來指導瀏覽器動作的,對伺服器端完全無用。所以,HTTP請求中不包括 #
var page = window.location.hash
? window.location.hash.slice(1) : 1;
// 取資料
fetchPage(page);
}
var fetchPage = function (page) {
// 準備搜尋條件資料
var filter = {
Name: $("#Name").val(),
Email: $("#Email").val(),
National: $("#National").val(),
CellPhone: $("#CellPhone").val(),
page: page,
}
// Ajax 呼叫
$.ajax({
url: '@Url.Action("PagedPartial", "Member")',
data: filter,
type: 'Post',
success: function (resultHtml) {
// 取得資料後將目前 hash 重設
window.location.hash = page;
// 將 PartialView 資料寫入 div id="MemberDatas" 的區塊
$('#MemberDatas').html(resultHtml);
// 重設頁碼按鈕
$('#MemberDatas .pagination li a').each(function (i, item) {
// 若是有超連結的頁碼
var hyperLinkUrl = $(item).attr('href');
if (typeof hyperLinkUrl !== 'undefined' && hyperLinkUrl !== false) {
// 取得當前按鈕(<a>)的對應頁碼
var pageNumber = $(item).attr('href');
// 將頁碼按鈕的 href 去除
$(item).attr('href', '#');
// 設定按下頁碼事件
$(item).click(function (event) {
// 停止事件的默認動作,例如有時候我們會利用連結的 <a> 來當作按鈕,
// 他本身 DOM 就擁有連結的功能,但是有時候我們會為他新增 onclick 的事件,
// 而只要在該 <a> 觸發的事件中加入event.preventDefault(),
// 就不會在執行他默認的動作,也就是不會再執行「連結到某個網址」這個動作。
event.preventDefault();
// 取得按下的頁碼的資料
fetchPage(pageNumber);
});
}
});
}
});
};
</script>
}
<div class="border border-dark bg-dark text-white rounded p-3">
<div class="row">
<div class="col-sm-4 col-md-3 col-lg-3">
<label class="col-form-label">姓名</label>
<div>
@Html.TextBox("Name", null, new { @class = "form-control", onkeyup = "Search()" })
</div>
</div>
<div class="col-sm-8 col-md-4 col-lg-4">
<label class="col-form-label">Email</label>
<div>
@Html.TextBox("Email", null, new { @class = "form-control" })
</div>
</div>
<div class="col-sm-3 col-md-3 col-lg-2">
<label class="col-form-label">國碼</label>
<select name="National" placeholder="國籍" class="form-control">
<option></option>
<option value="886">台灣 +886</option>
</select>
</div>
<div class="form-group col-sm-6 col-md-3 col-lg-3">
<label class="col-form-label">電話</label>
<div>
@Html.TextBox("CellPhone", null, new { @class = "form-control" })
</div>
</div>
<div class="col-sm-4 col-md-2 col-lg-2 d-flex align-items-center">
<button class="btn btn-success" onclick="Search()"><i class="fas fa-search fa-lg"></i>搜尋</button>
</div>
</div>
</div>
<hr />
<div id="MemberDatas">
</div>
</body>
```
### PartialView (SearchResult.cshtml)
```
@model IPagedList<MemberModel>
@using X.PagedList;
@using X.PagedList.Mvc;
@using X.PagedList.Mvc.Bootstrap4;
@Html.PagedListPager((IPagedList)Model, page => page.ToString(), Bootstrap4PagedListRenderOptions.Classic)
<table class="table table-bordered table-hover table-sm">
<thead>
<tr>
<th nowrap="nowrap" style="width:20%">姓名</th>
<th nowrap="nowrap" style="width:30%">Email</th>
<th nowrap="nowrap" style="width:10%">電話(國碼)</th>
<th nowrap="nowrap" style="width:20%">電話(號碼)</th>
<th nowrap="nowrap" style="width:20%"></th>
</tr>
</thead>
<tbody>
@if (!Model.HasItems())
{
<tr>
<td colspan="5" class="text-center">
查無會員資料
</td>
</tr>
}
else
{
foreach (var data in Model)
{
<tr>
<td nowrap="nowrap">@data.Name</td>
<td nowrap="nowrap">@data.Email</td>
<td nowrap="nowrap">+@data.NationCode</td>
<td nowrap="nowrap">@data.CellPhone</td>
<td nowrap="nowrap">
<a href="@(Url.Action("Profile", "Member") + "?memberId=" + data.Id)" class="btn btn-info"><i class="fa fa-eye"></i> 檢視</a>
</td>
</tr>
}
}
</tbody>
</table>
@Html.PagedListPager((IPagedList)Model, page => page.ToString(), Bootstrap4PagedListRenderOptions.ClassicPlusFirstAndLast)
```
---
## Controller
### Index
```
public ActionResult Index()
{
return View();
}
```
### PagedPartial ( 回傳 SearchResult.cshtml )
```
[HttpPost]
public ActionResult PagedPartial(SearchFilterViewModel searchFilter, int? page)
{
#region 找出符合篩選條件的資料
var memberFilter = new Repositories.Filters.MemberFilter();
if (!string.IsNullOrEmpty(searchFilter.Name))
memberFilter.Name = searchFilter.Name;
if (!string.IsNullOrEmpty(searchFilter.Email))
memberFilter.Email = searchFilter.Email;
if (!string.IsNullOrEmpty(searchFilter.NationCode))
memberFilter.NationCode = searchFilter.NationCode;
if (!string.IsNullOrEmpty(searchFilter.CellPhone))
memberFilter.CellPhone = searchFilter.CellPhone;
var members = _memberService.Search(memberFilter);
if (members == null)
members = new List<MemberModel>();
#endregion
#region 將資料轉為 PagedList 的資料
var pageNumber = page ?? 1;// 若無傳入 Page,預設查詢第1頁
var onePageOfMembers = members.ToPagedList(pageNumber, 1); // 參數說明: ToPagedList( 第幾頁 , 一頁要顯示多少資料 )
#endregion
return PartialView("SearchResult", onePageOfMembers);
}
```