Observerクラスとサーバー連携

では次に、Observerという機能を使用することによって、画面の更新処理をもっと簡単に記述する例を見てみよう。

せっかくなので、データをサーバー側から取得するようにしてみよう(本サンプルのソースコードはこちらからダウンロードできる)。作成する手順としては、先の例と同じように、プロジェクトを作成した後、ScriptManagerで「MicrosoftAjaxTemplates.js」を追加する。

次に、サーバーからデータを返すWebサービスを作成しよう。とりあえず「NameとAgeを持つPersonクラスのインスタンスを配列で返す」という動きにする。プロジェクトに新しく「Webサービス」を追加する(既定では「WebService1.asmx」というファイル名になる)。

まずサーバーとクライアント間でやりとりするためのデータ構造として、string型のNameプロパティとint型のAgeプロパティを持つEmployeeクラスを定義する。コードは次のようになる。

public class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Employeeクラスが定義できたら、WebService1クラスにJavaScriptと連携してデータを取得するメソッドGetEmployeesを定義する。

まず、ファイルの先頭に「using System.Web.Script.Services;」を追加し、WebService1クラスに「ScriptService」属性を追加する。処理の内容はとりあえず「渡されたパラメータに合わせて検索を行う」といった処理は行わず、常に同じデータを返すという単純な動きにしておこう。

JavaScriptからも呼び出せるように、GetEmployeesメソッドにもScriptMethod属性も追加しておく。そうすると、メソッドのコードは次のようになる。

[WebMethod]
[ScriptMethod(UseHttpGet=true)]
public List<Employee> GetEmployees()
{
    return new List<Employee> {
        new Employee { Name = "鈴木", Age = 30 },
        new Employee { Name = "田中", Age = 35 }
    };
}

それでは、Default.aspxに戻ってHTMLのテンプレートを記述しよう。今回はObserverクラスと連携するため、先ほどの例に比べると少し複雑になる。まずbody要素に、いくつかのXML名前空間の別名を追加しておく。

<body xmlns:sys="javascript:Sys" xmlns:dataview="javascript:Sys.UI.DataView" sys:activate="*">

テンプレート自体のコードは次のようになる。

<div id="imagesList" class="sys-template" sys:attach="dataview" dataview:data="{{ imagesArray }}" style="border:1px solid #ddd;">
    <p>
        名前:{{ Name }}<br />
        年齢:{{ Age }}
    </p>
</div>

先ほどの例に比べると、いくつかの属性が追加されている。

まず「sys-template」というクラスが追加されており「 sys:attach="dataview"」という属性が追加されている(ここで使用されている「sys」という接頭辞は、さきほどbody要素の属性に記述したものを使用している)。

そして、最後に「dataview:data="{{ imagesArray }}"」という属性が追加されている。これはJavaScript上のimagesArrayという変数と、このテンプレートをひもづけるという処理だ。imagesArrayという変数は、後でJavaScript内で使用する。

では、次にJavaScriptを記述しよう。まず、imagesArray変数を定義する。

var imagesArray = [];

次に、Observerクラスを使用して、imagesArrayに対する変更を監視する処理を記述する。コードは次のようになる。

Sys.Observer.makeObservable(imagesArray);

実は、この2行のJavaScriptで、HTML上のテンプレートとJavaScript上のimagesArray変数がひもづけられたことになる。後はimagesArrayに要素を追加したり削除したりすることで、HTMLも自動的に更新されるようになる。

それでは、まずimagesArrayの要素を削除する処理を記述する。とりあえず先頭の要素を削除するという処理にしておく。イベントハンドラから呼び出すため、onDeleteButtonClickという名前の関数で定義しておく。

function onDeleteButtonClick() {
    imagesArray.remove(imagesArray[0]);
}

最後にサーバーからデータをXMLで取得してimagesArrayに追加する処理を記述しよう。JavaScriptのコードからWebサービスにアクセスしてデータを取得する処理も、ASP.NET AJAXであれば非常に簡単に記述できる。

まず、JavaScriptからGetEmployeesメソッドにアクセスするためのクラスを生成するために、ScriptManagerにWebService1.asmxへの参照を追加する。

これだけの処理で、WebService1.asmxにアクセスするためのJavaScriptのクラスが自動生成される。インテリセンスも利用可能だ。

では、こうして生成したクラスを使用して、imagesArrayに要素を追加する処理を追加しよう。こちらもイベントハンドラから呼び出すため、onInsertButtonClickという名前の関数で定義しておく。

function onInsertButtonClick() {
    var ws = new WebApplication2.WebService1.GetEmployees(function(result) {
        for (var i = 0; i < result.length; i++) {
            imagesArray.add(result[i]);
        }
    });
}

GetEmployeesメソッドに渡す引数は、データ取得後に呼び出す処理を記述した関数になる。ここで、GetEmployeesの戻り値を、imagesArrayに追加している。

さて、あとは画面に追加ボタンと削除ボタンを追加し、それぞれクリックしたときにonInsertButtonClick関数とonDeleteButtonClick関数が呼び出されるようにすれば完成だ。ビルドして実行し、実際に動作することを確認してみよう。

このサンプルで行ったことをまとめると次のようになる。

  1. サーバーからデータ(XML or JSON)を取得する
  2. そのデータをテンプレートとバインドしている変数に代入する
  3. 自動的にDOM(画面)が更新される

Webサービスからデータを取得するロジックと、取得したデータをレンダリングするロジックが、明確に分離できているのが分かると思う。たとえば、レンダリングの仕方を変える場合でも、JavaScriptは一切変更する必要がないのだ。

このように、ASP.NET 4.0においては、Ajaxを使用したクライアントサイドのレンダリングも、非常に簡潔に記述できるようになっているのである。ASP.NET 3.5で追加されたWebサービス連携と組み合わせて使用すれば、高機能なAjaxアプリケーションが簡単に作れるようになるはずだ。

サンプルソースコード: WebApplication2.zip