technoshop

杉並区和泉のソフトウェアハウス、株式会社テラソフトの技術ブログです。主に.NET MVCとKnockoutJSの情報をまとめます。

.Net MVC4 で KnockoutJS ~ 【基礎編1】Knockoutのキホン、ViewModelとBinding

ザクです。やっと始まりましたKnockoutJS講座。タイトルは「.Net MVC4 で KnockoutJS」です。どうぞご贔屓に。

Knockoutの解説によくあるMVVM(Model View ViewModel)パターンの説明ははしょります。あれ、逆によくわかんないですよね。(並び順もなんとなく違う。。。Cの代わりなのでこれでいいのか)ようするにKnockoutを理解するには要らない概念と思われます。代わりに、Knockoutでもっとも重要な「ビューモデルとバインディング」について説明しますね。

Knockoutのアップデート

ビューモデルとかやる前に、Knockoutのアップデートです。MVC4に入っている2.2では古いので最新のにまで上げちゃいます。(執筆時点での最新はv3.2です)

Knockout : Downloads

ダウンロードできたら、プロジェクトのScriptsフォルダに放り込みます。古い方は削除してもいいでしょう。

Knockoutをアプリにバンドル

KnockoutのファイルをScriptsフォルダに入れるだけでは、もちろんアプリで使えませんね。バンドルしてやる必要があります。覚えてますか?App_Startフォルダの中にあるBundleConfig.csでアプリで利用するjs/cssファイルを決めてやるんでしたね。以下のように修正してください。

bundles.Add(new ScriptBundle("~/bundles/js").Include(
                        "~/Scripts/jquery-{version}.js",
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*",
                        "~/Scripts/knockout-{version}.js",
                        "~/Scripts/bootstrap.js"));

上のようにデフォルトのbundlesに突っ込んでしまえばいいと思います。アプリを起動して、ソースを確認してみてください。3.2.0-debugが取り込まれているのが確認できました。debugになってますが、開発中はこのままのほうが都合がいいので、このままにします。コンパイルするときにBundleが良きにはからってくれます。

コントローラとビューにKnockoutを入れてみる

StudentテーブルをKnockoutで表示するためにMVC4のコントローラとビューを用意しましょう。面倒なのでビュー付きでコントローラをスキャフォールドしてしまいます。追加のダイアログボックスはこんな感じになるはずです。

f:id:technoshop:20141001111539j:plain

これで適当なビューファイルが使えるようになりました。Knockoutの練習用にコントローラを調整します。IndexでStudentのリストをMVC4で表示しているのを、代わりにKnockoutで表示するのを当面のゴールとしましょうか。MVC4でDBからモデルのデータを取ってくるところが不要になるので、この部分を消すかコメントアウトし、ビューの呼び出しは中身を空にします。

public ActionResult Index()
{
    return View();
}

コントローラのIndexはこんな感じになります。次に、ビューの方です。こちらは試しにKnockout本家サイトのチュートリアルにあるFirst NameとLast Nameを表示するやつを試しに入れてみます。

learn.knockoutjs.com

あとで、Studentのリストを表示するのに使うので、元のIndex.cshtmlはバックアップとってもいいですね。(Gitならベター)まず、h2以下をバサッと削除し、下のコードに置き換えます。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>

@section Scripts {
<script type="text/javascript">
    function AppViewModel() {
        this.firstName = "ザク";
        this.lastName = "テラソフト";
    }

    // Activates knockout.js
    ko.applyBindings(new AppViewModel());
</script>
}

これだけです。アプリを起動して、Studentコントローラを見てみます。

f:id:technoshop:20141001114626j:plain

名前のところに、KnockoutのAppViewModelで定義してある変数に入れた値が表示されました。Knockoutの基本的な書き方はこれだけです。簡単ですね。KnockoutがAngularJSやBackboneなど他のものに比べて習得しやすいのは、この単純さにあります。開発が進むにつれビューモデルの構成が複雑になったり、ユーティリティ的なコードを加えられたりすることになりますが、基本的には2つのコードで動きます。上のコードを説明します。

