テーブル操作の基本
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" }
}
}
]
}
}
構造のポイント
- テーブルフィールド自体:
typeがSUBTABLE、valueは行の配列 - 各行:
id(行の識別子)とvalue(その行のフィールド群)を持つオブジェクト - 行内のフィールド: 通常のフィールドと同じ構造
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 プロパティは省略可能です。省略した場合、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;
});
})();
map や filter を使う場合は、必ず結果をテーブルの 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['テーブル'].value | kintone.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() |