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

4 principles for clean and beautiful web design

This article will discuss these 4 principles as t...

How to implement variable expression selector in Vue

Table of contents Defining the HTML structure Inp...

Detailed tutorial on installing mysql 5.7.26 on centOS7.4

MariaDB is installed by default in CentOS, which ...

A brief understanding of the three uses of standard SQL update statements

1. Environment: MySQL-5.0.41-win32 Windows XP Pro...

Generate OpenSSL certificates in Linux environment

1. Environment: CentOS7, Openssl1.1.1k. 2. Concep...

js date and time formatting method example

js date time format Convert the date and time to ...

Why do we need Map when we already have Object in JavaScript?

Table of contents 1. Don’t treat objects as Maps ...

MySQL join buffer principle

Table of contents 1. MySQL join buffer 2. JoinBuf...

Unicode signature BOM (Byte Order Mark) issue for UTF-8 files

I recently encountered a strange thing when debug...

Python MySQL database table modification and query

Python connects to MySQL to modify and query data...