テーブル操作の基本

にメンテナンス済み

kintone のテーブル(サブテーブル)は、1 つのレコード内で複数の行データを管理できる便利な機能です。見積明細や訪問記録、タイムカードなど、多くの業務アプリで活用されています。

しかし、JavaScript カスタマイズでテーブルを操作する場合、そのデータ構造は入れ子が深く、慣れていないと扱いにくいと感じる方も多いでしょう。

この記事では、kintone のテーブルを JavaScript で操作するための基本的な知識と、実践的なサンプルコードを紹介します。

テーブルのデータ構造を理解する

テーブルを操作するためには、まずそのデータ構造を正確に理解することが重要です。

テーブルフィールドの基本構造

kintone のテーブルフィールドは、以下のような構造を持っています。

{
  "テーブル": {
    "type": "SUBTABLE",
    "value": [
      {
        "id": "12345",
        "value": {
          "文字列フィールド": { "type": "SINGLE_LINE_TEXT", "value": "値1" },
          "数値フィールド": { "type": "NUMBER", "value": "100" }
        }
      },
      {
        "id": "12346",
        "value": {
          "文字列フィールド": { "type": "SINGLE_LINE_TEXT", "value": "値2" },
          "数値フィールド": { "type": "NUMBER", "value": "200" }
        }
      }
    ]
  }
}

構造のポイント

  1. テーブルフィールド自体: typeSUBTABLEvalue は行の配列
  2. 各行: id(行の識別子)と value(その行のフィールド群)を持つオブジェクト
  3. 行内のフィールド: 通常のフィールドと同じ構造
行IDについて

id は既存の行を識別するためのものです。新規に行を追加する場合は id を指定する必要はありません。kintone が自動的に採番します。

テーブルの値を取得する

イベントハンドラー内で取得する場合

kintone.events.on のコールバック関数内では、引数の event オブジェクトからテーブルの値を取得できます。

(() => {
  'use strict';

  kintone.events.on(['app.record.detail.show'], (event) => {
    const { record } = event;

    // テーブル全体の配列を取得
    const tableRows = record['テーブル'].value;

    // 各行のデータにアクセス
    tableRows.forEach((row, index) => {
      const textValue = row.value['文字列フィールド'].value;
      const numberValue = row.value['数値フィールド'].value;
      console.log(`行${index + 1}: ${textValue}, ${numberValue}`);
    });

    return event;
  });
})();

任意のタイミングで取得する場合

ボタンクリック時など、イベントハンドラー外で値を取得する場合は kintone.app.record.get() を使用します。

const handleButtonClick = () => {
  // 現在のレコード情報を取得
  const { record } = kintone.app.record.get();

  // テーブルの値を取得
  const tableRows = record['テーブル'].value;

  console.log('テーブルの行数:', tableRows.length);
  console.log('テーブルのデータ:', tableRows);
};
チェック

kintone.app.record.get() は、レコードの追加画面、編集画面、詳細画面でのみ使用できます。一覧画面では使用できません。

テーブルに行を追加する

テーブルに新しい行を追加するには、テーブルの value 配列に新しい行オブジェクトを追加します。

基本的な行の追加

(() => {
  'use strict';

  kintone.events.on(['app.record.create.show', 'app.record.edit.show'], (event) => {
    const { record } = event;

    // 新しい行データを作成
    const newRow = {
      value: {
        文字列フィールド: { value: '新しい値', type: 'SINGLE_LINE_TEXT' },
        数値フィールド: { value: '300', type: 'NUMBER' },
      },
    };

    // テーブルの末尾に行を追加
    record['テーブル'].value.push(newRow);

    return event;
  });
})();
typeプロパティについて

行を追加する際、各フィールドの type プロパティは省略可能です。省略した場合、kintone が自動的に判定します。ただし、明示的に指定しておくとコードの可読性が向上します。

特定の位置に行を挿入

配列の splice メソッドを使うと、任意の位置に行を挿入できます。

