ThymeleafでUnderscore.jsのtemplateを使った話


Underscore.jsのtemplateは個人的に結構好きなので、ちょっとした処理をするときに使っています。
標題の組み合わせでアプリ作っていた時の事象について、メモ書きです。

## 構成

大体こんな感じのHTMLです。

```HTML:サンプル
...中略 
    <button id="searchButton" type="button">検索する</button>
 　　<h3 >検索結果</h3>
    <div id="result">
    </div>
...中略
    <script th:inline="javascript">
        $(function () {
            $('#searchButton').click(function () {
                $.ajax({
                    type: 'GET',
                    dataType: 'JSON',
                    cache: false,
                    url: '/api/customers'
                }).done(function (data, text, xhr) {
                    var template = _.template($('#customersTemplate').html());
                    $('#result').append(template({data: data}));
                });
            });
        });
    </script>
    <script th:inline="javascript" id="customersTemplate" type="text/template">
        <table>
          <thead>
            <tr>
              <th>名前</th>
              <th>年齢</th>
            </tr>
          </thead>
          <tbody>
            <% _.each(data.customers, function (customer) { %>
              <tr>
                <td><%- customer.name %></td>
                <td><%- customer.age %></td>
              </tr>
            <% }); %>
          </tbody>
        </table>
    </script>
...中略
```

Ajaxで取得できるdata(JSON)はこんな感じ。

```JSON:data
{
  "customers": [
    {
      "name": "Taro",
      "age": "20"
    },
    {
      "name": "Jiro",
      "age": "35"
    },
    {
      "name": "Naoki",
      "age": "37"
    }
  ]
}
```

templateでやりたいこととしては、Underscoreの_.eachを使って、
取得したdataの情報をテーブルとしてレンダリングしたい、ということになります。

### Thymeleafの解析エラーになる
上記サンプルをそのまま実行すると、

```
org.xml.sax.SAXParseException: 要素のコンテンツは、整形式の文字データまたはマークアップで構成されている必要があります。
```

と、解析エラーとなります。

```
 <% _.each(data.customers, function (customer) { %>
```

ここで、整形式ではないということになると。
モードをHTML5でやっているので、設定変えれば状況変わるかもしれませんが、
本末転倒な感があるので違うやり方を考えます。

### スクリプトのインライン処理を使う

http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf_ja.html#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E5%87%A6%E7%90%86-javascript-%E3%81%A8-dart

こんな感じでtemplate部分に適用してみます。

```HTML:template
    <script th:inline="javascript" id="customersTemplate" type="text/template">
        <table>
          <thead>
            <tr>
              <th>名前</th>
              <th>年齢</th>
            </tr>
          </thead>
          <tbody>
          /*<![CDATA[*/
            <% _.each(data.customers, function (customer) { %>
              <tr>
                <td><%- customer.name %></td>
                <td><%- customer.age %></td>
              </tr>
            <% }); %>
          /*]]>*/
          </tbody>
        </table>
    </script>

```

これで解析エラーにはならず画面の表示もできるので、実際に_.templateを使ったレンダリングを確認したところ、以下のようになりました。
※ 見やすいようにBootstrapでstyle当ててます。  
![table.png](https://qiita-image-store.s3.amazonaws.com/0/51995/b7cbee43-4030-a2f4-c1d0-45c1af82bb52.png "table.png")

「/* /*]]>*/」って、、、コメントの一部がレンダリングされたのでしょうか。
これ、ざっと調べただけでは原因までは追えなかったのですが、Ember.js
使って同じような事象になっている例がありました。

https://github.com/thymeleaf/thymeleaf/issues/261#issuecomment-35145916

で、こちらを参考にインライン処理を変更するとこんな感じになります。

```HTML:template
    <script th:inline="javascript" id="customersTemplate" type="text/template">
        <table>
          <thead>
            <tr>
              <th>名前</th>
              <th>年齢</th>
            </tr>
          </thead>
          <tbody>
          <![CDATA[
            <% _.each(data.customers, function (customer) { %>
              <tr>
                <td><%- customer.name %></td>
                <td><%- customer.age %></td>
              </tr>
            <% }); %>
          <!-- ]]> -->
          </tbody>
        </table>
    </script>
```

...何か気持ち悪いですね、CDATAの閉じタグのコメントアウトとか...
ただ、これを適用すると、想定通りのレンダリングになることが確認できました。

## で、結局どうしたのか。
Thymeleaf側を弄るのをやめました。
Underscore側で、templateSettingsを変更して対応します。

```javascript:templateSettingsの例
    _.templateSettings = {
        evaluate:    /\{\{(.+?)\}\}/g,
        interpolate: /\{\{=(.+?)\}\}/g,
        escape:      /\{\{-(.+?)\}\}/g
    };

```

構成は、最終的にこのようになりました。

```HTML:サンプル
...中略 
    <button id="searchButton" type="button">検索する</button>
 　　<h3 >検索結果</h3>
    <div id="result">
    </div>
...中略
    <script th:inline="javascript">
        $(function () {
            $('#searchButton').click(function () {
                $.ajax({
                    type: 'GET',
                    dataType: 'JSON',
                    cache: false,
                    url: '/api/customers'
                }).done(function (data, text, xhr) {
                    var template = _.template($('#customersTemplate').html());
                    $('#result').append(template({data: data}));
                });
            });
            _.templateSettings = {
                    evaluate:    /\{\{(.+?)\}\}/g,
                    interpolate: /\{\{=(.+?)\}\}/g,
                    escape:      /\{\{-(.+?)\}\}/g
            };
        });
    </script>
    <script th:inline="javascript" id="customersTemplate" type="text/template">
        <table>
          <thead>
            <tr>
              <th>名前</th>
              <th>年齢</th>
            </tr>
          </thead>
          <tbody>
            {{ _.each(data.customers, function (customer) { }}
              <tr>
                <td>{{- customer.name }}</td>
                <td>{{- customer.age }}</td>
              </tr>
            {{ }); }}
          </tbody>
        </table>
    </script>
...中略
```

やりたかった挙動は実現できるので、これでいいかと思いました。
他にもよいやり方があったら教えて下さい。

## 使用したライブラリ
* Thymeleaf：2.1.4.RELEASE  
※ Spring Boot経由で使用
→ spring-boot-starter-thymeleaf：1.2.7.RELEASE
* underscore.js：1.8.3


## 参考ページ
以下を参考にさせていただきました。
ありがとうございました。  
http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf_ja.html#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E5%87%A6%E7%90%86-javascript-%E3%81%A8-dart
https://github.com/thymeleaf/thymeleaf/issues/261#issuecomment-35145916