【kintone】詳細画面でサブテーブルの値を変える

詳細画面を開くたびに、ある日付フィールドから現在までどれだけ時間が経過したか、分かるようなアプリを作ってみたいと思います。
レコードに保存するわけではなく、**見た目だけ**変えます。

# アプリの準備
見た目は画像のような感じで、赤字はフィールドコードです。

あの日：日付
経過日数：文字列(1行)
イベント：文字列(1行)
です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/5bdab438-3a1e-c25c-bf7b-51f83069d6d2.png)

# JavaScript

説明は後述します。

```js

kintone.events.on("app.record.detail.show", (event) => {
  const record = event.record;
  //サンプルコード流用↓
  const calculateDuration = (dateStr) => {
    const currentDate = luxon.DateTime.local().startOf("day");
    const date = luxon.DateTime.fromISO(dateStr).startOf("day");
    // 経過期間を計算する
    const duration = currentDate.diff(date, ["years", "months", "days"]);
    return duration.toObject();
  };
  // あの日からの経過日数を計算する
  if (record["テーブル"].value.length === 0) {
    return event;
  }
  //テーブルの要素を取得
  const tableData = kintone.app.record.getFieldElement("テーブル");
  //テーブルのtbody要素を取得
  const tableBody = tableData.tBodies[0];
  //テーブルのrows（行、tr）の要素を取得
  const tableRows = tableBody.rows;
  //テーブルの最後の行まで全部画面表示されるまで待つ（要素が見つかるまで0.1秒ごとに実行する）
  const set_interval_id = setInterval(() => {
    // 要素を見つける処理
    if (!!tableRows[record["テーブル"].value.length - 1]) {
      Array.prototype.slice.call(tableRows).forEach((tr, idx) => {
        const keika = calculateDuration(record.テーブル.value[idx].value.あの日.value);
        tr.cells[1].firstChild.firstChild.firstChild.textContent = keika.years + "年" + keika.months + "ヶ月";
      });
      clearInterval(set_interval_id);
    }
  }, 100);
  return event;
});
```

## Luxon
日付を扱うライブラリとしてLuxonを使いました。
Cybozu CDNはこちらを確認してね
https://developer.cybozu.io/hc/ja/articles/202960194#luxon

経過年月を取得する関数についてはこちらのサンプルコードを(varをconstに変えて)使いました。
https://developer.cybozu.io/hc/ja/articles/900000985463#usage-kintone

```js

//サンプルコード流用↓
const calculateDuration = (dateStr) => {
  const currentDate = luxon.DateTime.local().startOf("day");
  const date = luxon.DateTime.fromISO(dateStr).startOf("day");
  // 経過期間を計算する
  const duration = currentDate.diff(date, ["years", "months", "days"]);
  return duration.toObject();
};
```


## フィールド要素を取得する
テーブルのフィールドは直接取ってこれないので、ひとまずテーブルの要素を取得し、~~その要素の`children`をたどっていって・・・~~ `tbody`を取得後、`rows (tr)`の要素を取得するというDOM操作です。そのままDOM操作しようとしても表示が終わってなくて行の要素が取れないので、`setInterval`でテーブルの最後の行が表示されるまで待ってから行います。
https://developer.cybozu.io/hc/ja/articles/201942014#step3


```js
//テーブルの最後の行まで全部画面表示されるまで待つ（要素が見つかるまで0.1秒ごとに実行する）
const setIntervalId = setInterval(() => {
```

~~`.children`で取得した要素の情報(tableRows)~~ `tableBody.rows`で取得した行の要素は、配列オブジェクトではなくてHTMLCollectionというオブジェクトです。配列ではないので`forEach`などの配列のメソッドが使えません。↓のように`Array.prototype.slice.call(tableRows)`とやって配列オブジェクトに変えてから`forEach`を使います。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/slice#Array-like_objects

```js
const tableRows = tableBody.rows;
～略～
//HTMLCollectionであるtableRowsを配列に変形しつつ、forEach回す
Array.prototype.slice.call(tableRows).forEach((tr, idx) => {
  const keika = calculateDuration(record.テーブル.value[idx].value.あの日.value);
  tr.cells[1].firstChild.firstChild.firstChild.textContent = keika.years + "年" + keika.months + "ヶ月";
});
```

# できあがり

見た目だけだけど、今日までの経過年月を表示することができました。
未来の日付にしていると経過年月がマイナスになっちゃいますが、今回はそれでヨシとしましょう！

DOMをいじるというカスタマイズでしたが、kintone側のアップデートなどにより、htmlの構成が変わってしまうと使えなくなる可能性もあるので、要注意です👀

というわけで、今回はこのへんで～ﾉｼﾉｼ

![dom.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/960e1e18-b229-400d-a61e-8ea1771caa39.gif)