kintone のカスタマイズビュー に Underscore.js(lodash) の _.template を使うサンプル

kintone には ユーザーが独自の一覧画面を作成できる カスタマイズビュー という機能があります。
[kintone developer network](https://developer.cybozu.io/hc/ja) にも初心者向けから上級者向けまでのチュートリアルが用意されていますが、本当の初心者にはまだまだ理解が難しいと感じています。

今回、[kintone developer network](https://developer.cybozu.io/hc/ja) にありそうで無かった、Underscore.js の _.template を使ったサンプルを、コードの解説を交えて公開致します。

この段階を理解することは、Vue.jsやReact等の仕組みを理解して使っていく基礎知識では無いかと考えています。

コードに関しては、もっと良い書き方もあるかと思いますので、識者の方のご指摘もお願い致します。

# 概要
## やっていること
- テーブルが１つだけのシンプルな kintoneアプリを作成
- カスタマイズビューを追加
- カスタマイズビューのHTMLに lodashのテンプレートを設置
- JavaScript側で lodashのテンプレートを読み込み
- JavaScript側で kintone REST API をたたいて、レコードを取得
- 取得したレコードを 読み込んだテンプレートに流し込みHTMLを生成
- カスタマイズビューに 生成したHTMLを追加

# kintone アプリの準備
次のようなテーブルだけのkintoneアプリを作ります。
<img width="903" alt="スクリーンショット 2018-04-19 7.01.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/214523/46634230-8abd-3652-92d1-86c1679cde78.png">

# カスタマイズビュー HTML
一覧名:sort

```html
<style type="text/css">
.custum-view {
  padding: 16px 0 16px 16px;
}
</style>

<div class="custum-view">
  <div class="container">
    <table class="kintoneplugin-table">
      <thead>
        <th class="kintoneplugin-table-th"><span class="title">No.</span></th>
        <th class="kintoneplugin-table-th"><span class="title">項目</span></th>
      </thead>
      <tbody id="template">
      </tbody>
    </table>
    <div id="result"></div>
  </div>
</div>

<script type="text/html" id="lodashTemplate">
  <% _.forEach(fields, function(field) { %>
    <tr id="<%= field.No %>">
      <td><div class="kintoneplugin-table-td-control"><div class="kintoneplugin-table-td-control-value"><div class="kintoneplugin-input-outer"><%= field.No %></div></div></div></td>
      <td><div class="kintoneplugin-table-td-control"><div class="kintoneplugin-table-td-control-value"><div class="kintoneplugin-input-outer"><%= field.item %></div></div></div></td>
    </tr>
  <% }); %>
</script>
```

# JavaScript

```sort.js
jQuery.noConflict();
(function($) {
    "use strict";
    
    kintone.events.on("app.record.detail.show", function(event) {
        var record = event.record;
        var viewId = 123456;
        var app = kintone.app.getId();
        var recordId = record.$id.value;
        var href = '/k/' + app + '/';
        var string =
        '<input type="button" onclick=location.href="' + href + '?view=' + viewId + '&record=' + recordId +'" value="sortへ移動">' +
        '</input>';
        var html = _.template(string);
        $(kintone.app.record.getHeaderMenuSpaceElement()).append(html);
        return event;
    });

    kintone.events.on("app.record.index.show", function(event) {
        var undefined;
        var record = event.record;
        var app = kintone.app.getId();

        if (event.viewId !== 123456) {
            return;
        }

        var arg = {};
        var pair = location.search.substring(1).split('&');
        for (var i = 0; pair[i]; i++) {
            var kv = pair[i].split('=');
            arg[kv[0]] = kv[1];
        }
        if (arg.record === undefined) { return }

        var body = {
            "app": app,
            "id": arg.record
        };
        
        kintone.api(kintone.api.url('/k/v1/record', true), 'GET', body).then(function (resp) {
            var record = resp.record;
            var props = [];
            for (var i = 0; i < record.Table.value.length; i++) {
                props.push({
                    "No": record.Table.value[i].value.No.value,
                    "item": record.Table.value[i].value["項目"].value
                });
            }
            
            var lodashTemplate = $("#lodashTemplate").html();
            var compiled = _.template(lodashTemplate)({fields: props});
            $("#template").html(compiled);
        }).catch(function (error) {
            // error
            console.log(error);
        });        

        $('#template').sortable({
            cursor: 'move',
            opacity: 0.5,
            update: function(e, ui) {
                $('#result').text($('#template').sortable('toArray'));
            }
        });
        return event;
    });
})(jQuery);
```

# カスタマイズビュー表示結果
<img width="548" alt="スクリーンショット 2018-04-22 11.26.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/214523/cda00ddd-bf00-23f5-7276-324e19581bec.png">

# 解説

## kintoneカスタマイズビューとは何か？
ユーザー独自の一覧画面を作成できる機能です。
これを使うには、いわゆるJavaScriptカスタマイズの理解に加えて、HTML（及びCSS）の知識が必要になります。
カスタマイズビューの基本はこんな感じ。
1. カスタマイズビューを作成
2. JavaScriptからカスタマイズビューにHTMLを追加

## どのように実装するのか？
いろいろなやり方があり、[kintone developer network](https://developer.cybozu.io/hc/ja) のサンプルも統一されてない為、初学者には迷うところです。

そこで、今回はシンプルな流れで説明します。

1. カスタマイズビューに記述したHTMLのタグにCSSのid属性を設定します
2. JS側でkintoneからレコードを取得します
3. 2.で取得したレコードをカスタマイズビューのHTMLに追加します

個人的に理解しがたいと感じた、上記の1.と3.について詳しく説明します。

## HTMLをJavaScriptで操作する基本
下記HTMLをご覧下さい。

```myCustomize.html
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>myCustomize</title>
</head>
<html>
    <body>
        <p>カスタマイズサンプル</p>
        <div id="my-Customize">
          <!-- ここにJavaScriptからデータを埋め込みます -->
        </div>
    </body>
</html>
<script type="text/javascript">
    var myCustomize = document.getElementById('my-Customize');
    myCustomize.innerHTML = '<h1>テスト</h1>';
</script>

```

分けて説明します。

### 1. カスタマイズビューに記述したHTMLのタグにCSSのid属性を設定します

最終的に JavaScript側から HTMLに何らかのデータを埋め込むことができるように、HTMLタグにIDを付けておきます。
これにより、HTML文書の中の場所が特定されます。

``` myCustomize.html
<div id="my-Customize"> <!-- JS側から要素を特定する為にIDを設定 -->
  <!-- ここにJavaScriptからデータを埋め込みます -->
</div>

```

### 3. 2.で取得したレコードをカスタマイズビューのHTMLに追加します

JavaScriptのメソッド getElementById を使って、上記で設定したIDのHTML要素を取得します。
var myCustomize = document.getElementById('my-Customize');

このとき、変数 myCustomize の中身は下記の通りです。

`<div id="my-Customize"></div>`


``` myCustomize.js
// 上記の ID("my-Customize")で指定した要素を取得します
var myCustomize = document.getElementById('my-Customize');
```

最後に、取得したHTMLの間に、innerHTML プロパティを使ってデータ（この例では"テスト"という文字列）を埋め込みます。

```html

// JSからHTMLにデータを埋め込みます
myCustomize.innerHTML = '<h1>テスト</h1>';
```

初学者にはここが分かりにくい点だと感じます。
上記では、 `<h1>テスト</h1>` と HTMLコードとして埋め込んでいますが、テキストだけの場合は、 innerText または textContent を使っても同じ結果になります。
[element.innerHTML](https://developer.mozilla.org/ja/docs/Web/API/Element/innerHTML)

```
// JSからHTMLにデータを埋め込みます

// 例1 テキストを埋め込みます
myCustomize.innerText = 'テスト';

// 例2 テキストを埋め込みます
myCustomize.textContent = 'テスト';
```

# 最初のコードの説明
最初のコードの話に戻ります。
ここで説明したいことは、「仕組みは上記の基本で述べたことと同じ」ということです。

肝心な箇所は下記の scriptタグの type="text/html" のところだけです。
`<script type="text/html" id="lodashTemplate"></script>`

kintoneのカスタマイズビューでIDを指定したテンプレートを記述しておいて、JavaScript側でkintoneから取得したレコードのデータを lodashの template関数に引数として渡して、テンプレートに記述した <%= field.No %> 部分に差し込み、最後に `<tbody id="template"></tbody>` の間にHTMLとして埋め込みます。

主な箇所を順を追って説明します。

# 最終的なHTMLを埋め込む箇所
下記のID属性を付けた tbodyタグの間（子要素）に HTMLを埋め込みます。

```html

<tbody id="template">
</tbody>
```

# HTMLのテンプレート部分の説明

```html

<script type="text/html" id="lodashTemplate">
  <% _.forEach(fields, function(field) { %>
    <tr id="<%= field.No %>">
      <td><div class="kintoneplugin-table-td-control"><div class="kintoneplugin-table-td-control-value"><div class="kintoneplugin-input-outer"><%= field.No %></div></div></div></td>
      <td><div class="kintoneplugin-table-td-control"><div class="kintoneplugin-table-td-control-value"><div class="kintoneplugin-input-outer"><%= field.item %></div></div></div></td>
    </tr>
  <% }); %>
</script>
```
_.forEachについては[_.forEach](https://lodash.com/docs/4.17.5#forEach)の説明を参照してください。
_.forEachに渡した引数 fields には オブジェクトの配列をJS側でセットします。
実行時に function(field) にオブジェクトが１つづつ渡り、<%= field.No %> の箇所が差し替えられます。
素のHTMLとテンプレート変数で置き換える <%= %> が混ざっていることで見にくいですが、
<%= %> の間にある記述は、HTMLとして評価されないと思って下さい。
<%= %> が変数で置き換わる（あくまでイメージですが）ことは[_.template](https://lodash.com/docs/4.17.5#template)のドキュメントの例を見ていただければイメージできると思います。

# JavaScriptの説明
kintoneから取得した１レコード内のテーブルのデータをオブジェクトの配列にセットします。

```javascript

            var props = [];
            for (var i = 0; i < record.Table.value.length; i++) {
                props.push({
                    "No": record.Table.value[i].value.No.value,
                    "item": record.Table.value[i].value["項目"].value
                });
            }
```

次にテンプレートを読み込んで、テンプレートタグ(<%= %>)にデータを渡します。
ここでは、jQueryを使って書いています。
`$("#lodashTemplate").html()` でID要素で指定したHTMLを取得しています。
jQueryの [.html()](http://api.jquery.com/html/#html)はこちらから確認ください。

```javascript
            
            var lodashTemplate = $("#lodashTemplate").html();
            var compiled = _.template(lodashTemplate)({fields: props});
            $("#template").html(compiled);
```

jQueryを使わないで、下記の様に書いても同じです。

```javascript
var lodashTemplate = document.getElementById("lodashTemplate").innerHTML;
var compiled = _.template(lodashTemplate)({fields: props});
document.getElementById("template").innerHTML = compiled;
```

# まとめ

今回の記事では、下記について説明してきました。

- HTMLの文章に、JavaScriptからデータを追加する基本の操作
- HTMLテンプレートの利用について

kintoneだけでなく、データベースからレコードを取得して一覧に表示させるということは、良く有るのではと思います。
その際には、JavScript側でHTMLタグを生成するのでは無く、できるだけ今回紹介したlodashの_.templateのようなテンプレートエンジンを利用して、HTMLとプログラミングのコードを分離することで、コードも見やすく又マークアップエンジニアの方も作業がやり易い環境が作れると考えます。

時間がありましたら、Vue.jsで置き換えたサンプルも作りたいと思います。

---
# 記事とコードの引用について

以下様々なサイトより記事やコードを引用しております。ありがとうございます。

- undefined値の判定は次から引用しております(http://blog.tojiru.net/article/205007468.html)
- _.templateの使い方は次から引用しております。 (https://qiita.com/nabettu/items/c4c6670e67972b40bbd7),
(https://www.buildinsider.net/web/bookjslib111/101)