formily2.x使用拾遗

1,008 阅读3分钟

在实际生产中使用schema模式中遇到的问题记录如下

校验相关

schema模式的文档中集成的可以快速校验的validator相关属性

image.png

因为相关的种类过多,只介绍一种使用方式,其余的类似,可以自己尝试

const schema: ISchema = {
  type: 'object',
  properties: {
    input: {
      type: 'string',
      title: '输入框',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
      const: '123',
    },
  },
}

效果如下

image.png

在使用了const,如文档介绍效果,它会检验当前的表单项值,而且提示语较为僵硬,不支持定制化

如果想要灵活一些(产品硬要)就可以使用一下x-validator

const schema: ISchema = {
  type: 'object',
  properties: {
    input: {
      type: 'string',
      title: '输入框',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
      'x-validator':[
        {
          const:"123",
          message: '是不是输不了123!'
        }
      ]
    },
  },
};

image.png

通过x-validator可以给每一个校验规则加一些新的提示,可以说九分甚至十分的方便了

如果需要根据表单中其他项的值对某项做校验的话也能做到,也就是用x-reactions写自定义校验,自定义校验方法时第一个参数是自身的值

import { FormButtonGroup, FormItem, Input, Submit } from '@formily/antd-v5';
import { createForm } from '@formily/core';
import { FormProvider, ISchema, createSchemaField } from '@formily/react';

const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
  },
});

const form = createForm();

const validatorFn = (value, $b_value) => {
  if (value !== $b_value) {
  //提示和类型,用error可以在表单提交前进行拦截拦截
    return {
      message: 'a!==b',
      type: 'error',
    };
  }
};

const schema: ISchema = {
  type: 'object',
  properties: {
    a: {
      type: 'string',
      title: '输入框a',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
       // 通过字段联动协议将b的值用被动模式传给自定义校验函数使用
      'x-reactions': {
        dependencies: ['b'],
        fulfill: {
          schema: {
          //自定义校验规则,传函数校验
            'x-validator': '{{(value)=>validatorFn(value, $deps[0])}}',
          },
        },
      },
    },
    b: {
      type: 'string',
      title: '输入框b',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
  },
};

export default () => {
  return (
    <div>
      <FormProvider form={form}>
      //注入函数方法
        <SchemaField schema={schema} scope={{ validatorFn }} />
        <FormButtonGroup>
          <Submit onSubmit={console.log}>提交</Submit>
        </FormButtonGroup>
      </FormProvider>
    </div>
  );
};

image.png

不过如果只是需要对自身值做校验不需要联动的校验且校验规则固定,建议就用前面的方法formily提供的校验方法即可

跟后端的交互一些场景

有些表单需要带一些不需要更改的值要传给后端,但是在表单中其实是不需要展示的,这个时候可以使用x-hidden 保留数据但是不做展示,千万不要使用x-visible,会导致提交时该值不存在

import { FormButtonGroup, FormItem, Input, Submit } from '@formily/antd-v5';
import { createForm } from '@formily/core';
import { FormProvider, ISchema, createSchemaField } from '@formily/react';
import { useEffect } from 'react';

const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
  },
});

const form = createForm();

const schema: ISchema = {
  type: 'object',
  properties: {
    a: {
      type: 'string',
      title: '输入框a',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    b: {
      type: 'string',
      'x-hidden': true,
    },
  },
};

export default () => {
  useEffect(() => {
    form.setValues({
      a: '1',
      b: '2',
    });
  }, []);
  return (
    <div>
      <FormProvider form={form}>
        <SchemaField schema={schema} />
        <FormButtonGroup>
          <Submit onSubmit={console.log}>提交</Submit>
        </FormButtonGroup>
      </FormProvider>
    </div>
  );
};

image.png

如果使用x-visible:false会出现的情况如下

image.png

x-visible:false也有个妙用,在不使用observable做响应式处理时也可以在setValue时传一个只控制表单联动而不提交的值

import { FormButtonGroup, FormItem, Input, Submit } from '@formily/antd-v5';
import { createForm } from '@formily/core';
import { FormProvider, ISchema, createSchemaField } from '@formily/react';
import { Button } from 'antd';
import { useEffect, useState } from 'react';

const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
  },
});

