React antd realizes dynamic increase and decrease of form

React antd realizes dynamic increase and decrease of form

I encountered a pitfall when writing dynamic forms before. Using index subscript as key will cause bugs, and they are very serious!

I have time today to write down an article to record: how to deal with and logic

I'm using the antd3 version, the forms for 3 and 4 are a little different, but the difference shouldn't be that big.

need:

1. Select the type to switch and display the fixed template

2. By adding new fields, you can dynamically increase or decrease each row in the form

3. Control whether the fields in each row need to be filled in

4. Backfill parameters when editing

Effect picture:

Some key codes:

import React, { Component } from 'react';
import styles from './index.less';
import {
  Table,
  Button,
  Select,
  Popconfirm,
  Modal,
  Form,
  Input,
  Radio,
  Row,
  Col, Tooltip,
  Icon,
  message,
  Pagination, InputNumber,
} from 'antd';

const Option = Select.Option;
const FormItem = Form.Item;

let id = 0;

@Form.create()
class Index extends Component {
  marketId = 0;
  state = {
    selectType: '',
    orderType: 1, //Article 1 Map 2
    typeLoading: false,
    isEdit: false,
    lookVisible: false,
    visible: false,
    pageSize: 10,
    pageNum: 1,
    keyWord: '',
    row: {},
    typeList: {},
    mock: {},
    mapType: [{
      'fieldName': 'name',
      'isImg': 0,
      'order': 0,
      'remarks': 'Name',
    }, {
      'fieldName': 'label',
      'isImg': 0,
      'order': 0,
      'remarks': 'Labels',
    }, {
      'fieldName': 'lon',
      'isImg': 0,
      'order': 0,
      'remarks': 'Longitude',
    }, {
      'fieldName': 'lat',
      'isImg': 0,
      'order': 0,
      'remarks': 'Latitude',
    }],
    articleType: [{
      'fieldName': 'name',
      'isImg': 0,
      'order': 0,
      'remarks': 'Name',
    }, {
      'fieldName': 'label',
      'isImg': 0,
      'order': 0,
      'remarks': 'Labels',
    }],
  };
/**
   * Generate the required data format for the dynamic table value * @param values
   * @returns {[]}
   */
  createValues ​​= (values) => {
    const { row } = this.state;
    const data = [];
    const newValues ​​= { // Use a new object to carry the submitted data...values,
    };
    const fieldNameData = []; // Save fieldName value const remarksData = []; // Save remarks value const isImgData = []; // Save isImg value const orderData = []; // Save orderData value const fieldName = RegExp(/fieldName/);
    const remarks = RegExp(/remarks/);
    const isImg = RegExp(/isImg/);
    for (const key in newValues) {
      if (fieldName.test(key)) {
        fieldNameData.push(newValues[key]);
      }
    }
    for (const key in newValues) {
      if (remarks.test(key)) {
        remarksData.push(newValues[key]);
      }
    }
    for (const key in newValues) {
      if (isImg.test(key)) {
        isImgData.push(newValues[key]);
      }
    }
    for (const key in newValues) {
      if (isImg.test(key)) {
        orderData.push(newValues[key]);
      }
    }
    fieldNameData.forEach((item, index) => {
      data.push({
        fieldName: item,
        remarks: remarksData[index],
        isImg: isImgData[index],
        order: orderData[index],
        id: row.dataType ? row.dataType.id : '',
      });
    });
    return data;
  };

  handleOk = e => {
    this.props.form.validateFields((err, values) => {
      if (!err) {
        const { row, isEdit } = this.state;
        const params = {
          dataType: {
            name: values.name,
            type: values.type,
            id: row.dataType ? row.dataType.id : '',
          },
          typeFields: [],
        };
        params.typeFields = this.createValues(values);
        if (isEdit) {
          editType(params).then(res => {
            if (res.code === 0) {
              message.info('Modification successful');
              this.setState({
                visible: false,
                isEdit: false,
              });
              this.fetchTypeList();
              this.props.form.resetFields();
            }
          });
        } else {
          addType(params).then(res => {
            if (res.code === 0) {
              message.info('Added successfully');
              this.setState({
                visible: false,
                isEdit: false,
              });
              this.fetchTypeList();
              this.props.form.resetFields();
            }
          });
        }
      }
    });
  };

