在 kintone 中,子表格(Subtable)常用於整理一筆記錄中需要「逐筆條列」的資料。每一列都像一份迷你 kintone 表單,包含獨立的欄位結構。
在進行開發或資料更新時,開發者最常遇到的困擾,就是子表格的資料結構較深、以及
REST API 更新方式和 event.record 的賦值機制不同,而導致更新失敗、列順序錯亂、資料消失等狀況。
本文將整合兩種更新方式的差異與注意事項,協助你在開發時避免踩雷。
kintone 記錄的 record.表格.value 為陣列,每一項代表一列,結構如下:
{
id: "55108", // 子表格列 ID(系統自動生成)
value: {
項目: { type: "SINGLE_LINE_TEXT", value: "項目一" },
數量: { type: "NUMBER", value: 10 },
... // 其他欄位
}
}
使用 /k/v1/record 搭配 PUT 更新子表格時,kintone 會依據傳入資料的「id 是否存在」,以及「陣列中的順序」來決定:
以下整理成開發時最重要的行為:
在 REST API 的行為模式中,每一列的子表格列 id 與其原 value 互相關聯。
表格列物件中,最少僅需帶子表格列 id,即可代表整列資料,並且可以透過子表格列 id 指定特定列來更新該列的欄位值。
REST API 會把「未出現在子 value 陣列中的列」視為刪除。
你不必重送整列資料,只要在該列的 value 中包含欲更新的欄位,以及該欄位的新值即可。
例如:
{ id: "55109", value: { 欄位代碼: { value: "新值" } } }
💡註:如果是要追加新的一列資料,則不需要帶入 id。
將新的資料列排入陣列中,請求成功後系統會自動賦予子表格列 id。
可以透過改變子表格物件在陣列中的排序,來改變表格列的順序。範例如下:

原本順序:
更新時將順序改為:
kintone.events.on('app.record.edit.submit.success', async event => {
const table = event.record['表格'].value
const row1 = table[0] // 項目一
const row2 = table[1] // 項目二
const row3 = table[2] // 項目三
// 重新排序為:項目三、項目一、項目二
const reorderedTable = [row3, row1, row2]
const url = kintone.api.url('/k/v1/record')
await kintone.api(url, 'PUT', {
app: kintone.app.getId(),
id: event.recordId,
record: {
表格: {
value: reorderedTable,
}
}
})
return event
})
更新後的畫面順序會依照你送出的陣列順序重新排列。

從上圖右方原始資料可以看出,子表格列的資料連動其對應的 id 一起改變順序了。
由於 REST API 更新時可以利用子表格列 id 來代表該列,因此以下寫法也能達成一樣的效果:
await kintone.api(url, 'PUT', {
app: kintone.app.getId(),
id: event.recordId,
record: {
表格: {
value: [
{ id: 55110 }, // 項目三
{ id: 55108 }, // 項目一
{ id: 55109 }, // 項目二
]
}
}
})
若只要修改某一列,但其他列仍至少要帶 id(避免被刪除)
await kintone.api(url, 'PUT', {
app: kintone.app.getId(),
id: event.recordId,
record: {
表格: {
value: [
{ id: 55108 }, // 保留項目一,不做任何更動
{
id: 55109, // 項目二,帶入欲修改的部分欄位資料
value: { 項目: { value: '項目二(修改)' } }
},
// ❌ 未帶 55110 → // 項目三會被刪除
]
}
}
})

與 REST API 不同,event.record 的值是「呈現畫面所必須有的完整資料」,所以修改時必須確保其完整性。
不論是要新增/修改表格列中的全部或是部分欄位值,都一定要包含表格列中的所有欄位資訊,包含子表格列物件中 id 、以及其 value 中應包含子表格列的所有欄位、欄位值(value)與欄位類型(type)。
即便你改變 table.value 的陣列順序:
row.value 的內容會換位🔸 這是最容易誤會、跟 REST API 最大的差異之一。
以下為範例:

原本順序:
程式碼在記錄編輯保存時,改變表格列資料在陣列中的順序:
kintone.events.on('app.record.edit.submit', event => {
const table = event.record['表格'].value
const row1 = table[0] // 項目一
const row2 = table[1] // 項目二
const row3 = table[2] // 項目三
// 重新排序為:項目三、項目一、項目二
const reorderedTable = [row3, row1, row2]
// 對子表格重新賦值
event.record['表格'].value = reorderedTable
return event
})
結果:

如果子表格列增加,則會按照順序再產生新的表格列 id;
如果子表格列減少,則會從最後一個列 id 進行刪除,value 向上遞補。
kintone.events.on('app.record.edit.submit', event => {
const table = event.record['表格'].value
const row1 = table[0] // 項目三
const row2 = table[1] // 項目一
const row3 = table[2] // 項目二
// 移除中間的項目一
const reorderedTable = [row1, row3]
// 對子表格重新賦值
event.record['表格'].value = reorderedTable
return event
})

從以上範例可以看出,原先子表格列的順序為:
從陣列中移除中間的項目一,最後的值並不會是:
而是:
kintone 的子表格結構本身就比較複雜,再加上 REST API 與 event.record 在更新子表格時有不同的規則與行為,如果沒有先弄清楚,很容易在開發過程中出現更新失敗、列被刪除或 id 與內容對不起來的狀況。
實務上,無論是透過 REST API 還是在事件中操作 event.record,都建議先取得原始的子表格資料陣列,找到目標列後只調整需要變更的欄位值,再帶入更新後的完整子表格資料。
這種做法可以降低誤刪列、搞錯結構或破壞順序的風險,是相對安全、也最適合在專案中採用的模式。