📚
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
ページングを行うための ViewModel を作成します。これはサーバーに渡すデータ (pageIndex
、pageSize
) やサーバーから受け取るデータ (totalCount
、totalPage
、items
) を保持します。この ViewModel にページングを行うための 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
Controller では 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)
});
}
}
実行結果
実行してみるとこんな感じで表示されます。
次へ をクリックしてみるとページが切り替わっているのがわかります。
おわりに
今回のサンプルではナビゲーションが貧弱ですが、頑張ればページ番号にリンクをつけることができます。割とよく使うテクニックだと思われるので、ViewModel をクラス化してしまえば、かなり便利になりそうです。
Discussion