かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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