ThymoljsでEL式を動くようにする

【前提条件】

[環境]
[参考サイト]

Thymoljs公式サイト
Thymol Home

【概要】

前回、Thymoljsを使えばth:replaceが再現できることがわかりました。
ただし、前回のソースではEL式によるデータの参照ができない状態です。

今回はEL式によるデータの参照をさせてみます。

【HTMLソース】

サンプルソースは前回とほぼ同じです。

[top.html]
<!DOCTYPE html>
<html>
    <head th:include="/common/base/metaInfo :: commonHeader (title='Top Page')" >
        <title th:text="${title}">テストタイトル</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <script src="/js/lib/thymol/thymol.js" th:remove="all"
                data-thymol-load="/js/lib/thymol/project-setting.js, /js/lib/thymol/data/reviews.js"></script>
        <link rel="stylesheet" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="bs-content-area">
            <div th:replace="/common/component/topMenu :: topMenu">
            </div>
            <div class="bs-left-menu">
                <div th:replace="/genreSearch/genreSearch :: genreSearch">
                </div>
            </div>
            <div class="bs-main-contents">
                <div th:replace="/review/review :: reviewArea(reviewView=${it.review})">
                </div>
            </div>
        </div>
    </body>
</html>

前回と違う箇所の一つにdata-thymol-loadに「/js/lib/thymol/data/reviews.js」を追加したことがあります。
data-thymol-loadに複数指定したい場合はカンマで区切ります。
review.jsは今回のメインとなる箇所でデータを設定しているJSファイルです。

もう一つの違う箇所はdiv.bs-main-contents配下に新しいコンポーネントがあることです。
前回の状態でこの行があると上手く動かなかったのです。
原因としてはEL式にデータ(${it.review})がなかったことでした。

今回のサンプルではここを動くようにしています。

[review.html]

追加されたコンポーネント部分のソースです。

<!DOCTYPE html>
<html>
    <head th:fragment="commonHeader">
        <title>レビューページ</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="fm-review" th:fragment="reviewArea (reviewView)">
            <ul th:each="review : ${reviewView.reviews}">
                <li>
                    <h2 th:text="${review.title}">テストタイトル</h2>
                    <dl>
                        <dt>熱中度</dt>
                        <dd>5点</dd>
                        <dt>ストーリー</dt>
                        <dd>4点</dd>
                        <dt>操作性</dt>
                        <dd>3点</dd>
                        <dt>ロード時間</dt>
                        <dd>2点</dd>
                        <dt>音楽</dt>
                        <dd>1点</dd>
                    </dl>
                    <h3>良い点</h3>
                    <p th:text="${review.goodPoint}">
                        テストテストテスト。
                    </p>
                    <h3>悪い点</h3>
                    <p th:text="${review.badPoint}">
                        テストテストテスト。
                    </p>
                    <h3>コメント</h3>
                    <p th:text="${review.comment}">
                        テストテストテスト。
                    </p>
                </li>
            </ul>
        </div>
    </body>
</html>

th:fragmentでフラグメント指定し、パラメータを受け取ります。
パラメータはBeanを想定します。
こんな感じのBeanです。

public class ReviewView {

    public final List<ReviewBean> reviews;
}

public class ReviewBean {

    public final String title;

    public final String goodPoint;

    public final String badPoint;

    public final String comment;
}
[topMenu.html]

前回と変わりはありません。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Top Page</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="bs-top-menu" th:fragment="topMenu">
            <ul class="right-menu">
                <li><a href="" th:href="@{/top}" >TOP</a></li>
                <li><a href="../../login/login.html" th:href="@{/login}" >ログイン</a></li>
            </ul>
        </div>
    </body>
</html>
[genreSearch.html]

前回と変わりはありません。
前回のソースではEL式が動作せず、HTMLがそのまま出力されていました。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>ジャンル検索</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="cp-genre-search" th:fragment="genreSearch">
            <h1>ジャンル検索</h1>
            <ul>
                <li th:each="genre : ${it.genreSearch.genres}">
                    <a th:href="@{/genreSearch/id/{id}(id=${genre.id})}" th:text="${genre.title}">テスト</a>
                </li>
                <li th:remove="all">
                    <a href="#">アクション</a>
                </li>
                <li th:remove="all">
                    <a href="#">RPG</a>
                </li>
            </ul>
       </div>
    </body>
</html>

【データ設定】

では、データを設定するスクリプトを作成します。
データを設定するスクリプトは「/js/lib/thymol/data/reviews.js」に置きます。

thymol.configurePreExecution(function () {
    var data = {
        genreSearch: {
            genres: [
                {
                    id: 1,
                    title: "アクション"
                },
                {
                    id: 2,
                    title: "RPG"
                },
                {
                    id: 3,
                    title: "シミュレーション"
                }
            ]
        },
        review: {
            reviews: [
                {
                    title: "Thymol.jsのタイトル",
                    goodPoint: "Thymol.jsにより埋め込んだコメントです",
                    badPoint: "Thymol.jsにより埋め込んだコメントです2",
                    comment: "Thymol.jsにより埋め込んだコメントです3"
                }
            ]
        }
    };
    thymol.requestContext.createVariable("it", data);
});

thymol.configurePreExecutionは
公式ドキュメントによるとThymolのメイン処理が実行されるということです。
(メイン処理と言っているのはDOM書き換えのことだと思います)

ThymolではapplicationContext、sessionContext、requesetContextがありますが、
今回はrequestContextを使っています。
createVariableにキーと値を渡すとEL式として使うことができます。

サンプルでは「it」がルートとなっているので、キーには「it」を指定します。
値はBeanなのでBeanと同じ構成になるようなJSONオブジェクトを作成します。

【実際の画面】

作成したソースをApaceh経由で表示させてみます。
[0001.png]

EL式の部分が置き換わっているのがわかります。

【まとめ】

前回と今回でThymolの基本的な使い方を試してみました。

JavaScriptだけでThymeleafを実行した時と同じ結果が表示されるのは非常によいと思いました。
これでデザインチームと協業する時に同じ結果を見ながら作業ができます。
また、仮にプログラマだけで構成されたチームだとしても画面だけを確認したい場合も使えると思います。

th:replaceに関してはクロスサイトリクエストの問題がありましたが、
Apacheを経由すれば解決できます。

個人的にJavaScriptをきれいにモジュール化する手法を知らないので、
アプリケーションが巨大になった場合、いくつかのソースを共通化したい場合などは
課題になるかなと思ったりします。