美文网首页
使用antd的Form组件动态创建复杂Select表单

使用antd的Form组件动态创建复杂Select表单

作者: 追风的云月 | 来源:发表于2018-06-09 23:18 被阅读0次
    image.png
    image.png

    最近在项目中需要开发这样一个动态的select组件功能,梳理一下需求:

    1.左侧的select和右侧的select是一个联动效果,右侧的select有几个option取决于选择了左侧的某个option
    2.左侧的select有几个option取决于后台接口返回的数据
    3.当页面加载时,会去接口中读取已有的变量和成员表单,一行的变量和成员可以看做一个整体
    4.页面的表单可以用户手动增删

    在开发过程的遇到的问题和解决方案记录:

    1.动态增删表单

    原理是使用getFieldValue方法和setFieldsValue方法,通过增加keys数组成员和删除keys数组成员来达到增删表单的效果。

    removeFormItem = (k) => {
        const { form } = this.props;
        const keys = form.getFieldValue('keys');
        form.setFieldsValue({
            keys: keys.filter(key => key !== k),
        });
    }
    
    addFormItem = () => {
        const { form } = this.props;
        let keys = form.getFieldValue('keys');
        //增加表单时,这个key必须唯一,这里我引入uuid这个库来生成唯一ID 
        //( import UUID from 'uuid/v1')
        const nextKeys = keys.concat(UUID());
        form.setFieldsValue({
            keys: nextKeys,
        });
    }
    
    2.如何使用后台动态获取的值去设置页面的form表单

    这个一度成为我的难点,通过后台返回的已有变量列表值去设置初始页面的表单数量,比如后台返回3个,我需要设置三个select,那么这一步在什么时候做。

    • render的时候去设置已有表单,因为动态获取的表单数量是由父组件传入,所以在子组件中用props接收。
    • 在props中的taskVariableList属性读取每一项的id作为form的key值,遍历后放进一个formKeys的数组,这个数组就是已有表单的key值
    • getFieldDecorator('keys', { initialValue:formKeys}) 重点是使用这个方法设置初始表单
    const { getFieldDecorator, getFieldValue } = this.props.form;
    let {taskVariableList,taskVariableOptionList}=this.props;
    let formKeys=[],initValueObject={};
    //使用已选列表每项的id作为已有表单的key值
    if(taskVariableList.length>0){
        taskVariableList.map(variable=>{
            formKeys.push(variable.id);
            //将已有选项的值 以id-选项值的key-value形式存入一个对象
            initValueObject[variable.id]=variable
        })
    }else{
        //如果没有已有列表 保证页面有一个初始的select
        if(this.state.haveNoVariable){
            formKeys.push(singleSelectUUID);
        }
    }
    getFieldDecorator('keys', { initialValue:formKeys});
    
    3.如何渲染表单组件

    一切的数据获取,数据增删都是储存在form的keys属性里面,那么const keys = form.getFieldValue('keys'),使用这个keys数组去渲染表单,因为每次增删改查都是操作这个keys数组,所以通过keys总是渲染正确实时的表单数据

    <Form style={{margin:"0px 12px"}} className="task-variable">
        {
            keys&&keys.map((key,index)=>{
                return (
                    <Row gutter={24} key={key}>
                        <Col span={11}>
                            <FormItem
                                {...formItemLayout}
                                label="变量"
                            >
                                {getFieldDecorator(`variable#${key}`,{initialValue:initValueObject[key]?initValueObject[key]["fromDimensionId"]:initialLeftValue,})(
                                    <Select onChange={this.handleNewVariableChange.bind(this,taskVariableOptionList,key)}>
                                        {taskVariableOptionList.map(dimension=>{
                                            return (
                                                <Option value={dimension.fromDimensionId} key={`${dimension.id}-${index}`}>{dimension.name}</Option>
                                            )
                                        })}
                                    </Select>
                                )}
                            </FormItem>
                        </Col>
                        <Col span={11}>
                            <FormItem
                                {...formItemLayout}
                                label="成员"
                            >
                                {this.renderSubItem(initValueObject,taskVariableOptionList,key,getFieldDecorator,initValueObject[key]?initValueObject[key]["fromCodeId"]:initialRightValue)}
                            </FormItem>
                        </Col>
                        <Col className="select-action-wrap" span={2}>
                            {keys.length ===(index+1)&&keys.length!==1? (
                                <i className="icon font_family" onClick={this.addFormItem}>&#xe644;</i>
                            ) : <Icon
                                className="dynamic-delete-button"
                                type="minus-circle-o"
                                disabled={keys.length === 1}
                                onClick={() => this.removeFormItem(key)}
                            />}
                        </Col>
                    </Row>
                )
            })
        }
    </Form>
    
    4.如何渲染根据左侧选择的option动态渲染右侧select组件

    使用select的回调事件,每次选择左侧不同的option,将右侧的值存入state,然后读取这个值生成不同的option

    handleNewVariableChange=(optionData,key,value)=>{
        /**
         * optionData 右边的option 数组
         * key 传入的form表单的key 也就是已有变量列表的id或者新增表单的id
         * value 左侧的option变化value值 是变量的fromDimensionId
         * */
        let fromCodeList=[];
        optionData.map(dimension=>{
            if(dimension.fromDimensionId===value){
                fromCodeList=dimension.fromCodeList
            }
        })
        //以key作为state的唯一标志 将属于该key的fromCodeList存入 之后右边的option只需要读这个对应的fromCodeList来生成option
        this.setState({
            [key]:{"fromCodeList":fromCodeList},
            optionChangeFlag:true
        })
    }
    renderSubItem = (initValueObject,taskVariableOptionList,key,getFieldDecorator,initSubValue) =>{
        /**
         * initValueObject 已有变量列表的id-value对应的对象
         * taskVariableOptionList 备选option列表
         * key 传入的form表单的key 也就是已有变量列表的id或者新增表单的id
         * getFieldDecorator form的方法
         * initSubValue 右侧option的默认值
         * */
        let items=[];
        if(!this.state[key]){
            //这一步判断左侧option没有变化过 也就是取默认的fromCodeList
            if(!initValueObject[key]){
                //这一步判断是新增的表单 所有右侧取taskVariableOptionList第一项的fromCodeList来生成option
                items=taskVariableOptionList.length>0&&taskVariableOptionList[0].fromCodeList||[];
            }else{
                //这一步判断是已有的表单列表 右侧要取对应的fromCodeList生成option
                let fromDimensionId=initValueObject[key].fromDimensionId;
                taskVariableOptionList.map(option=>{
                    if(option.fromDimensionId===fromDimensionId){
                        items=option.fromCodeList
                    }
                })
            }
        }else{
            //这一步判断如果左侧option变化了 是取左侧的option发生变化时存入的fromCodeList
            items=this.state[key].fromCodeList;
        }
        if(this.state.optionChangeFlag&&items.length>0){
            //如果左侧的option曾经发生变化 那么右侧的initvalue值取第一项的值
            initSubValue=items[0].id
        }
        if(items.length>0){
            return(
                <div>
                    {getFieldDecorator(`member#${key}`,{initialValue:initSubValue})(
                        <Select >
                            {
                                items.map(suboption=>{
                                    return (
                                        <Option value={suboption.id} key={suboption.id}>{suboption.name}</Option>
                                    )
                                })
                            }
                        </Select>
                    )}
                </div>
            )
        }else{
            return(
                <div>
                    {getFieldDecorator(`member#${key}`,{initialValue:''})(
                        <Select initialValue={''}></Select>
                    )}
                </div>
            )
        }
    }
    

    相关文章

      网友评论

          本文标题:使用antd的Form组件动态创建复杂Select表单

          本文链接:https://www.haomeiwen.com/subject/rpfvsftx.html