technoshop

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

.Net MVC4 で KnockoutJS ~【基礎編2】 WebAPIで取得したデータをビューに表示(初めてのObservableArray)

ザクです。月曜公開にしようかと思いましたが、週末これで遊んでくれる人がいるかもと思って頑張って仕上げました!

「.Net MVC4 で KnockoutJS」の2回目はObservableArrayです。

今回は.Net MVC4側でWeb APIを使います。GETでモデルのデータを取ってきて、Knockoutのビューモデルに流し込む方法を説明します。それをビューに表示できたら一件落着という感じです。

これまで.Net MVC4で土台になるアプリの準備方法をWeb APIを含めて解説してきました。おさらいが必要な方はこちらからどうぞ。(全6回)

.Net MVC4にBootstrapをLessで組み込む - technoshop
Entity Framework Code First でRailsっぽくDBを管理する - technoshop
Entity Framework Code First でのリレーションシップ - technoshop
One-to-One 再入門(カスケードデリートとかもあるよ) - technoshop
SimpleMembershipをCode Firstに組み込む - technoshop
.Net MVC4 でも Web API で REST してみる! - technoshop

最後の回でWeb APIを使ってDBからデータを取り出せるようになりましたね。データをKnockoutのビューモデルに渡してやる準備をします。

Knockoutビューモデルの作成(含むWeb APIの呼び出しとデータローディング)

Web APIから受け取るデータの入れ物として、KnockoutのビューモデルにMVC4のモデルと同じ構造体を用意してやります。Studentモデルをベースにビューモデルを作ります。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

~ 一旦削除 ~

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

        self.StudentId = ko.observable(student.StudentId);
        self.LastName = ko.observable(student.LastName);
        self.FirstMidName = ko.observable(student.FirstMidName);
        self.EnrollmentDate = ko.observable(student.EnrollmentDate);
    }
    function SchoolViewModel() {
        var self = this;

        self.Students = ko.observableArray().ofType(Student);
        GetSchoolData();
        //Function to call Web API
        function GetSchoolData() {
            //Ajax Call Get 
            $.ajax({
                type: "GET",
                url: "/api/StudentAPI/",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (data) {
                    self.Students(data); //Put the response in ObservableArray
                },
                error: function (error) {
                    alert(error.status + "<--and--> " + error.statusText);
                }
            });
            //Ends Here
        }
    }
    ko.applyBindings(new SchoolViewModel());
})();
</script>
}

Index.cshtmlを上のように書き換えました。いきなり実践的なコードになりましたw

