Field组件
如果您想要对字段进行更精细的控制,则推荐使用Field
组件。Field
组件是创建表单自定义字段组件的终极武器,允许您完全控制字段的渲染、校验、可视、使能等等,并且所有字段特性属性均可以使用计算属性声明,这使用Field
组件具有超强的自定义能力,还可以很轻松地实现表单字段之间的联动。
创建字段
使用Field
组件创建一个字段。
<Field
name="user.firstName"
render={
({name, value, bind }) => {
return <input {...bind} />
}
}
/>
Field
组件至少要求提供name
和render
两个属性。
name
: 指向的是状态树中的某个字段的路径,如user.firstName
。render
: 用于渲染Field
组件,它接收一个AutoFieldRenderProps
类型的参数,负责渲染Field
组件。
简单的Field
组件示例:
import React from "react"
import { useForm,assert } from "@autostorejs/react";
import { Button,Input,Layout, JsonView, ColorBlock } from "x-react-components"
export default ()=>{
const { Form,Field,useReactive,reset,useField,dirty,valid } = useForm({
user:{
name:"fisher",
age:12,
vip:true,
tags:['Nodejs','React','Vue','Angular','TypeScript','JavaScript','CSS','HTML']
}
})
const [ state ] = useReactive()
const nameField = useField("user.name")
return <Layout>
<div>
<ColorBlock name="IsValid">{String(valid)}</ColorBlock>
<ColorBlock name="IsDirty">{String(dirty)}</ColorBlock>
<Form>
<Field
name="user.name"
label="Name"
validate={(value)=>assert(value.length>3,"长度必须大于3")}
// eslint-disable-next-line no-unused-vars
render={({name,value,bind,label,onChange,error,select,enable,visible,placeholder,validate,required,readonly,loading,help})=>{
return <div>
<Input label={label} {...bind}/>
{error ? <span style={{color:'red'}}>{error}</span> : null}
</div>
}}
/>
<Field
name="user.age"
label="Age"
validate={(value)=>assert(value>=18,"年龄不能小于18岁")}
// eslint-disable-next-line no-unused-vars
render={({name,value,bind,label,onChange,error,select,enable,visible,placeholder,validate,required,readonly,loading,help})=>{
return <div>
<Input label={label} {...bind}/>
{error ? <span style={{color:'red'}}>{error}</span> : null}
</div>
}}
/>
<Input label="FirstName" {...nameField} />
<Button onClick={()=>{
reset()
}}>Reset</Button>
</Form>
</div>
<div>
<JsonView data={state}/>
</div>
</Layout>
}
字段校验
Field
组件支持validate
属性用于配置字段的校验规则。
基本校验
<Field
validate={(value)=>{
return assert(value.length>3,"长度必须大于3")
}}
//...
/>
validate
该属性的值用于创建一个依赖于当前字段(name指向的状态)状态的计算或监听属性,并且其scope
也指向当前字段的值。 当字段值发生变化时,会重新计算该属性的值,从而会触发校验。validate
计算函数返回true/false
来代表校验是否通过。也可以throw Error
,然后通过错误对象的message
属性来获取错误信息并传递给render
函数。
<Field
validate={(value)=>{
return assert(value.length>3,"长度必须大于3")
}}
render={
({ name,
value,
error, // 校验错误信息
validate, // 校验结果true/false
bind
}) => {
return (
<div>
<input {...bind} />
{validate ? '校验合法' : '校验不合法'}
{error ? <span>{error.message}</span> : null}
</div>
)
}
}
/>
validate
是一个同步计算属性,依赖于当前字段的值,当字段值发生变化时,会重新计算该属性的值,从而会触发校验。
联动校验
正因为validate
是一个同步计算属性,所以也可以依赖于其他字段的值,从而实现字段之间的联动校验。
import React from 'react';
import { useForm } from '@autostorejs/react';
import { Button,Input, Layout, JsonView } from "x-react-components"
export default ()=>{
const { reset, useReactive, Field } = useForm({
order:{
name:"精通AutoStore",
price: 9.9,
count: 1,
}
})
const [ state ] = useReactive()
return <Layout>
<div>
<Field
name="order.name"
label="Name"
render={({ label,bind,validate })=>{
return <Input label={label} {...bind} validate={validate} />
}}
/>
<Field
name="order.price"
label="Price"
render = {({ label,bind,validate })=>{
return <Input label={label} {...bind} validate={validate} />
}}
/>
<Field
name="order.count"
label="Count"
validate = {(value)=>{
return value>0 && state.order.price > 9
}}
render = {({ label,bind,validate })=>{
return <>
<Input label={label} {...bind}/>
{!validate && <span style={{color:'red'}}>count must be greater than 0 and price must be greater than 9</span>}
</>
}}
/>
<Button onClick={()=>{
reset()
}}>Reset</Button>
</div>
<div>
<JsonView border={false} data={state}/>
</div>
</Layout>
}
在上例中,count
的校验规则是count>0 && order.price > 9
,由于其validate
是同步计算属性,根据计算属性的依赖收集规则,其自动依赖于order.price
和count
的值,所以当count
和order.price
更新时会触发validate
属性重新计算,从而触发校验。
提示
validate
属性是一个同步计算属性,具备依赖自动收集功能,当字段值发生变化时,会重新计算该属性的值,从而会触发校验。
字段计算属性
除了validate
属性,则字段组件还支持以下常用的计算属性:
属性 | 类型 | 默认值 | 计算属性 | 说明 |
---|---|---|---|---|
required | boolean | false | 同步 | 是否必填 |
visible | boolean | true | 同步 | 是否可见 |
enable | boolean | true | 同步 | 是否可用 |
readonly | boolean | false | 同步 | 是否只读 |
label | string | - | 同步 | 字段标签 |
placeholder | string | - | 同步 | 字段占位符 |
help | string | - | 同步 | 字段帮助信息 |
select | any[] | - | 异步 | 字段选择项 |
以上这些属性都是同步计算属性,当字段值发生变化时,会重新计算该属性的值,从而会触发字段的重新渲染。同时也支持计算属性的相关特性。
以上这些属性会在更新时触发字段的重新渲染,并传递给字段组件的render
函数。
<Field
name="user.firstName"
required={(state)=>{ ... }}
visible={(state)=>{ ... }}
enable={(state)=>{ ... }}
readonly={(state)=>{ ... }}
label={(state)=>{ ... }}
placeholder={(state)=>{ ... }}
help={(state)=>{ ... }}
select={async (state)=>{ ... }}
render={
({name, value, bind, required, visible, enable, readonly, label, placeholder, help, select }) => {
return (
<div>
{/* ... */}
</div>
)
}
}
/>
异步字段计算属性
以上的计算属性除了select
外,其他均是同步计算属性,当字段值发生变化时,会重新计算该属性的值,从而会触发字段的重新渲染。
但是有时候我们需要的计算属性是异步的,如我们想进行异步校验时,则需要使用useComputed
异步计算属性。
<Field
name="user.firstName"
render={
({name, value, bind }) => {
const valid = useComputed<boolean>(async (name:string)=>{
await delay(1000) // 模拟异步验证
return assert(name.length>3,"name长度必须大于3")
},{
depends:["user.name"],
scope:'user.name',
onError:()=>false
})
return (
<div>
{/* ... */}
</div>
)
}
}
/>
useComputed
用于动态创建一个异步计算属性。- 上例中,指定了
useComputed
的depends=["user.name"]
和scope:'user.name
,代表了其依赖于user.name
的值,当user.name
更新时,会重新计算该属性的值。并且useComputed
的scope
也指向user.name
的值。 - 当校验失败时不是只返回
false
,而是触发错误,通过error.message
来错误信息。 - 指定
onError:()=>false
的目的是当校验失败时,将计算值重置为false
。
实际示例如下:
import React from "react"
import { useForm,delay,assert } from "@autostorejs/react";
import { Button,Input,Layout, JsonView,Loading } from "x-react-components"
export default ()=>{
const { Form,Field,useReactive,reset,useComputed } = useForm({
user:{
name:"fisher",
age:12,
vip:true
}
})
const [ state ] = useReactive()
return <Layout>
<div>
<Form>
<Field
name="user.name"
label="Name"
render={({bind,label})=>{
const valid = useComputed<boolean>(async (name:string)=>{
await delay(1000) // 模拟异步验证
return assert(name.length>3,"name长度必须大于3")
},{
depends:["user.name"],
scope:'user.name',
onError:()=>false
})
return <div>
<Input label={label} {...bind}/>
{ valid.loading
? <Loading title="正在校验...."/>
: !valid.value && <span style={{color:'red'}}>{valid.error}</span>
}
</div>
}}
/>
<Button onClick={()=>{
reset()
}}>Reset</Button>
</Form>
</div>
<div>
<JsonView data={state}/>
</div>
</Layout>
}
配置onError:()=>false
的原因?
计算属性具有一个onError
回调函数,用于处理计算属性的错误,当计算属性发生错误时,会触发onError
回调函数,onError
的返回值会被作为计算结果写入。