React antdはフォームの動的な増減を実現します

React antdはフォームの動的な増減を実現します

以前、動的フォームを記述しているときに落とし穴に遭遇しました。インデックスの添え字をキーとして使用するとバグが発生し、非常に深刻になります。

今日は記録のために記事を書く時間があります。対処方法とロジック

私は antd3 バージョンを使用しています。3 と 4 のフォームは少し異なりますが、違いはそれほど大きくないはずです。

必要:

1. 切り替えるタイプを選択し、固定テンプレートを表示する

2. 新しいフィールドを追加することで、フォームの各行を動的に増減できます。

3. 各行のフィールドに入力する必要があるかどうかを制御する

4. 編集時のパラメータのバックフィル

効果画像:

いくつかのキーコード:

React をインポートします。{ コンポーネント } から 'react' をインポートします。
'./index.less' からスタイルをインポートします。
輸入 {
  テーブル、
  ボタン、
  選択、
  ポップコンファーム、
  モーダル、
  形状、
  入力、
  無線、
  行、
  列、ツールチップ、
  アイコン、
  メッセージ、
  ページネーション、入力番号、
} 'antd' から;

const Option = Select.Option;
FormItem は Form.Item に代入されます。

id = 0 とします。

@フォームを作成します()
クラス Index は Component を拡張します {
  マーケットID = 0;
  状態 = {
    選択タイプ: ''
    orderType: 1, //記事 1 マップ 2
    タイプロード: false、
    isEdit: false、
    見た目: 偽、
    表示: 偽、
    ページサイズ: 10,
    ページ番号: 1,
    キーワード: ''、
    行: {}、
    タイプリスト: {},
    モック: {},
    マップタイプ: [{
      'フィールド名': '名前',
      'isImg': 0,
      '順序': 0,
      '備考': '名前',
    }, {
      'フィールド名': 'ラベル',
      'isImg': 0,
      '順序': 0,
      '備考': 'ラベル',
    }, {
      'フィールド名': 'lon',
      'isImg': 0,
      '順序': 0,
      '備考': '経度',
    }, {
      'フィールド名': 'lat',
      'isImg': 0,
      '順序': 0,
      '備考': '緯度',
    }],
    記事タイプ: [{
      'フィールド名': '名前',
      'isImg': 0,
      '順序': 0,
      '備考': '名前',
    }, {
      'フィールド名': 'ラベル',
      'isImg': 0,
      '順序': 0,
      '備考': 'ラベル',
    }],
  };
/**
   * 動的テーブル値に必要なデータ形式を生成します * @param values
   * @returns {[]}
   */
  createValues ​​= (値) => {
    定数行 = this.state;
    定数データ = [];
    const newValues ​​= { // 送信されたデータを格納するために新しいオブジェクトを使用します...値、
    };
    const fieldNameData = []; // fieldName 値を保存 const commentsData = []; // comments 値を保存 const isImgData = []; // isImg 値を保存 const orderData = []; // orderData 値を保存 const fieldName = RegExp(/fieldName/);
    const 注釈 = RegExp(/注釈/);
    定数isImg = RegExp(/isImg/);
    for (constキーin newValues) {
      if (フィールド名.テスト(キー)) {
        フィールド名データ.push(newValues[キー]);
      }
    }
    for (constキーin newValues) {
      if (remarks.test(key)) {
        注釈Data.push(newValues[key]);
      }
    }
    for (constキーin newValues) {
      if (isImg.test(キー)) {
        isImgData.push(newValues[キー]);
      }
    }
    for (constキーin newValues) {
      if (isImg.test(キー)) {
        orderData.push(newValues[キー]);
      }
    }
    フィールド名データ.forEach((アイテム、インデックス) => {
      データ.push({
        フィールド名: アイテム、
        備考: 備考データ[インデックス],
        isImg: isImgData[インデックス],
        順序: orderData[インデックス],
        id: row.dataType ? row.dataType.id: ''、
      });
    });
    データを返します。
  };

  ハンドルOk = e => {
    this.props.form.validateFields((err, 値) => {
      もしエラーが起きたら
        定数 row, isEdit } = this.state;
        定数パラメータ = {
          データ型: {
            名前: 値.名前、
            タイプ: 値.type、
            id: row.dataType ? row.dataType.id: ''、
          },
          タイプフィールド: [],
        };
        パラメータ typeFields = this.createValues(値);
        if (isEdit) {
          editType(params).then(res => {
            (res.code === 0)の場合{
              message.info('変更に成功しました');
              this.setState({
                表示: 偽、
                isEdit: false、
              });
              this.fetchTypeList();
              this.props.form.resetFields();
            }
          });
        } それ以外 {
          addType(params).then(res => {
            (res.code === 0)の場合{
              message.info('正常に追加されました');
              this.setState({
                表示: 偽、
                isEdit: false、
              });
              this.fetchTypeList();
              this.props.form.resetFields();
            }
          });
        }
      }
    });
  };

  lookOrEditTypeModal = (フラグ、レコード) => {
    const { articleType, mapType } = this.state;
    if (flag === 'add') { //デフォルトの記事テンプレートを追加します this.marketId = articleType.length + 1; //動的キータグの長さを設定します this.setState({
        表示: true、
        行: { typeFields: articleType },
      });
    } それ以外の場合 (フラグ === '編集') {
      this.setState({
        表示: true、
      });
      getType({ dataTypeId: record.id }).then(res => {
        (res.code === 0)の場合{
          this.marketId = res.data.typeFields.length + 1; // 動的キータグの長さを設定する this.setState({
            行: res.data、
            isEdit: フラグ === 'edit'、
          });
        }
      });
    } それ以外 {
      this.setState({
        見た目: true、
      });
      getType({ dataTypeId: record.id }).then(res => {
        (res.code === 0)の場合{
          this.setState({
            行: res.data、
          });
        }
      });
    }
  };


  onChangeType = (値) => {
    const { フォーム } = this.props;
    const { orderType、 row、 articleType、 mapType } = this.state;
    this.props.form.resetFields();

    定数パラメータ = {};
    if (value === 1) { // 記事タイプ params['typeFields'] = articleType;
      this.marketId = articleType.length + 1;
    } それ以外 {
      パラメータ['typeFields'] = mapType;
      this.marketId = mapType.length + 1;
    }
    this.setState({
      行: パラメータ、
      注文タイプ: 値、
    });
  };
