QueryFormItem
A decorator component built on
QueryFormthat requests and updates the current field'sdataSourcebased on conditions.
SelectTable with Pagination Example
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': 'Keyword',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Search by name',
},
},
department: {
'type': 'string',
'title': 'Department',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
},
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one user before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one user',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Users',
required: true,
tooltip: 'Select at least one user from the table',
extra: 'Use the query area to filter the table before selecting.',
querySchema,
request,
paginationProps: {
pageSize: 8,
},
queryFormProps: {
submitText: 'Search',
resetText: 'Reset',
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>- 1
Tree with Light Mode and No Pagination Example
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Tree } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
interface TreeNode {
id: number
label: string
children?: TreeNode[]
}
const form = createForm()
const source: TreeNode[] = [
{
id: 1,
label: 'East Region',
children: [
{ id: 11, label: 'Shanghai' },
{ id: 12, label: 'Hangzhou' },
],
},
{
id: 2,
label: 'South Region',
children: [
{ id: 21, label: 'Shenzhen' },
{ id: 22, label: 'Guangzhou' },
],
},
]
function filterTree(nodes: TreeNode[], keyword: string): TreeNode[] {
if (!keyword)
return nodes
return nodes
.map((node) => {
const children = node.children ? filterTree(node.children, keyword) : undefined
if (node.label.includes(keyword) || (children && children.length > 0)) {
return { ...node, children }
}
return null
})
.filter(Boolean) as TreeNode[]
}
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': 'Keyword',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Filter node name',
},
},
},
}
async function request(values: Record<string, any>) {
await new Promise(resolve => setTimeout(resolve, 150))
const keyword = `${values.keyword ?? ''}`.trim()
const data = filterTree(source, keyword)
return {
data,
success: true,
total: data.length,
}
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one node before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedNodes: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one node',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Nodes',
required: true,
tooltip: 'Select at least one node from the tree',
extra: 'Light mode still supports FormItem label/required/feedback.',
querySchema,
request,
mode: 'light',
pagination: false,
queryFormProps: {
throttleWait: 200,
},
},
'x-component': 'Tree',
'x-component-props': {
nodeKey: 'id',
maxHeight: 260,
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
Tree,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>Custom Component Registration Example (Segmented)
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Segmented, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
department: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Segmented',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'default': '',
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('Please select at least one user before submit')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-validator': [
{
required: true,
message: 'Please select at least one user',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: 'Target Users',
required: true,
extra: '通过 queryFormProps.components 注册 Segmented 后,可在 querySchema 中直接使用。',
querySchema,
request,
mode: 'light',
paginationProps: {
pageSize: 8,
},
queryFormProps: {
throttleWait: 200,
components: {
Segmented,
},
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>- 1
External Form Initial Values Example
Note
If you need to pass form inside the decorator, use a function that returns the props object. That is because props inside decorators go through toJS, which would otherwise cause repeated component re-renders. See the example below for the exact pattern.
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, SelectTable } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
// External query form instance with initial query params.
const queryForm = createForm({
initialValues: {
keyword: 'User-1',
department: 'R&D',
},
})
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: 'Keyword',
},
},
department: {
'type': 'string',
'enum': [
{ label: 'All', value: '' },
{ label: 'R&D', value: 'R&D' },
{ label: 'Product', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
placeholder: 'Department',
style: 'width: 120px;',
},
},
},
}
async function handleSubmit() {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
const schema: ISchema = {
type: 'object',
properties: {
selectedUsers: {
'type': 'array',
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
mode: 'light',
label: '',
extra: 'Light mode query area uses an external form with initial values.',
request,
querySchema,
queryFormProps: {
form: () => queryForm,
throttleWait: 200,
},
},
'x-component': 'SelectTable',
'x-component-props': {
mode: 'multiple',
rowKey: 'id',
columns: [
{ prop: 'name', label: 'Name' },
{ prop: 'department', label: 'Department' },
],
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
SelectTable,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>- 1
Transfer with Clearing on Condition Changes Example
<script setup lang="ts">
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, Transfer } from '@silver-formily/element-plus'
import { createSchemaField, FormProvider } from '@silver-formily/vue'
import { ElButton, ElMessage } from 'element-plus'
import { createPermissionRequest } from './mock-user-request'
const form = createForm()
const request = createPermissionRequest()
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': '关键词',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: '按权限名称过滤',
},
},
module: {
'type': 'string',
'title': '模块',
'enum': [
{ label: '全部', value: '' },
{ label: '用户', value: 'user' },
{ label: '订单', value: 'order' },
{ label: '财务', value: 'finance' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
style: 'width: 130px;',
},
},
},
}
async function handleSubmit() {
try {
const values = await form.submit()
ElMessage.success(`Submit: ${JSON.stringify(values)}`)
}
catch {
ElMessage.error('请先选择至少一项权限')
}
}
const schema: ISchema = {
type: 'object',
properties: {
selectedPermissions: {
'type': 'array',
'x-validator': [
{
required: true,
message: '请选择至少一项权限',
},
],
'x-decorator': 'QueryFormItem',
'x-decorator-props': {
label: '',
required: true,
querySchema,
request,
pagination: false,
clearOnDataChange: true,
extra: '修改过滤条件并点击查询后,会自动清空已选择的数据。',
},
'x-component': 'Transfer',
'x-component-props': {
titles: ['可选权限', '已选权限'],
filterable: true,
},
},
},
}
const { SchemaField } = createSchemaField({
components: {
QueryFormItem,
Transfer,
},
})
</script>
<template>
<FormProvider :form="form">
<SchemaField :schema="schema" />
<ElButton type="primary" style="margin-top: 12px;" @click="handleSubmit">
Submit
</ElButton>
</FormProvider>
</template>Inject selected-list into extra with decorator-content Example
<script setup lang="ts">
import type { QueryFormItemSelectedListItem } from '@silver-formily/element-plus'
import type { ISchema } from '@silver-formily/json-schema'
import { createForm } from '@silver-formily/core'
import { QueryFormItem, QueryFormItemSelectedList, SelectTable } from '@silver-formily/element-plus'
import { Field, FormProvider } from '@silver-formily/vue'
import { defineComponent, h } from 'vue'
import { createUserRequest } from './mock-user-request'
const form = createForm()
const request = createUserRequest()
function getSelectedUserText(item: QueryFormItemSelectedListItem) {
return item.record?.name ?? String(item.value)
}
const SelectedListExtra = defineComponent({
name: 'SelectedListExtra',
setup() {
return () => h(QueryFormItemSelectedList, {
itemText: getSelectedUserText,
})
},
})
const querySchema: ISchema = {
type: 'object',
properties: {
keyword: {
'type': 'string',
'title': '关键词',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
clearable: true,
placeholder: '按名称搜索',
},
},
department: {
'type': 'string',
'title': '部门',
'enum': [
{ label: '全部', value: '' },
{ label: '研发', value: 'R&D' },
{ label: '产品', value: 'Product' },
],
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
clearable: true,
},
},
},
}
</script>
<template>
<FormProvider :form="form">
<Field
name="selectedUsers"
:decorator="[
QueryFormItem,
{
label: '目标用户',
querySchema,
request,
paginationProps: {
pageSize: 8,
},
queryFormProps: {
submitText: '查询',
resetText: '重置',
},
},
]"
:decorator-content="{
extra: SelectedListExtra,
}"
:component="[
SelectTable,
{
mode: 'multiple',
rowKey: 'id',
optionAsValue: true,
showAlertToolbar: false,
columns: [
{ prop: 'name', label: '名称' },
{ prop: 'department', label: '部门' },
],
},
]"
/>
</FormProvider>
</template>- 1
API
QueryFormItem Props
The component inherits most FormItem props. To avoid validation-error styles from breaking the internal QueryForm layout, it changes the FormItem class name, so a few FormItem-related props may not behave exactly the same. The following are QueryFormItem-specific props.
| Prop | Description | Type | Default |
|---|---|---|---|
mode | Query mode | enum | 'default' |
request | Query function. See Request Contract. | Function | - |
clearOnDataChange | Whether to clear the current field value after a successful query | boolean | false |
querySchema | Equivalent to queryFormProps.schema | object | - |
queryFormProps | Query form configuration | object | See QueryForm defaults |
pagination | Whether to enable pagination | boolean | true |
paginationProps | Pagination props forwarded to ElPagination | See Element Plus documentation | - |
paginationMap | Pagination key mapping used when building request params | object | Pagination Parameter Mapping |
immediate | Whether to run the query immediately after mount | boolean | true |
Events
| Prop | Type | Description | Default |
|---|---|---|---|
requestSuccess | Function | Triggered after a successful query. See QueryFormItemRequestSuccessPayload. | - |
requestFailed | Function | Triggered when the query fails | - |
QueryFormItemRequestSuccessPayload
requestSuccess is emitted after the request succeeds with result.success set to true. Its payload contains:
| Prop | Type | Description |
|---|---|---|
values | object | Query values collected by QueryForm, excluding pagination parameters |
pagination | object | undefined | Current page and page size; undefined when pagination is disabled |
dataSource | array | The value of result.data, also synchronized to the current field's dataSource |
total | number | undefined | Total item count; falls back to result.data.length when result.total is omitted |
result | object | Original value returned by request; its total is not replaced with the fallback shown above |
Request Contract
When pagination is enabled, the request function receives current and pageSize in addition to values collected from QueryForm. You can remap those keys through paginationMap.
request must return data in the following shape, similar to ProTable:
interface QueryResult {
data: any[]
success: boolean
total?: number
}successmust betruebeforedatais written into the fielddataSource.- When
totalis omitted,data.lengthis used by default. In paginated scenarios, returningtotalexplicitly is recommended.
Pagination Parameter Mapping
The default pagination keys are current and pageSize. If your backend expects different names, configure them through paginationMap:
const props = {
paginationMap: {
current: 'pageNum',
pageSize: 'pageSize',
},
}SelectedList API
QueryFormItemSelectedList displays the items selected by the current field. It is usually injected into the extra area of QueryFormItem through decoratorContent.extra. The component reads the current field value automatically and includes interactions for clearing all selections and removing a single selected item.
SelectedList Props
| Prop | Description | Type | Default |
|---|---|---|---|
itemText | Required. Returns the display text for each selected item | Function | - |
width | List width | number | 200 |
selectionText | Selected-count text. When a string is provided, {count} is replaced by the count | string | ((count: number) => string) | Generated from the current locale |
clearSelectionText | Text for the clear-all action | string | Generated from the current locale |
QueryFormItemSelectedListItem
itemText receives the following item shape:
| Prop | Description | Type |
|---|---|---|
value | Current item value. When a record can be resolved by rowKey, this is the record key value | any |
rawValue | Original item stored in the field value | any |
record | Full record matched from dataSource; when optionAsValue is enabled, this is the raw item | object | undefined |
index | Original index of the item in the field value array | number |