Skip to content

Field组件

如果您想要对字段进行更精细的控制,则推荐使用Field组件。Field组件是创建表单自定义字段组件的终极武器,允许您完全控制字段的渲染、校验、可视、使能等等,并且所有字段特性属性均可以使用计算属性声明,这使用Field组件具有超强的自定义能力,还可以很轻松地实现表单字段之间的联动。

创建字段

使用Field组件创建一个字段。

tsx
    <Field  
        name="user.firstName" 
        render={
            ({name, value, bind }) => {
                return <input {...bind} />
            }
        }
    />

Field组件至少要求提供namerender两个属性。

  • name: 指向的是状态树中的某个字段的路径,如user.firstName
  • render: 用于渲染Field组件,它接收一个AutoFieldRenderProps类型的参数,负责渲染Field组件。

简单的Field组件示例:

在 stackblitz 中打开
在 codesandbox 中打开
展开代码
复制代码
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属性用于配置字段的校验规则。

基本校验

tsx
<Field
    validate={(value)=>{
        return assert(value.length>3,"长度必须大于3")
    }}
    //...
/>
  • validate该属性的值用于创建一个依赖于当前字段(name指向的状态)状态的计算或监听属性,并且其scope也指向当前字段的值。 当字段值发生变化时,会重新计算该属性的值,从而会触发校验。
  • validate计算函数返回true/false来代表校验是否通过。也可以throw Error,然后通过错误对象的message属性来获取错误信息并传递给render函数。
tsx
<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是一个同步计算属性,所以也可以依赖于其他字段的值,从而实现字段之间的联动校验。

在 stackblitz 中打开
在 codesandbox 中打开
展开代码
复制代码
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.pricecount的值,所以当countorder.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函数

tsx
<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异步计算属性。

tsx
<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用于动态创建一个异步计算属性。
  • 上例中,指定了useComputeddepends=["user.name"]scope:'user.name,代表了其依赖于user.name的值,当user.name更新时,会重新计算该属性的值。并且useComputedscope也指向user.name的值。
  • 当校验失败时不是只返回false,而是触发错误,通过error.message来错误信息。
  • 指定onError:()=>false的目的是当校验失败时,将计算值重置为false

实际示例如下:

在 stackblitz 中打开
在 codesandbox 中打开
展开代码
复制代码
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的返回值会被作为计算结果写入。