ソースコードから理解する技術-UnderSourceCode

手を動かす(プログラムを組む)ことで技術を理解するブログ

knockout.jsをASP.NET MVC 4 で簡単に使ってみた

タイトルどおり、knockout.jsを使い、ASP.NET MVC にて
MVVMパターンを簡単に実装してみました。
一応、ModelをデータベースにCRUDする機能は有するサンプルです。

どのあたりが簡単なのかと言うと
・データベースへのCRUDはEF CodeFirstにて自動生成する
・Index、Edit、Deleteの各画面とControllerはデータスキャフォールドにて自動生成する
MVVMパターンは一覧に表示する画面にのみ適用する
辺りです。

データの編集、削除、詳細表示(Edit,Delete,Details)にMVVMパターンを適用しなかった理由は
対象のデータが別ユーザーにより変更されている可能性がある場合
MVVMパターンによるクライアント側でのデータの変更が相応しくないケースもあると考えたからです。
(クライアント側で編集したデータが、別ユーザーによって削除されているケースなど。)

今回はMVVMパターン
・データを保持するPOCOのModel
・一覧を表示するView
・一覧に表示するデータの表示形式を保持するViewModel
に適用してみました。

以下、実装方法です。

1.事前準備
ASP.NET MVC4の「Internet application」のテンプレートよりプロジェクトを作成します。
次にScriptsフォルダに、knockout-2.0.0.jsを配置してください。
(knockout-2.0.0.jsは、knockout.より取得できます。)


2.Modelの定義
今回は従業員の情報を保持するEmployeeクラスを作成しました。

Employee.cs


namespace MVVMSample.Models
{
public class Employee
{
public int EmployeeID {get; set;}
public string LastName {get; set;}
public string FirstName {get; set;}
public string City {get; set;}
}
}

注意点としては、テーブルのキーとなるプロパティ(ここではEmployeeID)を定義しないと
次のEF CodeFirstの実行時にエラーとなります。


3.EF CodeFirst、データスキャフォールドの使用
EF CodeFirstを使用し、データベースと接続周りを定義します。
次にデータスキャフォールドにてCreate、Index、Edit、Deleteの各画面とControllerを自動生成します。
これらの方法はASP.NET MVC 3 Tools UpdateによるEF コードファーストとデータスキャフォールディング
参考にしてください。


4.knockout.jsを参照
共通レイアウトファイルである_Layout.cshtmlに、knockout.jsの参照を追加します。

_Layout.cshtml


<script src="@Url.Content("~/Scripts/knockout-2.0.0.js")" type="text/javascript"></script>


5.Viewの定義
データスキャフォールドにて生成されてIndex.cshtmのソースを変更することで
MVVMパターンを実装していきます。
まずはViewのソースです。

Index.cshtml


@model IEnumerable<MVVMSample.Models.Employee>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
@Html.ActionLink("Create New", "Create")
</p>

<table class="ViewList">
<tr>
<th>
Name
</th>
<th>
@Html.DisplayNameFor(model => model.City)
</th>
<th></th>
</tr>
<tbody data-bind="foreach: employees">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: City"></td>
<td>
<a data-bind="attr: { href: EditUrl}">Edit</a>
<a data-bind="attr: { href: DetailsUrl}">Details</a>
<a data-bind="attr: { href: DeleteUrl}">Delete</a>
</td>
</tr>
</tbody>
</table>


<table>タグの中が、一覧を表示するためのViewです。
ModelではFirstName、LastNameに分けて保持していたデータを、Viewでは一つに纏めてNameとして表示します。

<tbody data-bind="foreach: employees"> で、employeesオブジェクトをバインドで取得し
ループすることで一覧を生成してます。

ループの中ではViewModelのプロパティにバインドすることで値を表示しています。
バインドするプロパティは、上から順にName、City、EditUrl、DatailsUrl、DeleteUrlです。
Name、Cityはdata-bind="text:を使ってテキストとして表示し
EditUrl、DalailsUrl、DeleteUrlはdata-bind="attr:を使ってリンクとして表示しています。


6.ViewModelの定義
次にViewModelです。Index.cshtmlのViewの下に記述します。


<script type="text/javascript">
function Employee(data){
this.EmployeeID = ko.observable(data.EmployeeID);
this.LastName = ko.observable(data.LastName);
this.FirstName = ko.observable(data.FirstName);
this.City = ko.observable(data.City);
this.Name = ko.computed(function() {
return this.FirstName() + " " + this.LastName();
}, this);
this.EditUrl = ko.computed(function() {
return "../employees/Edit/" + this.EmployeeID();
}, this);
this.DetailsUrl = ko.computed(function() {
return "../employees/Details/" + this.EmployeeID();
}, this);
this.DeleteUrl = ko.computed(function() {
return "../employees/Delete/" + this.EmployeeID();
}, this);
}

$(function () {
$.getJSON("@Url.Action("IndexJson")", function (data) {
var mappedData = $.map(data, function(item) { return new Employee(item) });

var viewModel = {
employees: ko.observableArray(mappedData)
};

ko.applyBindings(viewModel);
});
});
</script>


ViewModelは大きく2つに分かれ
・データ一件毎の表示形式を定義するEmployeeクラス(function Employee で始まる)
・JSONを使いサーバーよりデータを取得してViewにバインドする箇所($(function () で始まる)
で構成されています。

Employeeクラスで注目すべきなのは、ViewにてバインドしていたNameや各Urlの具体的な値を
実装していることです。
FirstNameとLastNameを組み合わせたName、各画面のパスにEmployeeIDを組み合わせたURL(Edit, Details, Delete)は、データベースに保存する項目ではなくViewに関わる項目であるため、ViewModelにて定義しています。

このEmployeeクラスを、JSONを使ってサーバーより取得したデータにマッピングしてViewに表示しています。


7.Controller
最後にViewModelより呼び出されるControllerです。
EF CodeFirstにて自動生成されるControllerに、IndexJsonアクションを追加しています。

EmployeesController.cs


private StoreContext db = new StoreContext();

public JsonResult IndexJson()
{
return Json(db.Employees.ToList(), JsonRequestBehavior.AllowGet);
}


db.Employees.ToList()で取得したデータを、JSON形式で返しています。


実行すると、こんな画面が表示されます。
f:id:UnderSourceCode:20130504100821j:plain
ModelではFirstName、LastNameに分かれていた名前が、ViewModelにて編集されてName列に表示されています。

また、下の赤枠は、リンクにフォーカスを当てたときのURLです。これもViewModelにて編集されてEmployeeIDが
追加されているのが分かるかと思います。

リンクをクリックすると、データスキャフォールドにてい生成されたCreate、Edit、Details、Deleteの
各画面に遷移可能です。

以上、Indexの一覧にのみMVVMパターンを適用する、knockout.jsの簡単なサンプルでした。

Githubにソースを公開しました。
https://github.com/UnderSourceCode/MVVMSample