ArrayTable
Table component for incrementally managed rows. It is especially well suited to large data sets. After the refactor, ArrayTable performance improved significantly, and large tables no longer lag during input.
Note
This component is intended for Schema-based scenarios only, and it only supports arrays of objects.
Tip
To display validation feedback on pagination, this component includes a fork of the Element Plus pagination component from version 2.9.11. Unless there is a special reason, that built-in pagination component will not be updated further, so its props should be treated as matching that version. Internal functions are still imported from peerDependencies, so localization continues to inherit from Element Plus. If you need to change the default language, wrap it with ElConfigProvider.
One known limitation remains: validation does not run for rows that have not been rendered yet. Formily only validates registered Fields, so unregistered Fields cannot participate in validation. Registering 100,000 Fields up front would cause severe page lag. In practice, the best workaround is to avoid examples or features that insert a huge amount of data all at once. If data is always added through the "add item" button, every Field will be registered correctly.
Markup Schema Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElAlert, ElButton } from 'element-plus'
const form = createForm()
const {
SchemaField,
SchemaArrayField,
SchemaObjectField,
SchemaVoidField,
SchemaStringField,
} = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Space,
},
})
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
function range(count) {
return Array.from({ length: count }).map(_ => ({ a1: null, a2: null, a3: null }))
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaArrayField
name="array"
x-decorator="FormItem"
x-component="ArrayTable"
:x-component-props="{
stripe: true,
paginationProps: { pageSize: 10 },
}"
>
<SchemaObjectField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ width: 80, title: 'Index' }"
>
<SchemaVoidField
x-decorator="FormItem"
x-component="ArrayTable.Index"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ prop: 'a1', title: 'A1', width: 200 }"
>
<SchemaStringField
x-decorator="Editable"
name="a1"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ title: 'A2', width: 200 }"
>
<SchemaStringField
x-decorator="FormItem"
:x-decorator-props="{
feedbackLayout: 'popover',
}"
name="a2"
:required="true"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ title: 'A3' }"
>
<SchemaStringField
name="a3"
:required="true"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
}"
>
<SchemaVoidField x-component="FormItem">
<SchemaVoidField x-component="Space" :x-component-props="{ style: 'height: 100%' }">
<SchemaVoidField x-component="ArrayTable.Remove" />
<SchemaVoidField x-component="ArrayTable.MoveUp" />
<SchemaVoidField x-component="ArrayTable.MoveDown" />
</SchemaVoidField>
</SchemaVoidField>
</SchemaVoidField>
</SchemaObjectField>
<SchemaVoidField x-component="ArrayTable.Addition" :x-component-props="{ defaultValue: { a1: null, a2: '', a3: '' } }" title="Add Item" />
</SchemaArrayField>
</SchemaField>
<Submit @submit="log">
Submit
</Submit>
<ElButton
@click="
() => {
form.setInitialValues({
array: range(100000),
})
}
"
>
Load 100k Rows of Large Data
</ElButton>
<ElAlert
:style="{ marginTop: '10px' }"
title="Note: Pages with the Formily plugin enabled communicate with the backend and may consume more browser resources. Testing in incognito mode without the plugin is recommended."
type="warning"
/>
</FormProvider>
</template>Markup Schema with Pagination Disabled Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Select,
Space,
Submit,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const form = createForm()
const {
SchemaField,
SchemaArrayField,
SchemaObjectField,
SchemaVoidField,
SchemaStringField,
} = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Select,
Editable,
Space,
},
})
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaArrayField
name="array"
x-decorator="FormItem"
x-component="ArrayTable"
:x-component-props="{
pagination: false,
height: 300,
}"
>
<SchemaObjectField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ width: 80, title: 'Index' }"
>
<SchemaVoidField
x-decorator="FormItem"
x-component="ArrayTable.Index"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ prop: 'a1', title: 'A1' }"
>
<SchemaStringField
x-decorator="Editable"
name="a1"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ prop: 'a2', title: 'A2' }"
>
<SchemaStringField
x-decorator="Editable"
name="a2"
x-component="Select"
:x-decorator-props="{
editProps: {
style: { width: '300px' },
},
}"
:enum="[
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
]"
default="option1"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
}"
>
<SchemaVoidField x-component="FormItem">
<SchemaVoidField x-component="Space" :x-component-props="{ style: 'height: 100%' }">
<SchemaVoidField x-component="ArrayTable.Remove" />
<SchemaVoidField x-component="ArrayTable.MoveUp" />
<SchemaVoidField x-component="ArrayTable.MoveDown" />
</SchemaVoidField>
</SchemaVoidField>
</SchemaVoidField>
</SchemaObjectField>
<SchemaVoidField x-component="ArrayTable.Addition" :x-component-props="{ defaultValue: { a1: null, a2: '', a3: '' } }" title="Add Item" />
</SchemaArrayField>
</SchemaField>
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>Markup Schema Draggable Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
function range(count) {
return Array.from({ length: count }).map((_, index) => ({ a1: index }))
}
const form = createForm({
initialValues: {
array: range(30),
},
})
const {
SchemaField,
SchemaArrayField,
SchemaObjectField,
SchemaVoidField,
SchemaStringField,
} = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Space,
},
})
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaArrayField
name="array"
x-decorator="FormItem"
x-component="ArrayTable"
:x-component-props="{
stripe: true,
}"
>
<SchemaObjectField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ width: 60 }"
>
<SchemaVoidField
x-decorator="FormItem"
x-component="ArrayTable.SortHandle"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ width: 80, title: 'Index' }"
>
<SchemaVoidField
x-decorator="FormItem"
x-component="ArrayTable.Index"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{ prop: 'a1', title: 'A1' }"
>
<SchemaStringField
x-decorator="Editable"
name="a1"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
:x-component-props="{
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
}"
>
<SchemaVoidField x-component="FormItem">
<SchemaVoidField x-component="Space" :x-component-props="{ style: 'height: 100%' }">
<SchemaVoidField x-component="ArrayTable.Remove" />
<SchemaVoidField x-component="ArrayTable.MoveUp" />
<SchemaVoidField x-component="ArrayTable.MoveDown" />
</SchemaVoidField>
</SchemaVoidField>
</SchemaVoidField>
</SchemaObjectField>
<SchemaVoidField x-component="ArrayTable.Addition" :x-component-props="{ defaultValue: { a1: null, a2: '', a3: '' } }" title="Add Item" />
</SchemaArrayField>
</SchemaField>
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>JSON Schema Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const { SchemaField } = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Space,
},
})
const form = createForm()
const schema = {
type: 'object',
properties: {
array: {
'type': 'array',
'x-decorator': 'FormItem',
'x-component': 'ArrayTable',
'items': {
type: 'object',
properties: {
column1: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
width: 80,
title: 'Index',
align: 'center',
},
'properties': {
index: {
'type': 'void',
'x-component': 'ArrayTable.Index',
},
},
},
column2: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { width: 200, title: 'A1' },
'properties': {
a1: {
'type': 'string',
'x-decorator': 'Editable',
'x-component': 'Input',
},
},
},
column3: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { width: 200, title: 'A2' },
'properties': {
a2: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column4: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: 'A3' },
'properties': {
a3: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column5: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
},
'properties': {
item: {
'type': 'void',
'x-component': 'FormItem',
'properties': {
space: {
'type': 'void',
'x-component': 'Space',
'x-component-props': {
style: 'height: 100%',
},
'properties': {
remove: {
'type': 'void',
'x-component': 'ArrayTable.Remove',
},
moveDown: {
'type': 'void',
'x-component': 'ArrayTable.MoveDown',
},
moveUp: {
'type': 'void',
'x-component': 'ArrayTable.MoveUp',
},
},
},
},
},
},
},
},
},
'properties': {
add: {
'type': 'void',
'x-component': 'ArrayTable.Addition',
'title': 'Add Item',
},
},
},
},
}
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>JSON Schema Pagination Configuration Example
<script lang="ts" setup>
import { createForm } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const { SchemaField } = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Space,
},
})
const form = createForm()
const schema = {
type: 'object',
properties: {
array: {
'type': 'array',
'x-decorator': 'FormItem',
'x-component': 'ArrayTable',
'x-component-props': {
paginationProps: {
pageSize: 2,
pageSizes: [2, 4, 6, 8, 10],
background: false,
size: 'small',
},
},
'items': {
type: 'object',
properties: {
column1: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
width: 80,
title: 'Index',
align: 'center',
},
'properties': {
index: {
'type': 'void',
'x-component': 'ArrayTable.Index',
},
},
},
column2: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: 'A1' },
'properties': {
a1: {
'type': 'string',
'x-decorator': 'Editable',
'x-component': 'Input',
},
},
},
column3: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
},
'properties': {
item: {
'type': 'void',
'x-component': 'FormItem',
'properties': {
space: {
'type': 'void',
'x-component': 'Space',
'x-component-props': {
style: 'height: 100%',
},
'properties': {
remove: {
'type': 'void',
'x-component': 'ArrayTable.Remove',
},
moveDown: {
'type': 'void',
'x-component': 'ArrayTable.MoveDown',
},
moveUp: {
'type': 'void',
'x-component': 'ArrayTable.MoveUp',
},
},
},
},
},
},
},
},
},
'properties': {
add: {
'type': 'void',
'x-component': 'ArrayTable.Addition',
'title': 'Add Item',
},
},
},
},
}
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>Effects Linkage Example
<script lang="ts" setup>
import { createForm, isField, onFieldChange, onFieldReact } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
Switch,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const {
SchemaField,
SchemaArrayField,
SchemaObjectField,
SchemaVoidField,
SchemaStringField,
SchemaBooleanField,
} = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Switch,
Space,
},
})
const form = createForm({
effects: () => {
// Active linkage mode
onFieldChange('hideFirstColumn', ['value'], (field) => {
field.query('array.column3').take((target) => {
if (isField(field)) {
target.visible = !field.value
}
})
field.query('array.*.a2').take((target) => {
if (isField(field)) {
target.visible = !field.value
}
})
})
// Passive linkage mode
onFieldReact('array.*.a2', (field) => {
field.visible = !field.query('.a1').get('value')
})
},
})
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaBooleanField
name="hideFirstColumn"
x-decorator="FormItem"
x-component="Switch"
title="Hide A2"
/>
<SchemaArrayField
name="array"
x-decorator="FormItem"
x-component="ArrayTable"
>
<SchemaObjectField>
<SchemaVoidField
name="column1"
x-component="ArrayTable.Column"
:x-component-props="{ width: 80, title: 'Index' }"
>
<SchemaVoidField x-component="ArrayTable.Index" />
</SchemaVoidField>
<SchemaVoidField
name="column2"
x-component="ArrayTable.Column"
:x-component-props="{
title: 'Visibility -> A2',
width: 100,
}"
>
<SchemaBooleanField
name="a1"
x-decorator="FormItem"
x-component="Switch"
/>
</SchemaVoidField>
<SchemaVoidField
x-component="ArrayTable.Column"
name="column3"
:x-component-props="{ title: 'A2', width: 200 }"
>
<SchemaStringField
x-decorator="FormItem"
name="a2"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
name="column4"
x-component="ArrayTable.Column"
:x-component-props="{ title: 'A3' }"
>
<SchemaStringField
name="a3"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaVoidField>
<SchemaVoidField
name="column5"
x-component="ArrayTable.Column"
:x-component-props="{
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
}"
>
<SchemaVoidField x-component="FormItem">
<SchemaVoidField x-component="Space" :x-component-props="{ style: 'height: 100%' }">
<SchemaVoidField x-component="ArrayTable.Remove" />
<SchemaVoidField x-component="ArrayTable.MoveUp" />
<SchemaVoidField x-component="ArrayTable.MoveDown" />
</SchemaVoidField>
</SchemaVoidField>
</SchemaVoidField>
</SchemaObjectField>
<SchemaVoidField x-component="ArrayTable.Addition" title="Add Item" />
</SchemaArrayField>
</SchemaField>
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>JSON Schema Linkage Example
<script lang="ts" setup>
import { createForm, isField, onFieldChange, onFieldReact } from '@silver-formily/core'
import {
ArrayTable,
Editable,
FormItem,
Input,
Space,
Submit,
Switch,
} from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
const { SchemaField } = createSchemaField({
components: {
FormItem,
ArrayTable,
Input,
Editable,
Switch,
Space,
},
})
const form = createForm({
effects: () => {
// Active linkage mode
onFieldChange('hideFirstColumn', ['value'], (field) => {
field.query('array.column3').take((target) => {
if (isField(field)) {
target.visible = !field.value
}
})
field.query('array.*.a2').take((target) => {
if (isField(field)) {
target.visible = !field.value
}
})
})
// Passive linkage mode
onFieldReact('array.*.a2', (field) => {
field.visible = !field.query('.a1').get('value')
})
},
})
const schema = {
type: 'object',
properties: {
hideFirstColumn: {
'type': 'boolean',
'title': 'Hide A2',
'x-decorator': 'FormItem',
'x-component': 'Switch',
},
array: {
'type': 'array',
'x-decorator': 'FormItem',
'x-component': 'ArrayTable',
'items': {
type: 'object',
properties: {
column1: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
width: 80,
title: 'Index',
align: 'center',
},
'properties': {
index: {
'type': 'void',
'x-component': 'ArrayTable.Index',
},
},
},
column2: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { width: 100, title: 'Visibility -> A2' },
'properties': {
a1: {
'type': 'boolean',
'x-decorator': 'FormItem',
'x-component': 'Switch',
},
},
},
column3: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { width: 200, title: 'A2' },
'properties': {
a2: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column4: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': { title: 'A3' },
'properties': {
a3: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
column5: {
'type': 'void',
'x-component': 'ArrayTable.Column',
'x-component-props': {
title: 'Operations',
prop: 'operations',
width: 200,
fixed: 'right',
},
'properties': {
item: {
'type': 'void',
'x-component': 'FormItem',
'properties': {
space: {
'type': 'void',
'x-component': 'Space',
'x-component-props': {
style: 'height: 100%',
},
'properties': {
remove: {
'type': 'void',
'x-component': 'ArrayTable.Remove',
},
moveDown: {
'type': 'void',
'x-component': 'ArrayTable.MoveDown',
},
moveUp: {
'type': 'void',
'x-component': 'ArrayTable.MoveUp',
},
},
},
},
},
},
},
},
},
'properties': {
add: {
'type': 'void',
'x-component': 'ArrayTable.Addition',
'title': 'Add Item',
},
},
},
},
}
async function log(...v: Record<string, any>[]) {
console.log(...v)
}
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<Submit @submit="log">
Submit
</Submit>
</FormProvider>
</template>API
ArrayTable
Table component
See https://element-plus.org/en-US/component/table.html
Extended Props
| Prop | Type | Description | Default |
|---|---|---|---|
pagination | boolean | Whether to enable pagination | true |
paginationProps | object | Pagination component props | { background: true, layout: "total, sizes, prev, pager, next" } |
ArrayTable.Column
Table column
See https://element-plus.org/en-US/component/table.html
Extended Props
| Prop | Type | Description | Default |
|---|---|---|---|
asterisk | boolean | Whether to show an asterisk | true |
Tip
- ArrayTableColumn automatically checks whether its internal FormItem is required and adds a red asterisk to the table header. If you do not want the asterisk, override it with the
asteriskprop. - ArrayTableColumn only forwards props. It does not support slots.
ArrayTable.SortHandle
See ArrayBase.SortHandle
ArrayTable.Addition
See ArrayBase.Addition
ArrayTable.Remove
See ArrayBase.Remove
ArrayTable.MoveDown
ArrayTable.MoveUp
See ArrayBase.MoveUp
ArrayTable.Index
See ArrayBase.Index