order: 4 group: order: 3 title: 高级用法

toc: false

表单方法(form)

FormRender v1.x 使用了状态内置的模型,所以外部对表单的所有操作都依赖于 form 实例提供的方法。 本篇会 cover 以下这些常用 api:

  1. form.submit
  2. form.setValues
  3. form.setValueByPath
  4. form.setSchemaByPath

以及以下的生命周期

  1. beforeFinish
  2. onFinish

我们写一个最常用的场景:加载一个已经填写完成的表单,从服务端异步获取数据(这里使用 mock);修改表单并提交新数据给服务端

例 1: 表单与服务端的基本交互

异步加载表单 Schema, 服务端数据填充表单初始值, 提交校验通过后提交数据给服务端

  1. /**
  2. * transform: true
  3. * defaultShowCode: true
  4. */
  5. import React, { useState, useEffect } from 'react';
  6. import { Button, Space, message } from 'antd';
  7. import FormRender, { useForm } from 'form-render';
  8. import { fakeApi, delay } from './utils';
  9. const Demo = () => {
  10. const form = useForm();
  11. const [schema, setSchema] = useState({});
  12. const getRemoteData = () => {
  13. fakeApi('xxx/getForm').then(_ => {
  14. form.setValues({ input1: 'hello world', select1: 'c' });
  15. });
  16. };
  17. useEffect(() => {
  18. // 异步加载表单
  19. delay(1000).then(_ => {
  20. setSchema({
  21. type: 'object',
  22. properties: {
  23. input1: {
  24. title: '简单输入框',
  25. type: 'string',
  26. required: true,
  27. },
  28. select1: {
  29. title: '单选',
  30. type: 'string',
  31. enum: ['a', 'b', 'c'],
  32. enumNames: ['早', '中', '晚'],
  33. },
  34. },
  35. });
  36. });
  37. }, []);
  38. const onFinish = (data, errors) => {
  39. if (errors.length > 0) {
  40. message.error(
  41. '校验未通过:' + JSON.stringify(errors.map(item => item.name))
  42. );
  43. } else {
  44. fakeApi('xxx/submit', data).then(_ => message.success('提交成功!'));
  45. }
  46. };
  47. return (
  48. <div style={{ width: '400px' }}>
  49. <FormRender form={form} schema={schema} onFinish={onFinish} />
  50. <Space>
  51. <Button onClick={getRemoteData}>加载服务端数据</Button>
  52. <Button type="primary" onClick={form.submit}>
  53. 提交(见console
  54. </Button>
  55. </Space>
  56. </div>
  57. );
  58. };
  59. export default Demo;

例 2:服务端校验

服务端校验,通过 beforeFinish 从外部回填 error 信息到表单,注意 beforeFinish 需返回要回填的 error

  1. /**
  2. * transform: true
  3. * defaultShowCode: true
  4. */
  5. import React from 'react';
  6. import { Button, message } from 'antd';
  7. import FormRender, { useForm } from 'form-render';
  8. import { fakeApi } from './utils';
  9. const schema = {
  10. type: 'object',
  11. properties: {
  12. input1: {
  13. title: '简单输入框',
  14. type: 'string',
  15. required: true,
  16. },
  17. select1: {
  18. title: '单选',
  19. type: 'string',
  20. enum: ['a', 'b', 'c'],
  21. enumNames: ['早', '中', '晚'],
  22. },
  23. },
  24. };
  25. const Demo = () => {
  26. const form = useForm();
  27. const onFinish = (data, errors) => {
  28. if (errors.length > 0) {
  29. message.error(
  30. '校验未通过:' + JSON.stringify(errors.map(item => item.name))
  31. );
  32. } else {
  33. fakeApi('xxx/submit', data).then(_ => message.success('提交成功!'));
  34. }
  35. };
  36. // 服务端校验在这里做
  37. const beforeFinish = ({ data, errors, schema, ...rest }) => {
  38. return fakeApi('xxx/validation').then(_ => {
  39. return { name: 'select1', error: ['外部校验错误'] };
  40. });
  41. };
  42. return (
  43. <div style={{ width: '400px' }}>
  44. <FormRender
  45. form={form}
  46. schema={schema}
  47. beforeFinish={beforeFinish}
  48. onFinish={onFinish}
  49. />
  50. <Button type="primary" onClick={form.submit}>
  51. 提交(见console
  52. </Button>
  53. </div>
  54. );
  55. };
  56. export default Demo;

例 3:bind

接口数据与展示经常会不符,例如 form 的交互是日期范围组件,服务端传的值是 startDate,endDate 两个字段。此时使用 bind 字段

  1. /**
  2. * transform: true
  3. * defaultShowCode: true
  4. */
  5. import React from 'react';
  6. import { Button, message, Space } from 'antd';
  7. import FormRender, { useForm } from 'form-render';
  8. import { fakeApi } from './utils';
  9. const schema = {
  10. type: 'object',
  11. properties: {
  12. dateRange: {
  13. bind: ['startDate', 'endDate'],
  14. title: '日期范围',
  15. type: 'range',
  16. format: 'date',
  17. },
  18. },
  19. };
  20. const Demo = () => {
  21. const form = useForm();
  22. const onFinish = (data, errors) => {
  23. if (errors.length > 0) {
  24. message.error(
  25. '校验未通过:' + JSON.stringify(errors.map(item => item.name))
  26. );
  27. } else {
  28. fakeApi('xxx/submit', data).then(_ => message.success('提交成功!'));
  29. }
  30. };
  31. const getRemoteData = () => {
  32. fakeApi('xxx/getForm').then(_ => {
  33. form.setValues({ startDate: '2020-04-04', endDate: '2020-04-24' });
  34. });
  35. };
  36. return (
  37. <div style={{ width: '400px' }}>
  38. <FormRender form={form} schema={schema} onFinish={onFinish} />
  39. <Space>
  40. <Button onClick={getRemoteData}>加载服务端数据</Button>
  41. <Button type="primary" onClick={form.submit}>
  42. 提交(见console
  43. </Button>
  44. </Space>
  45. </div>
  46. );
  47. };
  48. export default Demo;

例 4:服务端加载选择框的选项

服务端获取数据后展示下拉选项的选项值,我们提供了 form.setSchemaByPath 方法

  1. /**
  2. * transform: true
  3. * defaultShowCode: true
  4. */
  5. import React, { useEffect } from 'react';
  6. import { Button, message } from 'antd';
  7. import FormRender, { useForm } from 'form-render';
  8. import { fakeApi } from './utils';
  9. const schema = {
  10. type: 'object',
  11. properties: {
  12. input1: {
  13. title: '简单输入框',
  14. type: 'string',
  15. required: true,
  16. },
  17. obj1: {
  18. title: '对象',
  19. description: '这是一个对象类型',
  20. type: 'object',
  21. properties: {
  22. select1: {
  23. title: '单选',
  24. type: 'string',
  25. widget: 'select',
  26. },
  27. },
  28. },
  29. },
  30. };
  31. const Demo = () => {
  32. const form = useForm();
  33. const onMount = () => {
  34. form.setSchemaByPath('obj1.select1', {
  35. enum: ['east', 'south', 'west', 'north'],
  36. enumNames: ['东', '南', '西', '北'],
  37. });
  38. };
  39. const onFinish = (data, errors) => {
  40. if (errors.length > 0) {
  41. message.error(
  42. '校验未通过:' + JSON.stringify(errors.map(item => item.name))
  43. );
  44. } else {
  45. message.info(JSON.stringify(data));
  46. }
  47. };
  48. return (
  49. <div style={{ width: '400px' }}>
  50. <FormRender
  51. form={form}
  52. schema={schema}
  53. onMount={onMount}
  54. onFinish={onFinish}
  55. />
  56. <Button type="primary" onClick={form.submit}>
  57. 提交(见console
  58. </Button>
  59. </div>
  60. );
  61. };
  62. export default Demo;