【Formily+Antd】Select及联的做法

前提:
Formily:整体用SchemaForm,其实表体用Form也可以,只是写法有微小区别,思路是一样的。
数据结构:返回的数据结构大约是这样的:

content:[
{
  children:{
	    children:{
	    ...
	    }
     ...
     }
  ...
  }
{...}
...
]

实现效果:类似三个Select先选省,再选市,最后选区。在父级选择后,才会加载出子集,且父级发生变化后,子集会作出不同的变化且子集初始选择清空。类似下图
【Formily+Antd】Select及联的做法_第1张图片

思路和碰壁:
1、数据处理浅谈,主要是用effects生命周期控制,当对应name下的数据发生变化的时候用map对应处理数据。
2、一开始想着使用@formily/antd的Select,后来发现,做出来后当父级发生变化,子集的选定的值不能清空。如果通过effect中的

state.value=null

会有两个不想得到的后果1、组件初始化后如果该字段设置了required:true,那么渲染完成后就会提示没有填入,这不是我们想要的(不管用表单的任何生命周期都是这样)。2、该字段无法选择,一直是空的状态。
3、根据上述思路,如果不定value,用state定义其他属性字段皆不能得到相应的效果。
4、为了改变子集选定的值,尝试过用x-prop、x-component-props传值均不能达到效果,于是目光投到antd,@formily/antd也是从antd在此封装来的,既然封装达不到效果,那么Select组件就需要自己组装了。

计划:
1、用antd的Select自己在此封装出一个能根据数据变化的组件,该组件除了根据传入值做列表渲染之外,当传入数组发生改变的时候清除之前选择的值。在表中复用自己组装的组件。
2、effects仅用于控制数据的拆解和初始化,不负责组件的变化。
3、在第一步中还有个逻辑缺陷,就是如果父级变了,子集没变,依旧不能重新加载。我的处理方法是用effects配合,在该子集的父级上定义变量,如果父级发生变化,会改变变量的值,这个变量同样控制子集是否更新。

条件讲明白了就贴代码解释。

首先贴出封装的Select组件:

import React from 'react'
import { Select } from 'antd'
import _ from 'lodash'


class CreateSelect extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      change_value: null
    }
  }

  handleChange = (e) => {
    this.setState({
      change_value: e
    })

    this.props.onChange(e)

  }


  componentWillUpdate(newProps, newState) {
    if (this.props.data !== newProps.data) {
      this.handleChange(undefined)
    }
  }

  componentDidMount() {
    if (this.props.clear) {
      this.handleChange(undefined)
    }
  }

  render() {
    return (
      <Select
        allowClear
        style={this.props.set_style}
        onChange={this.handleChange}
        value={this.state.change_value}
        placeholder={this.props.placeholder}
      >
        {
          !_.isEmpty(this.props.data) ?
            _.map(this.props.data, item => {
              return 

上述代码中:
1、onChange是formily自带的函数,子组件调用函数可以改变父组件中对应字段的value值。
2、用react的componentWillUpdate生命周期控制判断传入值是否变化从而判断value值是否需要改变。
3、在计划的第三点提到的逻辑缺陷的对应的方法就是传入clear变量,但是这个变量不能放在componentWillUpdate这个生命周期和2中的条件并列,会陷入死循环。

我们再看看表单的写法:

{components}
        initialValues={initData}
        onSubmit={this.onSubmit}
        actions={actions}
        effects={($, { setFieldState }) => {
          $(LifeCycleTypes.ON_FIELD_CHANGE).subscribe(() => {
          ...
            setFieldState('qualification_major', state => {
              let temp = [];
              if (this.state.choosed_major && (state.value !== this.state.choosed_major)) this.setState({ clear: true })
              else this.setState({ clear: false })
              const info = this.state.info;
              _.map(info, item => {
                item.majors.map(item => {
                  if (item.major_id == state.value) {
                    item.levels.map(item => {
                      temp.push({ label: item.level_name, value: item.level_id });
                    });
                  }
                });
              });
              this.setState({
                level: temp,
                choosed_major: state.value
              });
            });
          });
  ...
  >
  ...
   			type="string"
            x-props={{ placeholder: '请选择专业' }}
            enum={major}
            title="专业"
            name="major"
            x-rules={
              {
                required: true,
                message: "请选择专业"
              }

            }
            x-component="CreateSelect"
            x-component-props={{ data: major, placeholder: '请选择专业', set_style: { width: 336 } }}
          />

上述代码中:
1、choosed_major这个变量是判断clear的标准,这个变量缓存上次选择的值,和这一次选择的值做比较。
2、CreateSelect是封装Select组件的名字,且用 x-component-props对该组件传值。在这个props里面并没有设置clear变量,是因为该变量应该放置在他的子集里面,这样的设计,在major字段发生变化的时候clear就为true,无论子集的数据有没有变化都会使得选择的值为空。

这么设计还有个问题:即时报错,虽然通过onChange可以回传值,但是值为空的时候无论设置undefined、null、‘’等等都不能规避这个问题,只能说对比用@formily/antd实现的效果,在使用体验上这种做法更好。
如图所示
在这里插入图片描述
这个问题查阅了很多资料暂时没有解决方法,当父集发生变化的时候对应的子集都会报错。Formily在报错时机上面没有提供更多的控制字段。

如有问题,欢迎指正。

你可能感兴趣的:(formily,前端,antd,react)