// 先頭に行を追加
record['テーブル'].value.unshift(newRow);

// 2番目の位置に行を挿入
record['テーブル'].value.splice(1, 0, newRow);

イベントハンドラー外で行を追加

const addTableRow = () => {
  // 現在のレコードを取得
  const data = kintone.app.record.get();
  const { record } = data;

  // 新しい行を作成
  const newRow = {
    value: {
      文字列フィールド: { value: '追加された値' },
      数値フィールド: { value: '500' },
    },
  };

  // 行を追加
  record['テーブル'].value.push(newRow);

  // 変更をレコードに反映
  kintone.app.record.set(data);
};
チェック

kintone.app.record.set() を呼び出さないと、変更がレコードに反映されません。必ず最後に呼び出してください。

テーブルの行を更新する

既存の行を更新するには、対象の行を特定してフィールドの値を変更します。

インデックスを指定して更新

(() => {
  'use strict';

  kintone.events.on(['app.record.edit.show'], (event) => {
    const { record } = event;

    // 1行目(インデックス0)の値を更新
    if (record['テーブル'].value.length > 0) {
      record['テーブル'].value[0].value['文字列フィールド'].value = '更新された値';
      record['テーブル'].value[0].value['数値フィールド'].value = '999';
    }

    return event;
  });
})();

条件に合致する行を更新

(() => {
  'use strict';

  kintone.events.on(['app.record.edit.show'], (event) => {
    const { record } = event;

    // 数値が100より大きい行の文字列を更新
    record['テーブル'].value.forEach((row) => {
      const numberValue = Number(row.value['数値フィールド'].value);
      if (numberValue > 100) {
        row.value['文字列フィールド'].value = '高額';
      }
    });

    return event;
  });
})();

map を使った一括更新

すべての行に同じ処理を適用する場合、map メソッドが便利です。

(() => {
  'use strict';

  kintone.events.on(['app.record.edit.show'], (event) => {
    const { record } = event;

    // すべての行の数値を2倍にする
    record['テーブル'].value = record['テーブル'].value.map((row) => {
      const currentValue = Number(row.value['数値フィールド'].value) || 0;
      row.value['数値フィールド'].value = String(currentValue * 2);
      return row;
    });

    return event;
  });
})();
チェック

mapfilter を使う場合は、必ず結果をテーブルの value に再代入してください。これらのメソッドは元の配列を変更せず、新しい配列を返します。

テーブルの行を削除する

インデックスを指定して削除

(() => {
  'use strict';

  kintone.events.on(['app.record.edit.show'], (event) => {
    const { record } = event;

    // 最後の行を削除
    if (record['テーブル'].value.length > 0) {
      record['テーブル'].value.pop();
    }

    // 先頭の行を削除
    // record['テーブル'].value.shift();

    // 特定のインデックス(例:2番目)の行を削除
    // record['テーブル'].value.splice(1, 1);

    return event;
  });
})();

条件に合致する行を削除

filter メソッドを使うと、条件に合致しない行だけを残すことができます。

(() => {
  'use strict';

  kintone.events.on(['app.record.edit.show'], (event) => {
    const { record } = event;

    // 数値が0の行を削除(0でない行だけを残す)
    record['テーブル'].value = record['テーブル'].value.filter((row) => {
      const numberValue = Number(row.value['数値フィールド'].value);
      return numberValue !== 0;
    });

    return event;
  });
})();

空の行を削除する実践例

(() => {
  'use strict';

  // すべての値が空の行を削除する
  const removeEmptyRows = (tableRows) => {
    return tableRows.filter((row) => {
      // 行内のすべてのフィールドを確認
      const fieldValues = Object.values(row.value);
      // 少なくとも1つの値が存在する行を残す
      return fieldValues.some((field) => {
        const value = field.value;
        return value !== '' && value !== null && value !== undefined;
      });
    });
  };

  kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], (event) => {
    const { record } = event;

    record['テーブル'].value = removeEmptyRows(record['テーブル'].value);

    return event;
  });
})();