const form = createForm();

const schema: ISchema = {
  type: 'object',
  properties: {
    a: {
      type: 'string',
      title: '输入框a',
      'x-decorator': 'FormItem',
      'x-component': 'Input',
      'x-reactions': {
        dependencies: ['b'],
        fulfill: {
          schema: {
            'x-disabled': '{{$deps[0]}}',
          },
        },
      },
    },
    b: {
      type: 'boolean',
      'x-visible': false,
    },
  },
};

export default () => {
  const [status, setStatus] = useState(true);
  useEffect(() => {
    form.setValues({
      a: '1',
      b: status,
    });
  }, [status]);
  return (
    <div>
      <FormProvider form={form}>
        <SchemaField schema={schema} />
        <FormButtonGroup>
          <Submit onSubmit={console.log}>提交</Submit>
        </FormButtonGroup>
      </FormProvider>

      <Button onClick={() => setStatus(!status)}>切换状态</Button>
    </div>
  );
};

image.png

用这个骚操作的时候,最好把这种只控制状态而不提交的描述放最后

实现一个自定义的ArrayCards组件

不是非要放着现成的不用,而是产品来了个大的,在删除前,需要搞个二次确认提示。而在formily提供的ArrayCards的删除是不支持在删除前提供的前置方法的(当然也有可能是我学艺不精,如果有老哥知道咋玩的也可以给我说一下,我好把我自定义的换过来o(╥﹏╥)o)

// ArrayCards
import { ArrayField } from '@formily/core';
import {
  observer,
  RecursionField,
  useField,
  useFieldSchema,
} from '@formily/react';
import { Button, Card, Modal } from 'antd';

export default observer((props) => {
  const field = useField<ArrayField>();
  const schema = useFieldSchema();
  const dataSource = Array.isArray(field.value) ? field.value : [];
  return (
    <>
      <Button
        onClick={() => {
          field.push({});
        }}
      >
        添加一个卡片
      </Button>
      {dataSource?.map((item, index) => {
        const items: any = Array.isArray(schema.items)
          ? schema.items[index] || schema.items[0]
          : schema.items;
        return (
          <Card
            key={index}
            title={props.title}
            extra={
              <a
                href="#"
                onClick={() => {
                  Modal.error({
                    title: '删除',
                    content: '你确定要删除?',
                    onOk: () => field.remove(index),
                  });
                }}
              >
                删除
              </a>
            }
          >
            <RecursionField schema={items} name={index} />
          </Card>
        );
      })}
    </>
  );
});
const schema: ISchema = {
  type: 'object',
  properties: {
    array: {
      type: 'array',
      'x-component': 'ArrayCards',
      'x-decorator': 'FormItem',
      'x-component-props': {
        title: '对象数组',
      },
      items: {
        type: 'object',
        properties: {
          a: {
            type: 'string',
            'x-decorator': 'FormItem',
            title: 'a',
            'x-component': 'Input',
          },
          b: {
            type: 'string',
            'x-decorator': 'FormItem',
            title: 'b',
            'x-component': 'Input',
          },
        },
      },
    },
  },
};

image.png

RecursionField组件可以解析传来的schema,用来还原在schema对象中的items下的properties的表单项 react.formilyjs.org/zh-CN/api/c…

useField()在自定义组件内读取当前字段属性,操作字段状态等,在所有 Field 组件的子树内都能使用,ArrayField中就有增改和移动的方法 core.formilyjs.org/zh-CN/api/m…

useFieldSchema() 可以拿到当前字段的schema react.formilyjs.org/zh-CN/api/h…
observer才能把自增表单的数据dataSource变成响应式来展示 react.formilyjs.org/zh-CN/api/s…