//メソッドを削除します! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
  削除ファイル = k => {
    const { フォーム } = this.props;
    const keys = form.getFieldValue('keys');
    キーの長さが1の場合
      戻る;
    }
    フォーム.setFieldsValue({
      キー: keys.filter(key => key !== k),
    });
  };
// メソッドを追加します! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
  ファイルを追加 = () => {
    const { フォーム } = this.props;
    const keys = form.getFieldValue('keys');
    const nextKeys = keys.concat(this.marketId++);
    フォーム.setFieldsValue({
      キー: nextKeys、
    });
  };

  judgeIsTemplet = (データ) => {
    if (!データ) {
      false を返します。
    }
    if ((data.fieldName === 'lat') || (data.fieldName === 'lon') || (data.fieldName === 'label') || (data.fieldName === 'name')) {
      true を返します。
    }
  };
  handleValidator = (ルール、値、コールバック) => {
    もし(!val) {
      折り返し電話();
    }
    検証結果 = /^[5A-Za-z0-9-\_]+$/.test(val);
    結果が検証された場合
      callback('正しいテーブルフィールドを入力してください');
    }
    折り返し電話();
  };

  列 = [
    {
      タイトル: 'タイプ名'、
      データインデックス: '名前',
      キー: '名前'、
      幅: 500,
    },
    {
      タイトル: 「タイプ」、
      データインデックス: 'type',
      キー: 'type'、
      レンダリング: (テキスト) => {
        戻り値 text === 1 ? '記事' : 'マップ';
      },
    },
    {
      タイトル: 「オペレーション」
      データインデックス: 'アドレス',
      キー: 'アドレス'、
      レンダリング: (テキスト、レコード) => {
        <div> を返す
          <Button type='link' onClick={() => this.lookOrEditTypeModal('look', record)}>表示</Button>
          <Button type='link' onClick={() => this.lookOrEditTypeModal('edit', record)}>編集</Button>
          <Popconfirm title="削除を確認しますか?" onConfirm={() => this.deleteTypeClick(record)}>
            <Button type='link'>削除</Button>
          </ポップ確認>
        </div>;
      },
    },
  ];

  与える() {
    const { selectType、typeLoading、mock、row、isEdit、typeList、keyWord、lookVisible } = this.state;
    const { getFieldDecorator、getFieldValue } = this.props.form;
    typeFields を row.typeFields || [] とします。
    定数 initData = [];
    typeFields.forEach((item, index) =>{//実際のデータに応じてデフォルトのキー配列を設定します。initData.push(index);
    });
    getFieldDecorator('keys', { initialValue: initData }); //フォームにキー フィールドを追加し、デフォルト値を設定します。これにより、編集時に編集とバックフィルの効果が生成されます。
    const キー = getFieldValue('キー');
    const formItems = keys.map((k) => (
      <行ガター={12} キー={k} クラス名={styles.form_row}>
        <FormItem ラベル="フィールド" キー={`fieldName_${k}`}>
          {getFieldDecorator(`fieldName_${k}`, {
            初期値: row.typeFields[k] ? row.typeFields[k].fieldName : ''、
            validateTrigger: ['onChange', 'onBlur'], //子ノードの値のタイミングをチェックするrules: [{
              必須: true、
              メッセージ: '英語のフィールドを入力してください!',
            }, {
              バリデータ: this.handleValidator、
            }],
          })(<Input placeholder="英語のフィールドを入力してください" max={30} disabled={this.judgeIsTemplet(row.typeFields[k])}/>)}
        </フォーム項目>
        <FormItem label="名前" key={`remarks_${k}`}>
          {getFieldDecorator(`remarks_${k}`, {
            初期値: row.typeFields[k] ? row.typeFields[k].remarks: ''、
            検証トリガー: ['onChange', 'onBlur'],
            ルール: [{
              必須: true、
              メッセージ: '中国語の名前を入力してください!',
            }],
          })(<Input placeholder="中国語名を入力してください" disabled={this.judgeIsTemplet(row.typeFields[k])}/>)}
        </フォーム項目>
        <FormItem label="Sort" key={`order_${k}`}>
          {getFieldDecorator(`order_${k}`, {
            初期値: row.typeFields[k] ? row.typeFields[k].order : 0,
          })(<InputNumber style={{width:75}} placeholder="Sort" />)}
        </フォーム項目>
        <FormItem label="画像" キー={k}>
          {getFieldDecorator(`isImg_${k}`, {
            初期値: row.typeFields[k] ? row.typeFields[k].isImg: 0,
            ルール: [{
              必須: true、
            }],
          })(<Radio.Group が無効 = {this.judgeIsTemplet(row.typeFields[k])}>
            <ラジオ値={0}>いいえ</ラジオ>
            <ラジオ値={1}>はい</ラジオ>
          </ラジオグループ>)}
        </フォーム項目>
        {!this.judgeIsTemplet(row.typeFields[k]) ? (
          <Icon type="minus-circle" onClick={() => this.removeFile(k)} title='削除'/>
        ) : ヌル}
      </行>
    ));


    戻る (
      <div className={styles.wrap_type}>
        <モーダル
          title="タイプ管理"
          可視 = {この状態が可視}
          onOk={this.handleOk}
          onCancel = {this.handleCancel}
          幅={890}
          // クラス名={styles.modal_type}
          マスククローズ可能={false}
        >
          <フォームレイアウト='インライン'>
            <行スタイル={{ textAlign: 'center', marginBottom: 14 }}>
              <FormItem label="タイプを選択">
                {getFieldDecorator('type', {
                  初期値: row.dataType ? row.dataType.type : 1,
                  ルール: [{
                    必須: true、
                  }],
                })(<onChange={this.onChangeType}、disabled={isEdit}、style={{ width: 200 }}> を選択します。
                  <オプション値={1}>記事の種類</オプション>
                  <オプション値={2}>マップタイプ</オプション>
                  <オプション値={3} 無効={true}>ファイルタイプ</オプション>
                </選択>)}
              </フォーム項目>
              <FormItem label="タイプ名">
                {getFieldDecorator('名前', {
                  初期値: row.dataType ? row.dataType.name : ''、
                  ルール: [{
                    必須: true、
                    メッセージ: 'タイプ名を入力してください!',
                  }],
                })(<Input placeholder="タイプ名を入力してください" style={{ width: 200 }}/>)}
              </フォーム項目>
            </行>
            {フォーム項目}
            <div style={{ margin: 'auto', textAlign: 'center' }}>
              <Button icon="plus" onClick={this.addFile} style={{ marginTop: 10 }}>新しいフィールドを追加</Button>
            </div>
          </フォーム>
        </モーダル>
      </div>
    );
  }
}

