一、引言
系统性学习formily,包含Scheme、校验、赋值、布局、自定义表单项、联动等,本文重点聚焦schema模式的api介绍
二、formily结构
2.1 初始化模版
formily schema模式下模版如下代码
import { createForm, onFieldChange, onFieldValueChange } from '@formily/core';
import { FormProvider, createSchemaField } from '@formily/react';
import { FormItem, Input } from "antd";
const Test = () => {
//1. 创建form实例
const form = createForm({})
//2. 创建shchema组件
const SchemaField = createSchemaField({
components:{
FormItem,
Input,
Select,
Checkbox
}
})
//3. 创建schema
const schema = {
type:'object'
properties:{
name:{
type:'string',
title:'姓名',
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
}
}
return (
<FormProvider form={form}>
<SchemaField schema={schema} />
</FormProvider>
)
}
2.2 表单结构
表单结构分为简单布局与嵌套布局
2.2.1 简单结构
顶层type表示容器节点,properties对象告诉formily其内部用于定义具体表单字段。
{
type:'object',
properties:{
name:{
type:'string',
title:'姓名',
'x-decorator':'FormItem',
'x-component':'Input',
},
age:{
type:'number',
title:'年龄',
'x-decorator':'FormItem',
'x-component':'InputNumber',
}
}
}
2.2.1 嵌套结构
formily的schema支持嵌套结构,实现思路十分简单,在properties内部继续递归的定义新的表单项组成
例如想实现如下结构表单,姓名表单与下面容器平级,容器内部又有两个表单项。只需在第一层的properties内部继续嵌套新的表单结构即可。
const schema1 = {
type:'object',
properties:{
name:{
type:'string',
title:'姓名',
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
part1:{
type:'void',
'x-component':'Part1', //自定义组件容器,承载新表单项
properties:{
part11:{
type:'string',
title:'part11',
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
part12:{
type:'string',
title:'part12',
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
},
}
},
}
//自定义容器组件
const Part1 = (props:any) => {
return (
<>
<div>第一部分</div>
<div style={{padding: '10px 0 10px 30px'}}>{props.children}</div>
</>
)
}
三、核心API
3.1 submit
submit方法可以同时实现两个功能校验+表单值获取。
import { createForm, onFieldChange, onFieldValueChange } from '@formily/core';
import { FormProvider, createSchemaField } from '@formily/react';
import { FormItem, Input } from "antd";
const Test = () => {
//1. 创建form实例
const form = createForm({})
//2. 创建shchema组件
const SchemaField = createSchemaField({
components:{
FormItem,
Input,
Select,
Checkbox
}
})
//3. 创建schema
const schema = {
type:'object'
properties:{
name:{
type:'string',
title:'姓名',
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
}
}
// 表单提交
const onSubmit = async()=> {
try{
const res = await form.submit()
}catch(e){
throw new Error('校验失败')
}
}
return (
<FormProvider form={form}>
<SchemaField schema={schema} />
<div>
<Button onClick={onSubmit}>提交</Button>
</div>
</FormProvider>
)
}
3.2 reset
reset实现表单项的重置功能
// 表单重置
const onReset = ()=> {
form.reset();
}
return (
<FormProvider form={form}>
<SchemaField schema={schema} />
<div>
<Button onClick={onReset}>重置</Button>
</div>
</FormProvider>
)
}
3.3 setValues
setValues是给表单项赋值使用,通常用于详情、编辑页中批量的给多个字段赋值。特别注意,表单项赋值时路径为void字段的需要忽略
案例1:批量给多个表单项赋值
useEffect(()=>{
form.setValuse({name:'dzp',age:20})
},[form])
/*schema结构*/
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
placeholder: '请输入姓名',
},
},
age: {
type: 'number',
title: '年龄',
required: true,
'x-decorator': 'FormItem',
'x-component': 'InputNumber',
},
},
}
案例2:嵌套表单项赋值
如下是一段schema表单项,既有嵌套,又有void类型字段,牢记赋值时void类型的字段直接忽略。
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
placeholder: '请输入姓名',
},
},
info1:{
type:"void",
properties:{
name1:{
type:"string",
title:"姓名1",
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
info2:{
type:'object',
properties:{
name2:{
type:"string",
title:"姓名2",
required: true,
'x-decorator':'FormItem',
'x-component':'Input',
},
},
}
}
},
email: {
type: 'string',
title: '邮箱',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
placeholder: '请输入邮箱',
},
},
},
}
根据规律,给表单项赋值时从根节点依次遍历,遇到类型是void的直接忽略,遇到object类型的视作对象。最终赋值结果如下:
form.setValues({
name:'name1',
name1:'name11',
info2:{
name2:'name2'
},
email:'test'
})
3.4 useForm
useForm可以获取form实例,通常用于自定义组件中获取表单实例对象。
import {useForm} from "@formily/react"
const Container = (props) => {
const form = useForm()
const onSubmit = () => {
if(form){
form.submit()
}
}
return (
<div>
{props?.children}
<div onClick={onSubmit}>提交</div>
</div>
)
}
3.5 setFormState
设置表单的状态,禁用态、只读态等;常用在根据页面类型统一设置表单状态案例中。
特殊:setFormState设置的表单状态优先级低于schema表单项状态设置
- API使用
form.setFormState({disabled:true})//查看页设置页面禁止
2. setFormState与schema同时设置了状态
如下案例展示通过setFormState设置所有表单项状态是禁止,但是name1内部设置非禁止态,因此name1是非禁止,name2是禁止。
form.setFormState({disabled:true}
const schema = {
type:'object',
properties:{
name1:{
type:'string',
component:'Input',
'x-disabled':false
},
name2:{
type:'string',
component:'Input',
}
}
}
四、schema核心参数
schema结构由如下几部分组成
- type:类型,例如object、string、number
- properties:定义表单存在哪些表单元素,例如如下代码properties内部定义了一个Input和InputNumber表单项
- x-decorator:定义表单项
- x-decorator-props:修饰整个表单项FormItem内容、样式等
- x-component:定义组件类型
- x-component-props:定义组件参数
- x-reactions:处理联动、state、schema更新等复杂逻辑
- x-disabled:处理禁止项
- x-display:显示隐藏
- x-validator:校验
- x-reactions:联动等复杂场景
4.1 x-decorator-props
x-decorator-props用于修饰表单项的样式、标签、内容等。表单项=标签+表单,因此不要混淆表单项与表单的概念。其常见属性有
- label:字符串或自定义组件
- labelCol:标签宽度
- labelAlign:left | right 标签对齐方式
- tooltip:标签旁的提示信息
- style:样式
- className:类名
const schema = {
type:'object',
properties: {
name: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-decorator-props':{
style:{
color:'red',
},
className:'custom-form-item',
label:'姓名',
labelAlign:'left',
}
},
},
}
4.2 x-component-props
x-component-props用于修饰表单的属性,常见属性参数有如下
- disabled
- placeholder
- style
- className
const schema = {
type:'object',
properties: {
name: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props':{
disabled:true,
placeholder:'输入内容',
style:{},
className:'xx'
}
},
},
}
4.3 x-disabled
x-disabled用于控制表单项的禁用,其参数是boolean值
const schema = {
type:'object',
properties: {
name: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-disabled':false
},
},
}
4.4 x-validator
x-validator用于表单项的校验,其参数是数组表示可以添加多个校验规则。校验规则分为两种,一种是必填校验,另一种是自定义校验,其中自定义校验函数的参数如下
validator:(value,rule,ctx)=>{}
参数1:表单项的值
参数2:规则
参数3:表单实例,其中ctx.form.values可以获取表单所有的值
email: {
type: 'string',
title: '邮箱',
'x-decorator': 'FormItem',
'x-component': 'Input',
x-validator':[
{
required:true,
message:'邮箱必须输入'
},
{
validator: (value,rules,ctx) {
if(!value) return '不能是空'
if(!ctx.form.values.password) {
return '密码不能是空'
}
return true
}
},
],
},
4.5 default
default为表单项设置默认值
const schema = {
type:'object',
properties: {
name: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'default':'test'
},
},
}
4.6 x-display
x-display控制表单项的展示隐藏,其取值有三种
- none:删除表单项,表单没有当前表单项
- hidden:隐藏表单项,表单项仍然存在在表单中
- visible:显示表单项,默认即显示
const schema = {
type:'object',
properties: {
name: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-display':'none'
},
},
}
4.7 x-reactions
x-reactions是formily高级api,可以实现表单联动、表单状态更新、schema更新等复杂场景。
4.7.1 基本结构
x-reactions可以传对象,也可以传数组对象,对象结构由以下4个模块构成。
- dependencies 依赖的表单项字段
- when 判断条件
- fulfill:条件成立执行其内部操作
- otherwise:条件不成立时,执行其内部操作
'x-reactions':[
{
dependencies:['.name'],
when:'{{$deps[0] === "dzp"}}',
fulfill:{
state:{},//表单项属性
schema:{},//表单项schema
run:'{{console.log("run1", $deps[0])}}'//函数执行
},
otherwise:{
state:{},
schema:{},
run:'{{console.log("run2", $deps[0])}}'
},
}
]
4.7.2 run执行
run的执行有两种情况,一种是没有when,一种是有when。
- 没有when:初始就执行、且依赖性发生变化就执行
初始页面加载执行run函数,表单项name每次输入变更都会执行run函数
'x-reactions':[
{
dependencies:['.name'],
fulfill:{
run:'{{console.log("run1", $deps[0])}}'//函数执行
}
}
]
- 有when:仅当when条件成立才执行run
表单项name的值输入是dzp时,才执行了run函数
'x-reactions':[
{
dependencies:['.name'],
when:'{{$deps[0] === "dzp"}}',
fulfill:{
run:'{{console.log("run1", $deps[0])}}'//函数执行
}
}
]
4.7.3 fulfill
fulfill内部的state和schema包含许多内容,下面列举常见的
schema
- 'x-component'
- default
- 'x-disabled'
state
- value
- disabled
- display
- componentProps
如下案例实现功能是:当姓名表单项输入dzp时,年龄表单项值填充123,且禁用,且标签更改成姓名2
const schema = {
type:'object',
properties:{
name:{
type:'string',
title:'姓名',
'x-decorator':'FormItem',
'x-component':'Input',
},
age:{
type:'string',
title:'年龄',
'x-decorator':'FormItem',
'x-component':'Input',
'x-reactions':[
{
dependencies:['.name'],
when:'{{$deps[0] === "dzp"}}',
fulfill:{
state:{
value:123,
},
schema:{
'x-disabled':true,
'x-decorator-props':{
label:'姓名2',
},
},
},
},
],
},
},
}
4.7.4 特殊场景
x-reactions可以直接传1个函数,主要用于异步数据加载、复杂场景处理等,十分的常见。
案例:异步加载select组件数据
const dzpFun = (field) => {
await new Promise((resolve) => setTimeout(resolve, 500));
const options = [
{ label: '大周平', value: 'dzp' },
{ label: '周老板', value: 'boss' },
];
field.setComponentProps({
options,
});
}
/*schema*/
const schema = {
type:'object',
properties:{
city:{
type:'array',
title:'城市',
'x-decorator':'FormItem',
'x-component':'Select',
'x-reactions':'{{dzpFun}}'
},
},
}
五、scope
scope是schema中用于访问外部变量和函数的桥梁,schema中所有通过'{{}}'定义的都是函数与变量都是scope的。特殊点:schema所有的动态属性也都必须使用'{{}}'操作
5.1 使用
scope的使用格式只有一种如下,即字符串模版
'{{name}}'//变量
'{{getName("test")}}'//方法
5.2 常见案例
- 在scope中定义表单校验方法并使用
// scope中定义的函数
const myValidator = (value: string,rule:any,ctx:any) => {
if (!value || !value.trim()) {
return '姓名不能是空';
}
if (value === '123') {
return '姓名不能是123';
}
return true; // 校验通过
};
// schema中调用
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-validator': [
{
validator: '{{myValidator}}'
}
],
},
},
};
2. scope变量控制表单项的联动、禁止、默认值等
//scope
const isDisabled = true;
const status = 'visible'; // 'visible' | 'hidden' | 'none'
const defaultV = 'dzp';
//schema
const schema = {
type: 'object',
properties: {
name: {
type: 'string',
title: '姓名',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
placeholder: '请输入姓名',
},
'x-disabled': '{{isDisabled}}',
'x-display': '{{status}}',
"default": '{{defaultV}}',
},
},
};
六、特殊指令
6.1 $self
$self 代表当前字段的实例对象,可在字符串表达式中访问字段的状态、值和方法。
- $self.value 当前表单项的值
const schema = {
type:'object',
properties:{
name:{
type:'string',
title:'姓名',
'x-decorator':'FormItem',
'x-component':'Input',
'x-disabled':'{{$self.value === "dzp"}}',
},
},
}
6.2 $deps
$deps 是一个数组,包含 dependencies 中声明的依赖字段的值,按声明顺序排列。使用时按数组下标获取
'x-reactions': [
{
dependencies: ['.name', '.age', '.email'], // 声明依赖
when:'{{$deps[0]==="xxx"}}'//第一个依赖name
when:'{{$deps[1]==="xxx"}}'//第二个依赖age
}
]