Form

业务场景中,我们会经常使用Form进行一些数据收集的工作。这一章,我们会实现一个简单的 Form 用来收集数据,并与后端通信进行传输。

首先看一下我们这一章需要实现的 Form 是什么样的:

Form.png

Form demo表单绑定数据源。

从上面的内容可以看出来,有必输字段,也有需要进行特殊校验的字段,如手机号,同时也会出现带后缀或者前缀的字段。

接下来,我们从 DataSet 的构建开始,一步步构建一个最为简单的 Form。

构建 DataSet

首先,我们需要为了这个 Form 构造一个简单的 DataSet 收集数据:

  1. // DataSet.js
  2. const FormDataSet = {
  3. // DataSet 不和后端交互时,自动新建一条数据,在表单场景下比较常见
  4. autoCreate: true,
  5. fields: [
  6. // 这里是与后端约定的,上传时用到的字段
  7. { name: 'phone', type: 'string', label: '手机号' },
  8. { name: 'password', type: 'string', label: '密码' },
  9. { name: 'confirmPassword', type: 'string', label: '确认密码' },
  10. { name: 'age', type: 'number', label: '年龄' },
  11. { name: 'sex', type: 'string', label: '性别' },
  12. { name: 'language', type: 'string', label: '语言' },
  13. { name: 'email', type: 'string', label: '邮箱' },
  14. { name: 'homePage', type: 'string', label: '个人主页' },
  15. { name: 'birth', type: 'date', label: '生日' },
  16. ],
  17. transport: {
  18. // 创建时 DataSet 将会调用的方法
  19. // url 随便找的,可以自己替换
  20. // create / read / update / destroy 都可以等量替换成函数,create 涉及到上传新创建的数据,因此需要用到 data
  21. create: ({ data, params, dataSet }) => {
  22. console.log(data+" ", params+" ", dataSet.toJSONData());
  23. return ({
  24. // url: 'v1/projects/${projectId}',
  25. // method: 'post',
  26. // data,
  27. })
  28. }
  29. },
  30. events: {
  31. load: ({ dataSet }) => {
  32. console.log('加载完成', dataSet)
  33. }
  34. }
  35. };
  36. export default FormDataSet;

接下来我们再看看需要实现的 Form,这里可以发现有几个需要注意的点:

  1. 性别、语言是多选框 / 下拉框,而这几项存在两种写法,这里我们介绍绑定数据源的写法。
  2. 手机号本身存在特殊的校验,需要我们在 field 内对字段进行相应的配置

首先来看看第一个问题。

绑定数据源的形式实现多选 / 下拉选择

绑定数据源实现多选 / 下拉的主要优势在于,如果这两个字段以后需要从后端获取多选 / 下拉值的时候,我们不需要对渲染的代码进行过多的改造即可实现。实际上,我们只需要为这两个下拉框新建两个 DataSet 即可。

首先,为性别和语言分别新建两个 DataSet 构造对象,分别叫做 sexOptionDataSet 与 languageOptionDataSet。

  1. // sexOptionDataSet.js
  2. const sexOptionDataSet = {
  3. fields: [
  4. // 这里的字段配置,实际上是根据我们自己的需求虚拟的
  5. // 实际上,如果从后端读取值的话,还是要按照后端的规范来写
  6. { name: 'text', type: 'string' },
  7. { name: 'value', type: 'string' },
  8. ],
  9. // 因为我们的需求比较简单,因此直接用 data 生成本地假数据即可
  10. data: [{
  11. text: '男',
  12. value: 'male',
  13. }, {
  14. text: '女',
  15. value: 'female',
  16. }],
  17. };
  18. export default sexOptionDataSet;
  19. // languageOptionDataSet.js
  20. // 与上面那个构造对象是同样的道理
  21. const languageOptionDataSet = {
  22. fields: [
  23. { name: 'text', type: 'string' },
  24. { name: 'value', type: 'string' },
  25. ],
  26. data: [{
  27. text: '简体中文',
  28. value: 'zh-cn',
  29. }, {
  30. text: '英语(美国)',
  31. value: 'en-us',
  32. }, {
  33. text: 'japanese',
  34. value: 'ja-jp',
  35. }],
  36. };
  37. export default languageOptionDataSet;

然后,我们修改一下之前生成的那个 DataSet,将我们新创建的两个 optionDataSet 传进去并进行构建。

  1. // DataSet.js
  2. import { DataSet } from 'choerodon-ui/pro';
  3. import SexOptionDataSet from './sexOptionDataSet';
  4. import LanguageOptionDataSet from './languageOptionDataSet';
  5. const sexOptionDataSet = new DataSet(SexOptionDataSet);
  6. const languageOptionDataSet = new DataSet(LanguageOptionDataSet);
  7. const FormDataSet = {
  8. autoCreate: true,
  9. fields: [
  10. { name: 'phone', type: 'string', label: '手机号' },
  11. { name: 'password', type: 'string', label: '密码' },
  12. { name: 'confirmPassword', type: 'string', label: '确认密码' },
  13. { name: 'age', type: 'number', label: '年龄' },
  14. // textField 指的是,下层的 options 中被指定用于显示选项名称的字段,这里我们已经定成 text 了
  15. // 同理,valueField 指下层 options 中被指定用于传输给后端的值,这里我们指定为 value
  16. { name: 'sex', type: 'string', label: '性别', textField: 'text', valueField: 'value', options: sexOptionDataSet },
  17. { name: 'language', type: 'string', label: '语言', textField: 'text', valueField: 'value', options: languageOptionDataSet },
  18. { name: 'email', type: 'string', label: '邮箱' },
  19. { name: 'homePage', type: 'string', label: '个人主页' },
  20. { name: 'birth', type: 'date', label: '生日' },
  21. ],
  22. transport: {
  23. create: ({ data, params, dataSet }) => {
  24. console.log(data+" ", params+" ", dataSet.toJSONData());
  25. return ({
  26. // url: 'v1/projects/${projectId}',
  27. // method: 'post',
  28. // data,
  29. })
  30. }
  31. },
  32. events: {
  33. load: ({ dataSet }) => {
  34. console.log('加载完成', dataSet)
  35. }
  36. }
  37. };
  38. export default FormDataSet;