  lookOrEditTypeModal = (flag, record) => {
    const { articleType, mapType } = this.state;
    if (flag === 'add') { //Add the default article template this.marketId = articleType.length + 1; //Set the dynamic key tag length this.setState({
        visible: true,
        row: { typeFields: articleType },
      });
    } else if (flag === 'edit') {
      this.setState({
        visible: true,
      });
      getType({ dataTypeId: record.id }).then(res => {
        if (res.code === 0) {
          this.marketId = res.data.typeFields.length + 1; //Set the dynamic key tag length this.setState({
            row: res.data,
            isEdit: flag === 'edit',
          });
        }
      });
    } else {
      this.setState({
        lookVisible: true,
      });
      getType({ dataTypeId: record.id }).then(res => {
        if (res.code === 0) {
          this.setState({
            row: res.data,
          });
        }
      });
    }
  };


  onChangeType = (value) => {
    const { form } = this.props;
    const { orderType, row, articleType, mapType } = this.state;
    this.props.form.resetFields();

    const params = {};
    if (value === 1) { //Article type params['typeFields'] = articleType;
      this.marketId = articleType.length + 1;
    } else {
      params['typeFields'] = mapType;
      this.marketId = mapType.length + 1;
    }
    this.setState({
      row: params,
      orderType: value,
    });
  };
//Delete method! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
  removeFile = k => {
    const { form } = this.props;
    const keys = form.getFieldValue('keys');
    if (keys.length === 1) {
      return;
    }
    form.setFieldsValue({
      keys: keys.filter(key => key !== k),
    });
  };
//Add method! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
  addFile = () => {
    const { form } = this.props;
    const keys = form.getFieldValue('keys');
    const nextKeys = keys.concat(this.marketId++);
    form.setFieldsValue({
      keys: nextKeys,
    });
  };

  judgeIsTemplet = (data) => {
    if (!data) {
      return false;
    }
    if ((data.fieldName === 'lat') || (data.fieldName === 'lon') || (data.fieldName === 'label') || (data.fieldName === 'name')) {
      return true;
    }
  };
  handleValidator = (rule, val, callback) => {
    if (!val) {
      callback();
    }
    let validateResult = /^[5A-Za-z0-9-\_]+$/.test(val);
    if (!validateResult) {
      callback('Please enter the correct table field');
    }
    callback();
  };

  columns = [
    {
      title: 'Type name',
      dataIndex: 'name',
      key: 'name',
      width: 500,
    },
    {
      title: 'Type',
      dataIndex: 'type',
      key: 'type',
      render: (text) => {
        return text === 1 ? 'Article' : 'Map';
      },
    },
    {
      title: 'Operation',
      dataIndex: 'address',
      key: 'address',
      render: (text, record) => {
        return <div>
          <Button type='link' onClick={() => this.lookOrEditTypeModal('look', record)}>View</Button>
          <Button type='link' onClick={() => this.lookOrEditTypeModal('edit', record)}>Edit</Button>
          <Popconfirm title="Confirm deletion?" onConfirm={() => this.deleteTypeClick(record)}>
            <Button type='link'>Delete</Button>
          </Popconfirm>
        </div>;
      },
    },
  ];

  render() {
    const { selectType, typeLoading, mock, row, isEdit, typeList, keyWord, lookVisible } = this.state;
    const { getFieldDecorator, getFieldValue } = this.props.form;
    let typeFields = row.typeFields || [];
    const initData = [];
    typeFields.forEach((item, index) => {//Set the default keys array according to the real data initData.push(index);
    });
    getFieldDecorator('keys', { initialValue: initData }); //Add the keys field to the form and set the default value. This will generate the effect of editing and backfilling when editing.
    const keys = getFieldValue('keys');
    const formItems = keys.map((k) => (
      <Row gutter={12} key={k} className={styles.form_row}>
        <FormItem label="field" key={`fieldName_${k}`}>
          {getFieldDecorator(`fieldName_${k}`, {
            initialValue: row.typeFields[k] ? row.typeFields[k].fieldName : '',
            validateTrigger: ['onChange', 'onBlur'], //Check the timing of child node values ​​rules: [{
              required: true,
              message: 'Please enter the English field!',
            }, {
              validator: this.handleValidator,
            }],
          })(<Input placeholder="Please enter the English field" max={30} disabled={this.judgeIsTemplet(row.typeFields[k])}/>)}
        </FormItem>
        <FormItem label="Name" key={`remarks_${k}`}>
          {getFieldDecorator(`remarks_${k}`, {
            initialValue: row.typeFields[k] ? row.typeFields[k].remarks : '',
            validateTrigger: ['onChange', 'onBlur'],
            rules: [{
              required: true,
              message: 'Please enter a Chinese name!',
            }],
          })(<Input placeholder="Please enter the Chinese name" disabled={this.judgeIsTemplet(row.typeFields[k])}/>)}
        </FormItem>
        <FormItem label="Sort" key={`order_${k}`}>
          {getFieldDecorator(`order_${k}`, {
            initialValue: row.typeFields[k] ? row.typeFields[k].order : 0,
          })(<InputNumber style={{width:75}} placeholder="Sort" />)}
        </FormItem>
        <FormItem label="Picture" key={k}>
          {getFieldDecorator(`isImg_${k}`, {
            initialValue: row.typeFields[k] ? row.typeFields[k].isImg : 0,
            rules: [{
              required: true,
            }],
          })(<Radio.Group disabled={this.judgeIsTemplet(row.typeFields[k])}>
            <Radio value={0}>No</Radio>
            <Radio value={1}>Yes</Radio>
          </Radio.Group>)}
        </FormItem>
        {!this.judgeIsTemplet(row.typeFields[k]) ? (
          <Icon type="minus-circle" onClick={() => this.removeFile(k)} title='Delete'/>
        ) : null}
      </Row>
    ));


    return (
      <div className={styles.wrap_type}>
        <Modal
          title="Type Management"
          visible = {this.state.visible}
          onOk={this.handleOk}
          onCancel = {this.handleCancel}
          width={890}
          // className={styles.modal_type}
          maskClosable={false}
        >
          <Form layout='inline'>
            <Row style={{ textAlign: 'center', marginBottom: 14 }}>
              <FormItem label="Select Type">
                {getFieldDecorator('type', {
                  initialValue: row.dataType ? row.dataType.type : 1,
                  rules: [{
                    required: true,
                  }],
                })(<Select onChange={this.onChangeType} disabled={isEdit} style={{ width: 200 }}>
                  <Option value={1}>Article type</Option>
                  <Option value={2}>Map type</Option>
                  <Option value={3} disabled={true}>File type</Option>
                </Select>)}
              </FormItem>
              <FormItem label="Type name">
                {getFieldDecorator('name', {
                  initialValue: row.dataType ? row.dataType.name : '',
                  rules: [{
                    required: true,
                    message: 'Please enter the type name!',
                  }],
                })(<Input placeholder="Please enter the type name" style={{ width: 200 }}/>)}
              </FormItem>
            </Row>
            {formItems}
            <div style={{ margin: 'auto', textAlign: 'center' }}>
              <Button icon="plus" onClick={this.addFile} style={{ marginTop: 10 }}>Add new field</Button>
            </div>
          </Form>
        </Modal>
      </div>
    );
  }
}