REST API でテーブルを操作する場合の注意点

REST API を使ってレコードを更新する際、テーブルの操作には特別な注意が必要です。

行 ID の扱い

REST API でテーブルを更新する場合、行の id を指定しないと、既存の行がすべて削除されて新しい行に置き換わります

// ❌ 悪い例:既存の行がすべて削除される
const updateParams = {
  app: kintone.app.getId(),
  id: recordId,
  record: {
    テーブル: {
      value: [
        {
          value: {
            文字列フィールド: { value: '新しい値' },
          },
        },
      ],
    },
  },
};
// ✅ 良い例:既存の行を保持しつつ更新
const updateParams = {
  app: kintone.app.getId(),
  id: recordId,
  record: {
    テーブル: {
      value: [
        {
          id: '12345', // 既存の行IDを指定
          value: {
            文字列フィールド: { value: '更新された値' },
          },
        },
        {
          // 新しい行(idを指定しない)
          value: {
            文字列フィールド: { value: '追加された値' },
          },
        },
      ],
    },
  },
};
チェック

REST API でテーブルを更新する場合は、必ず既存の行の id を取得して指定してください。指定しないと、意図せずデータが失われる可能性があります。

安全な更新パターン

const updateTableSafely = async (recordId, newRowData) => {
  const appId = kintone.app.getId();

  // 1. 現在のレコードを取得
  const currentRecord = await kintone.api(kintone.api.url('/k/v1/record.json', true), 'GET', {
    app: appId,
    id: recordId,
  });

  // 2. 既存のテーブル行を取得(idを保持)
  const existingRows = currentRecord.record['テーブル'].value;

  // 3. 新しい行を追加
  const updatedRows = [
    ...existingRows, // 既存の行(idが含まれている)
    {
      value: newRowData, // 新しい行(idなし)
    },
  ];

  // 4. レコードを更新
  return kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', {
    app: appId,
    id: recordId,
    record: {
      テーブル: { value: updatedRows },
    },
  });
};

テーブル操作のまとめ

操作イベントハンドラー内イベントハンドラー外
取得event.record['テーブル'].valuekintone.app.record.get().record['テーブル'].value
追加push() / unshift() / splice()同左 + kintone.app.record.set()
更新直接代入 / map()同左 + kintone.app.record.set()
削除pop() / shift() / splice() / filter()同左 + kintone.app.record.set()

練習問題

kintone のテーブルフィールドの value プロパティには何が格納されていますか?

テーブルフィールドの value プロパティには、各行を表すオブジェクトの配列が格納されています。各行オブジェクトは id(既存行の場合)と value(その行のフィールド群)を持ちます。

kintone.app.record.get() で取得したレコードを変更した後、変更を反映するために必要な処理は何ですか?

kintone.app.record.get() で取得したデータを変更した場合、kintone.app.record.set() を呼び出して変更をレコードに反映する必要があります。これを呼び出さないと、変更は画面に反映されません。

REST API でテーブルを更新する際、既存の行を保持するために必要なことは何ですか?

REST API でテーブルを更新する場合、既存の行を保持するには、その行の id を指定する必要があります。id を指定しない行は新規行として扱われ、id を指定しなかった既存行は削除されます。

関連記事

サブテーブルの並び替え
皆さんはサブテーブル、利用されていますか?とても便利な機能ですが、情報量が増えてくると、閲覧性に問題が起こってしまうこともあります。今回は解決策の1つとして、サブテーブルの各行を並び替えて登録しなおすJavaScriptカスタマイズを紹介い
レコードの更新(1件)
kintone REST APIを使用して、レコードを1件更新する方法を紹介します。Node.jsやGASでのサンプルコードも掲載しています。
全体の構造とevents.on
実用的なサンプルを交えながら、kintoneにおけるJavaScriptカスタマイズの使い方を紹介します。基本的なプログラムの構造から、スペースフィールドにボタンを設置する方法までを解説しています。
#kintone #JavaScript #TypeScript