接下来,Form 的初期准备工作就完成了,我们可以开始写渲染层的代码了。 Form 需要的组件比较多,比如用于数字输入的 NumberField,用于日期选取的 DatePicker 之类的,我们逐一进行引入。

  1. // Table.jsx
  2. import React from 'react';
  3. import FormDS from './DataSet';
  4. import {
  5. DataSet,
  6. Form,
  7. TextField,
  8. Password,
  9. NumberField,
  10. EmailField,
  11. UrlField,
  12. DatePicker,
  13. Select,
  14. SelectBox,
  15. Button,
  16. Menu,
  17. Dropdown,
  18. Icon,
  19. } from 'choerodon-ui/pro';
  20. class MyForm extends React.Component {
  21. constructor(props) {
  22. super(props);
  23. this.formDataSet = new DataSet(FormDS);
  24. }
  25. // 这里需要注意一下,Form 能接收 record 和 dataSet 这两个属性二选一。
  26. // 其中传入 dataSet 属性后,Form 会默认指向 DataSet 的 ds.current,默认是第一条
  27. // 如果需要指定 dataSet 中的某条数据的话,请使用 record 属性
  28. render() {
  29. return (
  30. <Form dataSet={this.formDataSet}>
  31. <TextField name="phone" />
  32. <Password name="password" />
  33. <Password name="confirmPassword" />
  34. <NumberField name="age" />
  35. <SelectBox name="sex" />
  36. <Select name="language" />
  37. <EmailField name="email" />
  38. <UrlField name="homePage" />
  39. <DatePicker name="birth" />
  40. <div>
  41. <Button type="submit">注册</Button>
  42. <Button type="reset" style={{ marginRight: 8 }}>重置</Button>
  43. </div>
  44. </Form>
  45. )
  46. }
  47. }
  48. export default MyForm;

可以看到,借助 DataSet 实现的表单,整体给人感觉非常清爽,负责数据交互的逻辑均被隐藏到了 DataSet 中。 接下来我们来处理第二个问题,如何对字段进行校验。

对字段进行校验

field 中存在三个字段,validator、pattern 和 required。这三个字段可以帮助我们进行字段的相关校验。

首先看手机号码的校验,手机号的校验非常容易找到,在网上稍微搜索一下就可以找得到,我们把它放到 pattern 中。

其次,每个字段都拥有必输校验,所以我们需要在每个字段上加上required = true

最后,确认密码字段存在需要与密码字段进行比对的需求,因此我们需要使用 validator 字段定义自定义函数,进行校验的自定义。

稍稍改造一下我们之前的 DataSet:

  1. // DataSet.js
  2. import { DataSet } from 'choerodon-ui/pro';
  3. import SexOptionDataSet from './sexOptionDataSet';
  4. import LanguageOptionDataSet from './languageOptionDataSet';
  5. const sexOptionDataSet = new DataSet(SexOptionDataSet);
  6. const languageOptionDataSet = new DataSet(LanguageOptionDataSet);
  7. const FormDataSet = {
  8. autoCreate: true,
  9. fields: [
  10. { name: 'phone', type: 'string', label: '手机号', pattern: '1[0-9]{10}', required: true },
  11. { name: 'password', type: 'string', label: '密码', required: true },
  12. { name: 'confirmPassword', type: 'string', label: '确认密码', required: true, validator: (value, name, record) => {
  13. // 这个地方能返回的不止有布尔类型和字符串类型,你也可以自己自行写一个 html 标签自定义样式,比如字体颜色改成蓝色
  14. if (value !== record.get('password')) {
  15. return '您两次输入的密码不一致,请重新输入';
  16. }
  17. return true;
  18. } },
  19. { name: 'age', type: 'number', label: '年龄', required: true },
  20. { name: 'sex', type: 'string', label: '性别', textField: 'text', valueField: 'value', options: sexOptionDataSet, required: true },
  21. { name: 'language', type: 'string', label: '语言', textField: 'text', valueField: 'value', options: languageOptionDataSet, required: true },
  22. { name: 'email', type: 'string', label: '邮箱', required: true },
  23. { name: 'homePage', type: 'string', label: '个人主页', required: true },
  24. { name: 'birth', type: 'date', label: '生日', required: true },
  25. ],
  26. transport: {
  27. create: ({ data, params, dataSet }) => {
  28. console.log(data+" ", params+" ", dataSet.toJSONData());
  29. return ({
  30. // url: 'v1/projects/${projectId}',
  31. // method: 'post',
  32. // data,
  33. })
  34. }
  35. },
  36. events: {
  37. load: ({ dataSet }) => {
  38. console.log('加载完成', dataSet)
  39. }
  40. }
  41. };
  42. export default FormDataSet;

这样我们就做好了一个最简单的表单,下一章我们会讲解如何利用 DataSet 构造一个简单的树。