かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

LightSwitch(HTML Client)でjsRenderを使ってテーブルを表示してみた

CodeZineでjsRenderというものを知りました。

これは、面白そうということでぐぐってたら公式ページとともにMSDNマガジンの記事が…!?華麗にスルーしてたけど、公式とあわせて読むととりあえず大体OKっぽい。

LightSwitchはjQueryとjQuery mobileが使えたりするので、jQuery mobileのテーブルの出し方はこちらを参考にしました。

最近の個人的なJavaScriptの遊び場

こういうJavaScriptで遊ぶ時は最近LightSwitchのHTMLClientを使ってます。お手軽に基本的な画面は作れてJavaScriptで色々いじくるための骨組みができるのでとってもいい感じです。

jsRenderの取り込み

jsRenderの公式からjsviews.min.jsをDLしてきてLightSwitchのFileViewにしてScriptsフォルダに置きます。ついでに、LightSwitchのランタイム自体もNuGetで更新してテーマもダークにしてみました。*2
default.htmのcssのファイル名をバージョンアップしたcssのバージョン番号とあうように変更します。

<link rel="stylesheet" type="text/css" href="Content/dark-theme-1.0.1.css" />
<link rel="stylesheet" type="text/css" href="Content/msls-dark-1.0.1.css" />

<link rel="stylesheet" type="text/css" href="Content/jquery.mobile.structure-1.3.1.min.css" />
<link rel="stylesheet" type="text/css" href="Content/msls-1.0.1.min.css" />

そして、scriptタグもバージョン番号を変えるのとjsRenderのJavaScriptを読み込むようにします。

<script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/globalize/0.1.1/globalize.min.js"></script>
<script type="text/javascript" src="Scripts/winjs-1.0.min.js"></script>
<script type="text/javascript" src="Scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="Scripts/jquery.mobile-1.3.1.min.js"></script>
<script type="text/javascript" src="Scripts/datajs-1.1.0.min.js"></script>
<script type="text/javascript" src="Scripts/Generated/resources.js"></script>
<script type="text/javascript" src="Scripts/msls-1.0.1.min.js"></script>
<script type="text/javascript" src="Scripts/Generated/generatedAssets.js"></script>
<script type="text/javascript" src="Scripts/jsviews.min.js"></script>

データの作成

とりあえず簡単にデータを定義します。NameとAgeを持っただけのシンプルなデータです。
f:id:okazuki:20130807232137p:plain
こいつを表示するためのBrowseページと編集追加する画面を適当に定義します。そして、その画面を使って4件くらいデータを入れました。
f:id:okazuki:20130807232607p:plain

jsRenderで遊ぶ

ついに本題です。jsRenderで遊びましょう。Personとして定義したデータを表示したいので、NameとAgeを持つテーブルの行を表示するテンプレートをdefault.htmに以下のように定義してみました。

<!-- テーブルのヘッダー部のテンプレート(固定テキストだからテンプレートにする必要性はないかも?) -->
<script id="personHeaderRowTemplate" type="text/x-jsrender">
    <tr>
        <th>名前</th>
        <th>年齢</th>
    </tr>
</script>
<!-- Personをテーブルの行として展開するテンプレート。名前はHTMLエンコードする -->
<script id="personRowTemplate" type="text/x-jsrender">
    <tr>
        <td>{{html:Name}}</td>
        <td>{{:Age}}</td>
    </tr>
</script>
<!-- Personの配列をテーブルの行として展開するテンプレート -->
<script id="personRowsTemplate" type="text/x-jsrender">
    {{for rows tmpl="#personRowTemplate"/}}
</script>

最初のはヘッダー。あとはテーブルのボディ部です。最後のテンプレートでforを使って複数行のデータの展開にも対応しています。テンプレートの定義ができたので、default.htmのreadyメソッドで、テンプレートを読み込んでおきます。

<script type="text/javascript">
    $(document).ready(function () {
        // テンプレートを読み込んでおく
        $.templates("personHeaderRowTemplate", "#personHeaderRowTemplate");
        $.templates("personRowTemplate", "#personRowTemplate");
        $.templates("personRowsTemplate", "#personRowsTemplate");
        msls._run()
        .then(null, function failure(error) {
            alert(error);
        });
    });
</script>

あとはBrowseのページにタブを新規追加して、そこにPersonSetのデータをレンダリングするカスタムコントロールを定義してレンダリング処理をかいていきます。ここらへんは定型句なんですが、jsRenderのおかげで退屈なHTML組み立て処理がかなりすっきりしています。
ちょっと困ったのは、jsRenderで作ったHTMLを$()に食わせるとエラーになったのでdivタグを作って、そこに流し込んでcontentsでjQueryのオブジェクトとして取り出しています。
*3

/// <reference path="../GeneratedArtifacts/viewModel.js" />
/// <reference path="../Scripts/jsviews.min.js" />

myapp.BrowsePersonSet.PersonSet_render = function (element, contentItem) {
    var $element = $(element);
    // テーブルの骨組みを組み立てる。jquery mobileのテーブルを使う
    var table = $('<table id="peopleTable" data-role="table" class="ui-responsive"/>')
        .appendTo($element);
    // ヘッダ
    var headerHtml = $.render.personHeaderRowTemplate();
    $('<thead />')
        .html(headerHtml)
        .appendTo(table);
    // ボディ
    var tbody = $('<tbody />').appendTo(table);

    var c = contentItem.value;

    // コレクションのデータをレンダリングする
    if (c.isLoaded) {
        renderRows(tbody, c.data);
        return;
    }

    c.addChangeListener("isLoaded", function () {
        renderRows(tbody, c.data);
    });
};

// tbodyにrowsで渡されたデータを追加する
function renderRows(tbody, rows) {
    var rowsHtml = $.render.personRowsTemplate({ rows: rows });
    $('<div />')
        .html(rowsHtml)
        .contents()
        .appendTo(tbody);
}

myapp.BrowsePersonSet.loadMorePeople_canExecute = function (screen) {
    // データに続きがある場合は読み込める
    return screen.PersonSet.canLoadMore;
};
myapp.BrowsePersonSet.loadMorePeople_execute = function (screen) {
    // データの続きを読み込み、tbodyにデータを追加する
    screen.PersonSet.loadMore().then(function (r) {
        renderRows($('#peopleTable tbody'), r.items);
    });
};

最後の2つのメソッドは、カスタムコントロールの下に置いたボタンの処理で、続きのデータがあったら読み込むといった処理になってます。

最後に、データの続きを読む処理を試すためにPersonSetの取得件数をプロパティから3件に減らしておきます。

実行してみよう

アプリを実行したら、データの読み込みが終わる前に素早くタブ切り替えをしてテーブルを表示してるタブに移動します。そうするとテーブルにデータが3件表示されています。

f:id:okazuki:20130807233735p:plain
続きを読み込むボタンを押すと、続きのデータが読み込まれます。今回はデータを4件しか作ってないので、これ以上データがないのでボタンが消えます。(無効化したら消えるように設定してたんだった)
f:id:okazuki:20130807233933p:plain

まとめ

  • LightSwitchのカスタムコントロールでjQuery mobileのコントロールの実験とかやると面白い。
  • jsRenderはいいテンプレートエンジンだ。
  • LightSwitchは、結構JavaScriptで何でもできそう
  • JavaScriptはインテリセンスあるとはいえC#に比べたら貧弱で辛い

*1:悲しいけど機械翻訳

*2:今度は自作テーマあててみようかな

*3:ここを参考にしました。http://stackoverflow.com/questions/11047670/creating-a-jquery-object-from-a-big-html-string