EditableProTable
EditableProTable reuses the ProTable column model, editing state machine, and validation logic for workflows where the whole table is one form field. Search, pagination, table options, and focus revalidation are disabled by default.
When to use
- Use it when users need to add, edit, or delete multiple records continuously and submit the whole table as one controlled value.
- Use it to reuse
ProTablecolumns, validation, and slots while disabling browsing-oriented features such as search and pagination by default. - For editing a single record in a workflow that is still primarily about querying and browsing, use the built-in editing capabilities of
ProTableinstead.
Examples
Controlled editing, record creation, and component instance
View full code
<script setup lang="ts">
import { ref } from 'vue'
import {
EditableProTable,
type EditableConfig,
type EditableProTableInstance,
type ProColumns,
type ProKey,
type RecordCreatorProps,
} from 'antdv-next-pro'
type Member = Record<string, unknown> & {
id: number
name: string
role: '开发' | '设计' | '测试'
allocation: number
active: boolean
}
const tableRef = ref<EditableProTableInstance<Member>>()
const value = ref<Member[]>([
{ id: 1, name: '林默', role: '开发', allocation: 80, active: true },
{ id: 2, name: '周芮', role: '设计', allocation: 60, active: true },
])
const editableKeys = ref<ProKey[]>([])
const lastAction = ref('editableKeys 由 v-model 完整控制')
let sequence = 3
const columns: ProColumns<Member>[] = [
{
title: '成员',
dataIndex: 'name',
valueType: 'text',
formItemProps: { rules: [{ required: true, message: '请输入成员姓名' }] },
},
{
title: '角色',
dataIndex: 'role',
valueType: 'select',
valueEnum: { 开发: '开发', 设计: '设计', 测试: '测试' },
},
{ title: '投入比例', dataIndex: 'allocation', valueType: 'percent' },
{ title: '参与项目', dataIndex: 'active', valueType: 'switch' },
{ title: '操作', valueType: 'option', width: 160 },
]
const editable: EditableConfig<Member> = {
type: 'multiple',
async onSave(_key, record) {
await new Promise((resolve) => setTimeout(resolve, 120))
lastAction.value = `已保存「${record.name}」`
},
}
const recordCreatorProps: RecordCreatorProps<Member> = {
record: () => ({
id: sequence++,
name: '',
role: '开发',
allocation: 50,
active: true,
}),
position: 'bottom',
creatorButtonText: '添加成员',
newRecordType: 'dataSource',
}
const editFirst = () => {
const first = value.value[0]
if (first && tableRef.value?.startEditable(first.id)) {
lastAction.value = '正在编辑第一行'
}
}
const patchFirst = () => {
const first = value.value[0]
if (first && tableRef.value?.setRowData(first.id, { allocation: 100 })) {
lastAction.value = 'setRowData 已浅合并第一行'
}
}
</script>
<template>
<div class="demo-frame vp-raw">
<p class="demo-label">LIVE · CONTROLLED VALUE + COMPONENT REF</p>
<div class="demo-actions">
<span>编辑中:{{ editableKeys.length ? editableKeys.join(', ') : '无' }}</span>
<span>{{ lastAction }}</span>
<button type="button" @click="editFirst">编辑第一行</button>
<button type="button" @click="patchFirst">投入改为 100%</button>
</div>
<EditableProTable
ref="tableRef"
v-model:value="value"
v-model:editable-keys="editableKeys"
:columns="columns"
:editable="editable"
:record-creator-props="recordCreatorProps"
:max-length="5"
row-key="id"
@values-change="lastAction = `完整 value 已更新,共 ${value.length} 行`"
@editable-error="lastAction = '保存失败,编辑状态已保留'"
>
<template #header-role="{ column }"> {{ column.title }}(共享列插槽) </template>
</EditableProTable>
</div>
</template>
<style scoped>
.demo-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
margin-bottom: 14px;
color: #64748b;
font-size: 12px;
}
.demo-actions button {
padding: 5px 10px;
border: 1px solid #bfd2e7;
border-radius: 6px;
background: #fff;
color: #1768d3;
cursor: pointer;
}
</style>Controlled data and edit state
v-model:value is the only controlled whole-table data entry point; a conflicting dataSource prop is not exposed. defaultValue supplies uncontrolled initial rows.
<EditableProTable
v-model:value="members"
v-model:editable-keys="editableKeys"
:columns="columns"
:editable="{ type: 'multiple', onSave, onDelete }"
:record-creator-props="recordCreatorProps"
row-key="id"
/>editable.type is single or multiple. onSave, onCancel, and onDelete may return Promises. Returning false from save or delete keeps the current edit/data state. Column-level editable can vary by record.
Validation and lifecycle
Place validation in column formItemProps.rules:
const columns: ProColumns<Member>[] = [
{
title: 'Name',
dataIndex: 'name',
valueType: 'text',
formItemProps: {
rules: [{ required: true, message: 'Enter a name' }],
},
},
]
const editable: EditableConfig<Member> = {
type: 'multiple',
async onSave(key, record, origin) {
await api.save(record)
},
async onDelete(key, record) {
await api.remove(record.id)
return true
},
}saveEditable returns false when validation fails. Lifecycle exceptions emit editable-error. The component-level formItemProps wraps the whole EditableProTable in an Antdv Next FormItem for outer-form integration.
Record creator
const recordCreatorProps: RecordCreatorProps<Member> = {
record: () => ({
id: crypto.randomUUID(),
name: '',
}),
position: 'bottom',
parentKey: undefined,
newRecordType: 'dataSource',
creatorButtonText: 'Add member',
}| Field | Description |
|---|---|
record | Record or factory called for each creation |
position | top / bottom; defaults to bottom |
parentKey | Parent for a new tree row |
newRecordType | dataSource writes immediately; cache writes after a successful save |
creatorButtonText | Creator button label |
Every new row must have a unique rowKey. maxLength counts the flattened tree, hides the button at the limit, and is also enforced by ref-based addEditRecord.
Shared field core and FormItem boundary
EditableProTable does not maintain a separate field map. It reuses the bare-control core from ProFormFields through ProTable. treeSelect, slider, and segmented work in editable columns like existing valueType values, while checked fields and async options share the same v-model, race, and error behavior.
Column formItemProps only provide cell validation and field configuration; they do not create a nested FormItem. Root-level formItemProps only wrap the complete EditableProTable in one outer FormItem. Custom editors still use column renderFormItem and write drafts through context.update(value).
API
Props
| Prop | Type | Description |
|---|---|---|
columns | ProColumns<T>[] | Shared ProTable column model |
value / defaultValue | T[] | Controlled value and uncontrolled initial value |
editableKeys | ProKey[] | Current editing keys; supports two-way binding |
editable | false | EditableConfig<T> | Editing mode and lifecycle |
recordCreatorProps | false | RecordCreatorProps<T> | Creator configuration |
maxLength | number | Maximum row count |
formItemProps | Record<string, unknown> | Outer Antdv Next FormItem props |
onValuesChange | (values, changedRecord) => void | Whole-table change callback |
onTableChange | (pagination, filters, sorter) => void | Table-state callback |
request / params / postData | Same as ProTable | Optional remote initialization/refresh |
toolbar / rowSelection / columnsState | Same as ProTable | Opt into related capabilities |
polling / manualRequest | Same as ProTable | Remote request controls |
scroll / size / bordered | Same as ProTable | Table presentation |
Events
onValuesChange / onTableChange props and @values-change / @table-change are two syntaxes for the same Vue listener channel. Choose one syntax; every change is dispatched exactly once, so do not bind the same handler through both forms:
| Event | Arguments | Description |
|---|---|---|
update:value | rows | v-model:value update |
update:editable-keys | keys | v-model:editable-keys update |
values-change | rows, changedRecord | Any row mutation |
table-change | pagination, filters, sorter | Table-state change |
request-error | error | Remote request failure |
editable-error | error | Editing lifecycle failure |
Slots
EditableProTable forwards every slot to its inner ProTable:
toolbar-titleandtoolbar-actions.header-${columnKey}.cell-${columnKey}or${columnKey}with{ value, record, index, column, editable }.- Other slots supported by the underlying Antdv Next Table.
Component instance
EditableProTableInstance<T> includes all ProTable methods and adds whole-table access:
const editableRef = ref<EditableProTableInstance<Member>>()
editableRef.value?.startEditable(memberId)
const first = editableRef.value?.getRowData(0)
const all = editableRef.value?.getRowsData()
editableRef.value?.setRowData(memberId, { allocation: 100 })| Method | Description |
|---|---|
getRowData(indexOrKey) | Numeric input resolves an exact row key first, then a top-level index |
getRowsData() | Read a copy of all current rows |
setRowData(indexOrKey, value) | Resolve with the same key/index rule, shallow-merge, and update v-model:value |
Inherited methods are reload, reset, setPageInfo, clearSelected, fullScreen, scrollTo, startEditable, saveEditable, cancelEditable, and addEditRecord.