📚
knockout.js を使ってページングを実装してみる
はじめに
knockout.js が便利で楽しいため、いろいろと試してみました。今回はページングを実装してみます。
実行手順
Shared/_Layout.cshtml
knockout.js を呼び出すのを忘れないようにしてください。knockout.js は NuGet で入手できます。
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
Home/Index.cshtml
ページングを行うためのビュー モデルを作成します。これはサーバーに渡すデータ (pageIndex、pageSize) やサーバーから受け取るデータ (totalCount、totalPage、items) を保持します。このビュー モデルにページングを行うための movePrevious、moveNext、moveTo などのメソッドを実装します。サーバーとのやり取りは load メソッドで行います。
HTML では knockout.js の作法に従って data-bind 属性を記述します。click イベントも data-bind に記述できるのが便利です。今回は実装していませんが、先頭や最後のページのときのナビゲーションを無効化したい場合は、data-bind="if:" や data-bind="notif:" を利用すると便利です。
@{
ViewBag.Title = "ホーム";
}
<div>
<div>
<a href="javascript:void(0)" data-bind="click: movePrevious">前へ</a>
<a href="javascript:void(0)" data-bind="click: moveNext">次へ</a>
</div>
<hr />
<div data-bind="foreach: items">
<div>名前: <span data-bind="text: Name"></span></div>
<div>誕生日: <span data-bind="text: Birthday"></span></div>
<div>国籍: <span data-bind="text: Nationality"></span></div>
<hr />
</div>
</div>
<script type="text/javascript">
$(function () {
var viewModel = {
// サーバーのURL
url: "@Url.Content("~/Home/Index")",
// 現在のページ番号
pageIndex: ko.observable(1),
// ページに表示する件数
pageSize: ko.observable(3),
// 総件数
totalCount: ko.observable(0),
// 総ページ数
totalPage: ko.observable(0),
// 結果のリスト
items: ko.observableArray([]),
// 前のページに移動するメソッド
movePrevious: function () {
if (this.pageIndex() > 1) {
this.moveTo(this.pageIndex() - 1);
}
},
// 次のページに移動するメソッド
moveNext: function () {
if (this.pageIndex() < this.totalPage()) {
this.moveTo(this.pageIndex() + 1);
}
},
// 指定したページに移動するメソッド
moveTo: function (index) {
this.pageIndex(index);
this.load();
},
// サーバーに問い合わせるメソッド
load: function () {
// パラメーターはサーバーでは FormCollection で受け取る
var param = {
pageIndex: this.pageIndex,
pageSize: this.pageSize
};
// コールバック
var callback = function (data) {
viewModel.totalCount(data.TotalCount);
viewModel.totalPage(data.TotalPage);
viewModel.items.removeAll();
$.each(data.Items, function (i, e) { viewModel.items.push(e) });
};
// POST でリクエストを投げる
$.post(this.url, param, callback, "json");
}
};
ko.applyBindings(viewModel);
viewModel.load();
})
</script>
HomeController.cs
コントローラーでは Skip メソッドと Take メソッドを使って必要なデータを取り出します。
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection collection)
{
var data = new[]
{
new { Name = "藍澤優", Birthday = "4/30", Nationality = "台湾" },
new { Name = "藍澤葵", Birthday = "11/12", Nationality = "台湾" },
new { Name = "藍澤玲", Birthday = "7/10", Nationality = "台湾" },
new { Name = "藍澤光", Birthday = "9/27", Nationality = "台湾" },
new { Name = "窓辺ななみ", Birthday = "4/6", Nationality = "日本" },
new { Name = "クラウディア窓辺", Birthday = "11/20", Nationality = "アメリカ" },
new { Name = "クロード窓辺", Birthday = "4/4", Nationality = "アメリカ" },
new { Name = "ウェブマトリクスマン", Birthday = "不明", Nationality = "日本" }
};
var pageIndex = int.Parse(collection["pageIndex"]);
var pageSize = int.Parse(collection["pageSize"]);
return Json(new
{
TotalCount = data.Count(),
TotalPage = (int)Math.Ceiling((double)data.Count() / pageSize),
Items = data.Skip(pageSize * (pageIndex - 1)).Take(pageSize)
});
}
}
実行結果
実行すると、次のように表示されます。

次へ をクリックするとページが切り替わることを確認できます。

おわりに
今回のサンプルではナビゲーションが簡易的ですが、工夫すればページ番号にリンクも付与できます。よく使うテクニックですので、ビュー モデルをクラス化するとさらに便利になります。
Discussion