Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
特集:最新技術を活用したWebアプリとは?

特集:最新技術を活用したWebアプリとは?

ASP.NET SignalRとknockout.jsで実現する次世代Webアプリ

2013年5月7日

SignalRとknockout.jsは相性がよく、これらの最新技術を組み合わせることで、リアルタイムに更新されるWebアプリを簡単に構築できる。その方法を紹介する。

芝村 達郎
  • このエントリーをはてなブックマークに追加

SignalRとknockout.js

 ASP.NET SignalR(以降、SignalR)を使うと、リアルタイムに変化するデータを、サーバーから簡単に受け取れるようになる。しかし実際のところ、そのデータをビューに反映させることが非常に手間である。理由としては、jQueryなどのライブラリを使ったとしても、「ビュー上の要素をidやclass名などの識別子を用いて取得し、値を更新」という方法しかないためだ(次のコードはその例である)。

JavaScript
// 表示テキストの書き換え(idが「message」の要素のテキストを書き換え、class名が「alert」の要素のHTMLコードを書き換えている)
$("#message").text("メッセージ");
$(".alert").html("<b>メッセージ</b>");

// フォーム値の取得
var text = $("#form").val();
表示されている値を、jQueryを使って操作するJavaScriptコード

 これではWebページ上の機能が増えるに従って、「実際に通信を行う処理」よりも「ビューにデータを反映する処理」のコードの方が多くなってしまうということにもなりかねない。あるいは、要素の識別にidやclass名といった文字列を使うため、記述のミスやビューの修正による影響で(ビューへの反映処理が)動作しなくなることも考えられる。

 このような問題を回避し、さらにはビューとロジックの分離を行うためのJavaScriptライブラリが「knockout.js」だ。SignalRとknockout.jsを組み合わせることで、サーバーからのデータ更新と同時にビューの更新といった、これまでは手間がかかっていた処理も簡単に実現可能となる。

 それではSignalRとknockout.jsを使った簡単なアプリのコードを見ながら、その相性の良さを見ていこう(両者の組み合わせで実現できることを説明するのが本稿の目的なので、SignalRやknockout.jsの使い方の説明については割愛する)。コードはC#で記述する。

SignalRとknockout.jsの簡単なサンプル

 今回は、テキストボックスとボタンだけという、非常にシンプルなサンプル・アプリを用意した。テキストボックスに1行のメッセージを入力して[送信]ボタンを押すと、これまでの入力履歴をリストとして表示するだけのアプリだが、SignalRとknockout.jsを使ううえでの基本的な機能を含んでいる。

今回作成した、1行メッセージを投稿するアプリの実行例
今回作成した、1行メッセージを投稿するアプリの実行例

サーバー側のSignalR Hubクラスのコード

 サーバー側のコードは非常に簡単なものなので、説明は割愛する(以下に、コードのみ掲載する)。今回のサンプルで肝心なのは、後述するクライアント側のJavaScriptコードの方だ。

C#
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

[HubName("message")]
public class MessageHub : Hub
{
  public void Send(string text)
  {
    Clients.All.Receive(text);
  }
}
サーバー側のSignarR Hubクラスのコード(MessageHub.cs)

ASP.NET and Web Tools 2012.2」をインストール済みのVisual Studio 2012のIDEで「ASP.NET 空の Web アプリケーション」テンプレートでプロジェクトを作成し、「SignalR Hub クラス」項目テンプレートを使ってプロジェクト内にMessageHub.csファイルを作成し、そのファイルにこのようなSignarR Hubクラスを実装すればよい(あとは、作成したHubを公開するために、Global.asaxファイルのApplication_Startメソッド内でRouteTable.Routes.MapHubsメソッドの呼び出しを追記する必要がある)。SignalRの基本的なプログラミング方法は、「特集:ASP.NET SignalR入門(前編):ASP.NET SignalRを知る- @IT」を参考にしてほしい。

 それではクライアント側の説明に移ろう。