デフォルトのインデックスをエクスポートします。

重要な点は、marketID を動的に追加されたキーとして設定し、その値を動的キーとして使用することです。 (配列の添え字インデックスをキーとして使用しないでください)。

これで、React antd でフォームを動的に増減する方法についての記事は終了です。React antd でフォームを動的に増減する方法についての詳細は、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • React で Antd の Form コンポーネントを使用してフォーム機能を実装する方法
  • Reactはantdフォーム割り当てを使用してポップアップボックスの操作を変更します

<<:  Linuxで中断されたシステムを呼び出す方法

>>:  MySQL デッドロックのトラブルシューティングの全プロセス記録

推薦する

React Hooks の一般的な使用シナリオ (概要)

目次1. ステートフック1. 基本的な使い方2. 更新3. 合併を実現する4. 遅延初期化状態5. ...

Ubuntu環境にAnaconda3をインストールするための完全な手順

目次Anaconda の紹介1. ダウンロード1.1 インストールパッケージを保存するフォルダを作成...

MySQL 5.7.22 バイナリパッケージのインストールとインストール不要版 Windows 設定方法

次のコードは、MySQL 5.7.22 バイナリ パッケージのインストール方法を紹介しています。具体...

Docker で MySQL マスター スレーブ レプリケーションを実装するためのサンプル コード

