RecursionField
描述
递归渲染组件,主要基于 JSON Schema 做递归渲染,它是 SchemaField 组件内部的核心渲染组件,当然,它是可以独立于 SchemaField 单独使用的,我们使用的时候主要是在自定义组件中使用,用于实现具有递归渲染能力的自定义组件
Schema 协议说明
本页只说明 @silver-formily/vue 如何递归消费 Schema。Schema、ISchema、属性协议和联动协议的完整定义请查看 JSON Schema 重建文档站。
简易递归
可以从组件属性中读取独立的 schema 对象,传给 RecursionField 渲染
<script setup>
import { createForm } from '@formily/core'
import { createSchemaField, FormProvider, RecursionField } from '@silver-formily/vue'
import { ElInput } from 'element-plus'
import { defineComponent, h } from 'vue'
const Custom = defineComponent({
name: 'Custom',
props: {
name: String,
schema: Object,
},
setup(props) {
return () =>
h(RecursionField, {
name: props.name,
schema: props.schema,
onlyRenderProperties: true,
})
},
})
const { SchemaField, SchemaObjectField } = createSchemaField({
components: {
Custom,
ElInput,
},
})
const form = createForm()
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaObjectField
name="custom"
x-component="Custom"
:x-component-props="{
schema: {
type: 'object',
properties: {
input: {
'type': 'string',
'x-component': 'ElInput',
},
},
},
}"
/>
</SchemaField>
</FormProvider>
</template><script setup>
import { createForm } from '@formily/core'
import { createSchemaField, FormProvider, RecursionField } from '@silver-formily/vue'
import { ElInput } from 'element-plus'
import { defineComponent, h } from 'vue'
const Custom = defineComponent({
name: 'Custom',
props: {
name: String,
schema: Object,
},
setup(props) {
return () =>
h(RecursionField, {
name: props.name,
schema: props.schema,
onlyRenderProperties: true,
})
},
})
const { SchemaField, SchemaObjectField } = createSchemaField({
components: {
Custom,
ElInput,
},
})
const form = createForm()
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaObjectField
name="custom"
x-component="Custom"
:x-component-props="{
schema: {
type: 'object',
properties: {
input: {
'type': 'string',
'x-component': 'ElInput',
},
},
},
}"
/>
</SchemaField>
</FormProvider>
</template>
查看源码
自增列表递归
使用useField和useFieldSchema来获取当前字段上下文中的字段实例和字段 schema
<script setup lang="tsx">
import { createForm, isArrayField } from '@formily/core'
import { observer } from '@silver-formily/reactive-vue'
import {
createSchemaField,
FormProvider,
RecursionField,
useField,
useFieldSchema,
} from '@silver-formily/vue'
import { ElButton, ElInput, ElSpace } from 'element-plus'
import { defineComponent } from 'vue'
const ArrayItems = observer(
defineComponent({
name: 'ArrayItems',
setup() {
const fieldRef = useField()
const schemaRef = useFieldSchema()
function handleAdd() {
if (isArrayField(fieldRef.value)) {
fieldRef.value.value?.push({ id: Date.now() })
}
}
return () => {
const field = fieldRef.value
const schema = schemaRef.value
const itemSchema = schema?.items
const normalizedItemSchema = Array.isArray(itemSchema) ? itemSchema[0] : itemSchema
const items = isArrayField(field) && Array.isArray(field.value)
? field.value.map((item, index) => (
<div key={item.id ?? index} style={{ marginBottom: '10px' }}>
<ElSpace>
<RecursionField schema={normalizedItemSchema} name={index} />
<ElButton onClick={() => field?.remove(index)}>
Remove
</ElButton>
</ElSpace>
</div>
))
: null
return (
<div>
{items}
<ElButton onClick={handleAdd}>Add</ElButton>
</div>
)
}
},
}),
)
const { SchemaField, SchemaStringField, SchemaArrayField, SchemaObjectField }
= createSchemaField({
components: {
ArrayItems,
ElInput,
},
})
const form = createForm()
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaArrayField name="custom" x-component="ArrayItems">
<SchemaObjectField>
<SchemaStringField name="input" x-component="ElInput" />
</SchemaObjectField>
</SchemaArrayField>
</SchemaField>
</FormProvider>
</template><script setup lang="tsx">
import { createForm, isArrayField } from '@formily/core'
import { observer } from '@silver-formily/reactive-vue'
import {
createSchemaField,
FormProvider,
RecursionField,
useField,
useFieldSchema,
} from '@silver-formily/vue'
import { ElButton, ElInput, ElSpace } from 'element-plus'
import { defineComponent } from 'vue'
const ArrayItems = observer(
defineComponent({
name: 'ArrayItems',
setup() {
const fieldRef = useField()
const schemaRef = useFieldSchema()
function handleAdd() {
if (isArrayField(fieldRef.value)) {
fieldRef.value.value?.push({ id: Date.now() })
}
}
return () => {
const field = fieldRef.value
const schema = schemaRef.value
const itemSchema = schema?.items
const normalizedItemSchema = Array.isArray(itemSchema) ? itemSchema[0] : itemSchema
const items = isArrayField(field) && Array.isArray(field.value)
? field.value.map((item, index) => (
<div key={item.id ?? index} style={{ marginBottom: '10px' }}>
<ElSpace>
<RecursionField schema={normalizedItemSchema} name={index} />
<ElButton onClick={() => field?.remove(index)}>
Remove
</ElButton>
</ElSpace>
</div>
))
: null
return (
<div>
{items}
<ElButton onClick={handleAdd}>Add</ElButton>
</div>
)
}
},
}),
)
const { SchemaField, SchemaStringField, SchemaArrayField, SchemaObjectField }
= createSchemaField({
components: {
ArrayItems,
ElInput,
},
})
const form = createForm()
</script>
<template>
<FormProvider :form="form">
<SchemaField>
<SchemaArrayField name="custom" x-component="ArrayItems">
<SchemaObjectField>
<SchemaStringField name="input" x-component="ElInput" />
</SchemaObjectField>
</SchemaArrayField>
</SchemaField>
</FormProvider>
</template>
查看源码
API
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| schema | 要渲染的 Schema 对象 | ISchema | — |
| name | 渲染时挂载的字段名称,常配合 basePath 推导路径 | string | schema.name |
| basePath | 计算 name 的基路径 | FormPathPattern | 当前字段路径 |
| onlyRenderProperties | 仅渲染子节点 properties,不渲染当前节点 | boolean | false |
| onlyRenderSelf | 仅渲染当前节点,不自动递归子节点 | boolean | false |
| mapProperties | 属性映射函数,可在渲染前改写 schema | Function | — |
| filterProperties | 属性过滤函数,返回 false 的节点不会渲染 | Function | — |
FormPathPattern
ts
type FormPathPattern = string | number | Array<string | number> | RegExp