クライアント側のデータ取得とビューモデル作成のコード

 knockout.jsは、強力なデータ・バインディング&テンプレート処理機能を備えたライブラリだ。MVVM(Model-View-ViewModel)パターンにおけるビューモデル(VM)を作成してHTMLタグにdata-bind属性を付けることで、DOM操作をほとんど行うことなく、ビューに対する処理を行うことが可能になる。

 まずは「(knockout.js用の)ビューモデルの定義」と「(SignalR Hubからデータを送受信するための)SignalRのプロキシ・クラスの作成」から見ていこう。knockout.jsにおけるビューモデルとは、「HTMLコード側(=後述のデータ・バインディングやテンプレートの部分)に対して公開するオブジェクト」と考えておけば問題ない。

 下記のコードの注意点としては、SignalRがサーバーとの接続を完了するまでに多少ラグが発生する場合があるので、その場合に備えてビューモデルにstartedプロパティを用意して、SignalRサーバーとの接続が完了したかを判定可能にしている。

JavaScript
……省略……
<script src="Scripts/jquery-1.9.1.js"></script>
<script src="Scripts/jquery.signalR-1.0.1.js"></script>
<script src="Scripts/knockout-2.2.1.js"></script>
<script>
$(function() {
  var connection = $.hubConnection();
  var message = connection.createHubProxy("message");

  var viewModel = {
    started: ko.observable(false),
    form: ko.observable(),
    items: ko.observableArray(),
    send: function () {
      message.invoke("send", viewModel.form());

      viewModel.form("");
    }
  };

  ko.applyBindings(viewModel);

  message.on("receive", function (text) {
    viewModel.items.push(text);
  });

  connection.start(function () {
    viewModel.started(true);
  });
});
</script>
……省略……
ビューモデルの定義とSignalRの初期化を行うJavaScriptコード(index.html)

knockout.jsを使うには、NuGetからプロジェクト内にインストールしたり、Knockoutのサイトからファイルをダウンロードしたり、CDNを直接参照したりすればよい。いずれの場合も、HTMLコードの<head>タグ内などに「<script type="text/javascript" src="knockout-2.2.1.js"></script>」のようにしてknockout.jsファイルの参照を記述する必要がある(このほか、jQueryライブラリやSignalRのJavaScriptクライアント・ライブラリも同様に参照する必要がある)。その下に<script>タグを記述して、そのタグ内に、このようなコードを記述する。

クライアント側のデータ・バインディングのコード

 今回のアプリでは、knockout.jsのデータ・バインディング&テンプレート機能を使って、サーバーからプッシュされた文字列を<ul>タグ内に表示する。具体的には次のコードのように、<ul>タグのdata-bind属性でforeachバインディングを指定すると、その子要素がテンプレートとして扱われる。新しい文字列がコレクション(=ビューモデルのitemsプロパティ)に追加(=push)されると自動的に<li>タグが生成され、さらにdata-bind属性でtextバインディングを指定しているので、<li>タグのテキスト要素として追加した文字列が表示される。

HTML
……省略……
<body>
  <ul data-bind="foreach: items">
    <li data-bind="text: $data"></li>
  </ul>

  <input type="text" data-bind="value: form" />
  <input type="button" data-bind="click: send, enable: started" value="送信" />
</body>
……省略……
データ・バインディングの設定を行ったHTMLコード(index.html)

 つまりknockout.jsのdata-bind属性を使ってデータ・バインディング定義が完了すれば、あとは全てJavaScriptコードだけの話となり、「SignalRからビューモデルに定義されたプロパティの操作」や「ビューモデルに定義された関数からSignalRのメソッド呼び出し」なども容易に行えるようになるのだ。

 さて、まとめとなるが、SignalRとknockout.jsの相性がよい最大の理由として、knockout.jsがテンプレート機能を持っていることが挙げられるだろう。SignalRを使ったアプリを開発すると、動的に要素を作成してビューへ反映させる処理が多くなりがちだが、複雑なDOMを開発者がコードにより構築するのは非常に手間がかかる。しかし、knockout.jsを使うことでビューモデルのコレクションに対して要素を追加するだけで、どんなに複雑な要素でも、用意したテンプレートから構築されてビューへ自動的に反映される。

 これからのWebアプリでは、JavaScriptを使ったサーバー間通信やビューの操作は増えていき、今以上に複雑さが増していくだろう。そういった将来に備えて今から、ネットワーク部分にはSignalR、ビューの操作にはknockout.jsを使っていこう。そうすることで、これまで以上にリッチなユーザー・インターフェイス(UI)を持つWebアプリを実現しやすくなるだけでなく、ビューとロジックの分離によって見通しのよいコードを書けるようになるはずだ。

サイトからのお知らせ

Twitterでつぶやこう!