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>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 | 'default' | 'light' | 'default' |
request | Query function | Request Contract | - |
clearOnDataChange | Whether to clear the current field value after a successful query | boolean | false |
querySchema | Equivalent to queryFormProps.schema | ISchema | - |
queryFormProps | Query form configuration | QueryFormItemQueryProps | 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 | QueryFormItemPaginationMap | Pagination Parameter Mapping |
immediate | Whether to run the query immediately after mount | boolean | true |
Events
| Prop | Type | Description | Default |
|---|---|---|---|
requestSuccess | (payload: QueryFormItemRequestSuccessPayload) => void | Triggered after a successful query | - |
requestFailed | (error: any) => void | Triggered when the query fails | - |
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',
},
}