Studentモデル以外にも今後色々入れていくことになるので、親となるビューモデルは「SchoolViewModel」としました。MVC4のStudentモデルを参考に同じ名前のStudentモデルと変数を用意します。扱うモデルが増えてくるとビューモデルの中がゴチャゴチャしてくるので、中で利用するモデルはMVC4のモデルに合わせて分けて定義します。(注:Knockoutのビューモデルでの変数定義の大文字小文字はC#側に合わせてください。JavaScriptがケースセンシティブなのでJsonデータのやり取りで悩まなくてすみます)Studentモデルの変数は全てKnockoutのObservableとして代入しました。MVC4側のStudentモデルにはEnrollmentsコレクションのナビゲーションプロパティがありますが、わかりやすくするためにここでは一旦省きます。

ビューモデルの中は主に(var self = this;は無視して)3つのコードで構成されています。

最初にko.observableArrayが出てきました。これは配列のデータを格納するのに使います。ofType(Student)は、Knockout用の外部ライブラリで、ビューモデルをここで示したように作るだけで、ビューモデルにJsonデータを簡単に流し込めるようにするものです。下にknockout-oftypeのGithubと作者による解説(英語)へのリンクを貼っておきます。

lelandrichardson/knockout-oftype · GitHub
Working with Typed Arrays in Knockout.js - Tech.pro

jsファイルをダウンロードしたら、Scriptsフォルダに加えてください。BundleConfig.csも修正します。

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

次にGetSchoolDataです。これは真下にある同名のファンクション呼び出しです。ファンクションの中身はJQueryajaxコールになっています。非同期でStudentAPIをGETで呼びます。呼び出しが成功するとJson形式でStudentsデータを取得し、self.Studentsにデータを流し込んでオブジェクトを作ります。これが完了したら、ビューでStudentのデータが取り出せるようになっているはずです。

Chrome拡張機能を使ったKnockoutのデバッグ

Student/Indexの結果をブラウザで確認します。アウトプットのところはまだ何も書いていないので勿論なにも表示されません。でも、Knockoutはこの状態でもレンダリング用のデータを用意してくれています。

JavaScripのデバッグにはChromeが便利なので、KnockoutもChromeを使ってデバッグします。Knockoutの開発の際に便利な機能拡張を入れましょうか。Chromeのウェブストアで「Knockout」を検索します。でましたね。「Knockoutjs context debugger」というやつがそうです。インストールしましょう。

インストールできましたか?これの使い方は簡単で、Knockoutのビューモデルが仕込まれているhtmlのbodyのどこかを右クリックするだけで、その要素にバインドされているビューモデルの中身が表示されます。バインドしていなくても、bodyから見えるようになっているはずです。

f:id:technoshop:20141002135251p:plain

Studentデータがオブジェクトの配列として取れました!

ObservableArrayをforeachバインディングで表示する

表示するオブジェクトの準備は整いました。早速、ビューにバインドしてみましょう。

ObservableArrayに格納されているデータを一覧表示するには「foreach」バインディングを使います。削除した元のテーブルを戻しつつKnockout用に直してみます。

<h2>Index</h2>

<table>
    <tr>
        <th>
            Last Name
        </th>
        <th>
            First Name
        </th>
        <th>
            Enrollment Date
        </th>
        <th></th>
    </tr>
    <!-- ko foreach: Students -->
    <tr>
        <td><span data-bind="text: LastName"></span></td>
        <td><span data-bind="text: FirstMidName"></span></td>
        <td><span data-bind="text: EnrollmentDate"></span></td>
        <td>
            <a data-bind="attr: { href: '/Student/Edit/' + StudentId() }">Edit</a> |
            <a data-bind="attr: { href: '/Studnet/Details/' + StudentId() }">Details</a> |
            <a data-bind="attr: { href: '/Student/Delete/' + StudentId() }">Delete</a>
        </td>
    </tr>
    <!-- /ko -->
</table>

ビューのHTML側はこんな感じになります。Knockoutを使うとRazorはあまり使わなくなります。もちろん共存させることも可能ですが。

<!-- ko foreach: Students --><!-- /ko -->で囲った部分がforeachバインディングです。ビューモデルのStudentsオブジェクト(ko.observableArrayで中身は個々のStudentオブジェクトの配列を持つ)を指定して、中身を繰り返し表示させます。このforeachの囲いの中では、Studnetオブジェクトの変数名を直接指定して、特定のHTML要素にバインドできるようになります。文字だけ表示させる場合はtextバインディングでtd要素内に各変数の中身を出力し、リンクのhrefの中に表示する場合は、attrバインディングでhrefをJavaScriptでStudentIdをベースになるURLの文字列とコンカチします。

attrバインディングのStudentIdのおしりに「()」が加えられている点に注意してください。Knockoutの変数に格納された値をプログラム的に取り出す時(バインディングに直接指定する時は要らない)は必ず括弧()が必要です。(この括弧はKnockout開発における悩みどころの一つです)KnockoutのObservableはファンクションなので、値を取り出す(returnをもらう)には括弧が必要というわけです。JavaScriptの中でObservableを扱う際にはしょっちゅう出てくるので注意しておいてください。

http://kojs.sukobuto.com/docs/foreach-binding
http://kojs.sukobuto.com/docs/attr-binding

リロードして表示させます。

f:id:technoshop:20141002152846j:plain

今回はここまで。次回は表計算っぽいのやりますよ~。