目次1. 概要1. 原則2. 実装3. スレーブインスタンスを作成する4. マスタースレーブ構成要約...

JavaScript で円形のプログレスバー効果を実装する

この記事では、円形のプログレスバー効果を実現するためのJavaScriptの具体的なコードを参考まで...

デザイン理論:人間中心のグリーンデザイン

「人間中心」と「グリーンデザイン」という2つの視点から考える——デザイン業界の同僚とも議論する2つの...

JavaScriptのイベントループの仕組みの分析

目次序文: 1. イベント ループとタスク キューの理由: 2. イベントループメカニズム: 3. ...

MySQL の nvl() 関数に似た ifnull() 関数についての簡単な説明

IFNULL(式1,式2) expr1 が NULL でない場合、IFNULL() は expr1 ...

DockerコンテナはホストのMySQL操作にアクセスする

背景:インターフェイスを提供する Flask プロジェクトがあり、これは Docker コンテナを使...

MySQL が my.cnf を読み込む順序の詳細

目次MySQLがmy.cnfを読み込む順序1. mysql.server の起動方法2. mysql...

HTML マークアップ言語 - テーブルタグ

123WORDPRESS.COM HTML チュートリアル セクションに戻るには、ここをクリックして...

mysql 5.7.17 winx64.zip インストールと設定方法のグラフィックチュートリアル

はじめに: Windows 10 を再インストールし、同時にファイルを整理しました。しかし、MySQ...

Web面接におけるJS事前解析と変数プロモーションの違い

目次事前分析とは何ですか?変数と関数の準備の違いvar 変数の繰り返し宣言変数と関数の昇格の優先順位...

Nginx http を https にアップグレードする手順を完了する

httpとhttpsの違いは一部のウェブサイトでは、http を開くと、安全ではないというメッセージ...