export default Index;

The key point is to set a marketID as the dynamically added key, and then use its value as the dynamic key. (Never use the array subscript index as the key)!

This is the end of this article about how to dynamically increase or decrease forms with react antd. For more information about how to dynamically increase or decrease forms with react antd, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • How to use Antd's Form component in React to implement form functions
  • React uses antd form assignment to modify the operation of the pop-up box

<<:  How to call the interrupted system in Linux

>>:  MySQL DeadLock troubleshooting full process record

Recommend

How to import and export Cookies and Favorites in FireFox

FireFox is a commonly used browser with many exte...

VMware15/16 Detailed steps to unlock VMware and install MacOS

VMware version: VMware-workstation-full-16 VMware...

js implements single click to modify the table

Pure js implements a single-click editable table ...

Learn MySQL execution plan

Table of contents 1. Introduction to the Implemen...

Three ways to achieve text flashing effect in CSS3 Example code

1. Change the transparency to achieve the gradual...

Web Design Experience: Efficiently Writing Web Code

Originally, this seventh chapter should be a deep ...

Implementation steps of vue-element-admin to build a backend management system

Recently, when I was working on a conference heal...

JavaScript Design Pattern Command Pattern

The command pattern is a behavioral design patter...

Detailed explanation of the difference between tags and elements in HTML

I believe that many friends who are new to web pag...

VMwarea virtual machine installation win7 operating system tutorial diagram

The installation process of VMwarea will not be d...

How to force vertical screen on mobile pages

I recently wrote a mobile page at work, which was...

HTML+CSS to achieve surround reflection loading effect

This article mainly introduces the implementation...

Example of using javascript to drag and swap div positions

1 Implementation Principle This is done using the...

JavaScript prototype and prototype chain details

Table of contents 1. prototype (explicit prototyp...