Knockoutでは、まずビューモデルを定義します。このサンプルではAppViewModelとなっています。JavaScriptのファンクションになっていれば名前はなんでもいいです。ただ「ViewModel」とつけてやると、「ああここがKnockoutの始まりか」とわかって親切ですね。ビューモデルはKnockoutで使う構造体の定義です。ビューモデルと言っているので、実行時にビューにバインドします。一番最後の行の「ko.applyBindings(new AppViewModel());」はこのViewModelの中身をビューで使えるようにする実行のコードですね。JavaScript側はこれだけです。

ビューファイルの中では、特定のhtmlタグをビューモデル内の変数にバインドするdata-bindを書くことで、ビュー側で変数をどこでどのように表示するか宣言します。(宣言的バインディングです)これだけで、ビューモデルの中の変数をビュー側に持ってきて表示することができるようになりました。

Observableを使ってみよう

Observableを使ってもっとKnockoutらしさを実感してみましょうか。ObservableはKnockoutの変数で、これが双方向バインディングのキモとなります。

Knockout : Observables

ビューモデルの変数にObsrevableを使ってみます。ビューにも2行追加します。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>

<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>

@section Scripts {
<script type="text/javascript">
    function AppViewModel() {
        this.firstName = ko.observable("ザク");
        this.lastName = ko.observable("テラソフト");
    }

    // Activates knockout.js
    ko.applyBindings(new AppViewModel());
</script>
}

先ほどビューモデルに定義した名前の変数にko.observableを使って代入しました。これだけで変数が双方向バインディングの変数になります。Studentをリロードして、フォームの名前を変えてみてください。(フォームの値を変えてからイチイチクリックちなければなりませんが)値を変えたら、上のテキストもその値に変わったと思います。ここでは、同じ変数を2つの違う形でビューにバインドしています。表示がtextで、入力がvalueです。JQueryで同じことをやろうと思うと、これ結構面倒くさいです。エクセルアプリみたいに入力箇所が増えてくるとidやclassがゴチャゴチャしてきて管理不能になります。Knockoutではdata-bindをビューに書いてやらなければなりませんが、JQueryに頼ることなくビューモデルの中のデータを操作して簡単にビューに表示できるようになりました。

エクセルの関数ちっくなComputed Observable

3種類あるObservableのうちもう一つComputed Observableも見ておきましょう。Computedというだけに、ビューモデルを構成している変数をいろいろ組み合わせて、より複雑な結果を出力する変数が作れます。

Knockout : Computed Observables

Observableみたいに、ビューモデルに変数を作り、それにko.computedを使って代入してやります。ko.computedの中身はJavaScriptの「ファンクション」になります。下がfirstnameとlastnameをコンカチするko.computedを追加したサンプルです。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>

<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>

<p>Full name: <strong data-bind="text: fullName"></strong></p>

@section Scripts {
<script type="text/javascript">
    function AppViewModel() {
        var self = this;

        self.firstName = ko.observable("ザク");
        self.lastName = ko.observable("テラソフト");
        self.fullName = ko.computed(function () {
            return self.firstName() + " " + self.lastName();
        });
    }
    ko.applyBindings(new AppViewModel());
</script>
}

このko.computedでは単純にビューモデルの変数をスペースを間に挟んでくっつけたものをfullnameとして出力しました。ここでは簡単な文字列の操作でしたが、数値を扱えばエクセルちっくなアウトプットを出す特別な変数を作ることができますね!(ザクが「エクセルアプリをウェブに移植するためのライブラリ」と主張する根拠はこれです)

ビューモデルのなかの「var self = this;」って何?と思った人もいると思います。(するどい!)これはビューモデルで構造体を作るとき、ファンクションが入れ子になることがKnockoutではしょっちゅうあるので、親のファンクションにある変数を子のファンクションで取り出したいとき、親側でこの一行を入れておきます。すると子のファンクション側ではself.???として、親の変数が利用できるというわけです。(JavaScriptのスコープの話ですね)Knockoutで構造体を作るときの作法として覚えておきましょう。

今回はここまで。次回はObservableArrayを使ってループ表示をやりますよ~。