ASP.NET Core MVCでページング機能を実装する 

ページング機能の実装方法を探していたら、一からコードを書いて実装する方法と、パッケージを利用して実装する方法があったので紹介します。

## 環境
- ASP.NET Core2.2
- ASP.NET Core MVC
- Bootstrap4
- Visual Studio 2019
- X.PagedList.Mvc.Core7.9.0(NuGetパッケージ)

店舗管理機能で、登録店舗の一覧ページにページング機能を実装することを想定しています。

## 1からコードを書いて実装する
### 参考サイト
[Students/Index にページングを追加する(Microsoft)](https://docs.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/sort-filter-page?view=aspnetcore-2.2#add-paging-to-students-index)


### 1.ページング用のクラスを新規作成
```ruby:PaginatedList.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity
{
    public class PaginatedList<T> : List<T>
    {
        public int PageIndex { get; private set; }
        public int TotalPages { get; private set; }

        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            this.AddRange(items);
        }

        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageIndex < TotalPages);
            }
        }

        public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}
```

### 2.Indexアクションメソッドを変更
```ruby:Controllers/ShopController.cs
        public async Task<IActionResult> Index(int? pageNumber)
        {
            // 「編集」「詳細」ページから戻ってくる際のﾍﾟｰｼﾞ番号保持
            ViewData["pageNumber"] = pageNumber ?? 1;

            // ソートデータ取得用のクエリ発行
            var shops = _context.Shop.Select(m => m);

            // 1ページ内の項目レコード表示数
            int pageSize = 5;

            return View(await PaginatedList<Shop>.CreateAsync(shops.AsNoTracking(), pageNumber ?? 1, pageSize));
        }
```

### 3.ページングリンクの追加
先頭のmodelステートメントでPaginatedList< T >を使用する

```ruby:View/Shop/Index.cshtml
	@model PaginatedList<Models.Shop>

	----------------------
		一覧表示処理(tableとか)
	----------------------



//ページャーを表示したい箇所に下記を挿入
	@{
		var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
		var nextDisabled = !Model.HasNextPage ? "disabled" : "";
		var active = "";
	}　

	<nav aria-label="">
		<ul class="pagination">
			<li class="page-item @prevDisabled">
				<a asp-action="Index"  asp-route-pageNumber="@(Model.PageNumber - 1)" class="page-link">Previous</a>
			</li>
			@for (int i = 1; i < Model.TotalPages + 1; i++)
			{
				active = Model.PageNumber == i ? "active" : "";
				<li class="page-item @active">
					<a asp-action="Index"  asp-route-pageNumber="@i" class="page-link ">@i</a>
				</li>
			}
			<li class="page-item @nextDisabled">
				<a asp-action="Index"  asp-route-pageNumber="@(Model.PageNumber + 1)" class="page-link">Next</a>
			</li>
		</ul>
	</nav>
        
```

### 表示内容
![pager1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337557/2bb84a71-a712-4c3d-dfb6-f60bcfbf03d5.png)



## X.PagedList.Mvc.Coreパッケージを利用する
### 参考サイト
[X.PagedList](https://github.com/dncuug/X.PagedList)

### 1.パッケージの追加
追加方法は割愛。画像は下記のやつ。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337557/d377d804-0918-291c-a3bf-dcfff0b8cc9a.png)
バージョンは7.9.0

### 2.Indexアクションメソッドを変更
```ruby:Controllers/ShopController.cs
        public async Task<IActionResult> Index(int? page)
        {
            var pageNumber = page ?? 1;
            IQueryable<Shop> query = _context.Shop;
            return View(query.ToPagedList(pageNumber, 3));
        }
```

### 3.ページングリンクの追加
先頭のmodelステートメントでPaginatedList< T >を使用する

```ruby:View/Shop/Index.cshtml
@using X.PagedList.Mvc.Core;
@using X.PagedList;

	----------------------
		一覧表示処理(tableとか)
	----------------------

//ページャーを表示したい箇所に下記を挿入
@Html.PagedListPager(
    (IPagedList)Model,
    page => Url.Action("Index", new { page }),
    new X.PagedList.Mvc.Common.PagedListRenderOptionsBase    //Bootstrap4を利用する場合はオプションを利用してタグにクラスを追加しないとデザインが反映されない
    {
        LiElementClasses = new string[] { "page-item" },
        PageClasses = new string[] { "page-link" },
        MaximumPageNumbersToDisplay = 5,                                                 //表示するページャーの数
        DisplayLinkToNextPage = X.PagedList.Mvc.Common.PagedListDisplayMode.Never,       //「次へ」の非表示設定
        DisplayLinkToPreviousPage = X.PagedList.Mvc.Common.PagedListDisplayMode.Never,   //「前へ」の非表示設定
        DisplayLinkToFirstPage = X.PagedList.Mvc.Common.PagedListDisplayMode.Never,      //「最初へ」の非表示設定
        DisplayLinkToLastPage = X.PagedList.Mvc.Common.PagedListDisplayMode.Never,       //「最後へ」の表示設定
    }
)
        
```

### 表示内容
![pager2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337557/779d8fe5-3035-c6f2-9147-3708fc21b059.png)

### オプションに関して
参考サイトのGitHubの「README.md」に記載してある。


## 使ってみて
大体「X.PagedList.Mvc.Core」使っておけばOKな気がする。
表示方法のオプションとか、独自でタグにクラスもつけれるので、困ることはあんまりないかなと。（Bootstrap3とBootstrap4でタグのクラスのつけ方が違うので、そこだけ注意。）
ただ、Microsoftのドキュメント読むのも面白いし勉強になるので、MVC勉強中の人はドキュメント読むのもお勧めします。



## 参考サイト
[X.PagedList](https://github.com/dncuug/X.PagedList)
[Students/Index にページングを追加する(Miscrosoft)](https://docs.microsoft.com/ja-jp/aspnet/core/data/ef-mvc/sort-filter-page?view=aspnetcore-2.2#add-paging-to-index-method)
[ページ送り（Pagination）](https://cccabinet.jpn.org/bootstrap4/components/pagination)