繁体   English   中英

React:动态添加输入字段到表单

[英]React: dynamically add input fields to form

我为表单使用formy-react,当事件触发时,我想呈现更多选项,代码看起来像这样:

class MultipleChoice extends Component {
constructor(props) {
    super(props);

}

render() {

    return(
        <div>
           <Form>
               <div id="dynamicInput">
                   <FormInput />
               </div>
           </Form>
        </div>
    );

}
}

我有一个按钮和onClick事件,我想触发一个函数,该函数将另一个函数附加到div id“ dynamicInput”中,这可能吗?

是的,我们可以更新组件的基础数据(即stateprops )。 React之所以如此出色,原因之一是它使我们可以专注于数据而不是DOM。

假设我们有一个输入列表(以state字符串数组存储)要显示,并且当单击按钮时,我们向该列表添加一个新的输入项:

class MultipleChoice extends Component {
    constructor(props) {
        super(props);
        this.state = { inputs: ['input-0'] };
    }

    render() {
        return(
            <div>
               <Form>
                   <div id="dynamicInput">
                       {this.state.inputs.map(input => <FormInput key={input} />)}
                   </div>
               </Form>
               <button onClick={ () => this.appendInput() }>
                   CLICK ME TO ADD AN INPUT
               </button>
            </div>
        );
    }

    appendInput() {
        var newInput = `input-${this.state.inputs.length}`;
        this.setState(prevState => ({ inputs: prevState.inputs.concat([newInput]) }));
    }
}

显然,该示例不是很有用,但是希望它将向您展示如何完成所需的工作。

我没有使用formy-react,但我解决了同样的问题,在此处张贴以防有人尝试不使用formyy进行相同操作。

class ListOfQuestions extends Component {
  state = {
    questions: ['hello']
  }

  handleText = i => e => {
    let questions = [...this.state.questions]
    questions[i] = e.target.value
    this.setState({
      questions
    })
  }

  handleDelete = i => e => {
    e.preventDefault()
    let questions = [
      ...this.state.questions.slice(0, i),
      ...this.state.questions.slice(i + 1)
    ]
    this.setState({
      questions
    })
  }

  addQuestion = e => {
    e.preventDefault()
    let questions = this.state.questions.concat([''])
    this.setState({
      questions
    })
  }

  render() {
    return (
      <Fragment>
        {this.state.questions.map((question, index) => (
          <span key={index}>
            <input
              type="text"
              onChange={this.handleText(index)}
              value={question}
            />
            <button onClick={this.handleDelete(index)}>X</button>
          </span>
        ))}
        <button onClick={this.addQuestion}>Add New Question</button>
      </Fragment>
    )
  }
}

以下是此的完整解决方案

    var OnlineEstimate = React.createClass({
    getInitialState: function() {
        return {inputs:[0,1]};
    },
    handleSubmit: function(e) {
        e.preventDefault();
        console.log( this.refs );
        return false;

    },
    appendInput: function(e) {
        e.preventDefault();
        var newInput = this.state.inputs.length;

        this.setState({ inputs: this.state.inputs.concat(newInput)},function(){
            return;
        });

        $('.online-est').next('.room-form').remove()

    },
    render: function() {
        var style = {
            color: 'green'
        };
        return(
                <div className="room-main">
                    <div className="online-est">
                        <h2 className="room-head">Room Details
                            <button onClick={this.handleSubmit} className="rednew-btn"><i className="fa fa-plus-circle"></i> Save All</button>&nbsp;
                            <a href="javascript:void(0);" onClick={this.appendInput} className="rednew-btn"><i className="fa fa-plus-circle"></i> Add Room</a>
                        </h2>

                       {this.state.inputs.map(function(item){
                            return (
                                    <div className="room-form" key={item} id={item}>
                                        {item}
                                        <a href="" className="remove"><i className="fa fa-remove"></i></a>
                                        <ul>
                                            <li>
                                                <label>Name <span className="red">*</span></label>
                                                <input type="text" ref={'name'+item} defaultValue={item} />
                                            </li>

                                        </ul>
                                    </div>
                            )

                       })}
                    </div>
                </div>

        );
    }
   });

这是现代动态解决方案的工作原理,具体取决于json文件,通过使用React Hooks重用Input组件。 外观如下:

在此处输入图片说明

使用这种范例的好处 :输入组件(具有其自身的挂钩状态)可以在任何其他应用程序部分中重用,而无需更改代码的任何行。

缺点更加复杂。 这是简化的json(用于构建基于的组件):

{
    "fields": [
        {
            "id": "titleDescription",
            "label": "Description",
            "template": [
                {
                    "input": {
                        "required": "true",
                        "type": "text",
                        "disabled": "false",
                        "name": "Item Description",
                        "value": "",
                        "defaultValue": "a default description",
                        "placeholder": "write your initail description",
                        "pattern": "[A-Za-z]{3}"
                    }
                }
            ]
        },
        {
            "id": "requestedDate",
            "label": "Requested Date",
            "template": [
                {
                    "input": {
                        "type": "date",
                        "name": "Item Description",
                        "value": "10-14-2007"
                    }
                }
            ]
        },
        {
            "id": "tieLine",
            "label": "Tie Line #",
            "template": [
                {
                    "select": {
                        "required": true,
                        "styles": ""
                    },
                    "options": [
                        "TL625B",
                        "TL626B-$selected",
                        "TL627B",
                        "TL628B"
                    ]
                }
            ]
        }
    ]
}

带钩子的无状态输入组件,可以读取不同的输入类型,例如:文本,数字,日期,密码等。

import React, { forwardRef } from 'react';

import useInputState from '../Hooks/InputStateHolder';

const Input = ({ parsedConfig, className }, ref) => {
  const inputState = useInputState(parsedConfig);
  return (
    <input
      //the reference to return to parent
      ref={ref}
      //we pass through the input attributes and rewrite the boolean attrs
      {...inputState.config.attrs}
      required={inputState.parseAttributeValue(inputState.config, 'required')}
      disabled={inputState.parseAttributeValue(inputState.config, 'disabled')}
      className={`m-1 p-1 border bd-light rounded custom-height ${className}`}
      onChange={inputState.onChange}
    />
  )
};
//we connect this separated component to passing ref
export default forwardRef(Input)

挂钩持有人InputStateHolder.js文件

import { useState } from 'react';

const useInputState = (initialValue) => {
  //it stores read the json, proccess it, 
  //applies modifies and stores input values
  const [config, setInputConfig] = useState({
    isLoaded: false,
    attrs: { ...initialValue }
  });

  //mutating and storing input values
  function changeValue(e) {
    const updatedConfig = { ...config };
    updatedConfig.attrs.value = e.target.value;
    setInputConfig({ ...config })
  }
  // to apply form configs to input element 
  //only one time at the first load
  function checkTheFirstLoad() {
    const updatedConfig = { ...config };
    if (config.attrs.value.length === 0) {
      updatedConfig.attrs.value = config.attrs.defaultValue;
      //defaultValue is not allowed to pass as attribute in React
      //so we apply its value depending on the conditions and remove it
      delete updatedConfig.attrs.defaultValue;
      updatedConfig.isLoaded = true;
      setInputConfig(updatedConfig);
    }
  }
  //parsing boolean input attributs such as required or disabled
  function parseAttributeValue(newState, attribute) {
    return typeof newState.attrs[attribute] === 'string' && newState.attrs[attribute] === 'true'
      ? true : false
  }

  !config.isLoaded && checkTheFirstLoad();

  //returning the hook storage 
  return {
    config,
    onChange: changeValue,
    parseAttributeValue
  }
}

export default useInputState;

父FormFields组件(包含表单和提交标签):

import React, { createElement } from "react";

import Input from '../UI/Input';

const FormField = ({ setConfig }) => {
  //it receives the parsed json and check to not be empty
  if (!!Object.keys(setConfig).length) {
    const fieldsConfig = setConfig.fields;
    //the array to get created elements in
    const fieldsToGetBuilt = [];
    // the array to store input refs for created elements
    const inputRefs = [];
    // the function to store new ref
    const setRef = (ref) => inputRefs.push(ref);
    fieldsConfig.map(field => {
      switch (true) {
        //here is we create children depending on the form configs
        case (!!field.template[0].input): {
          let classes = 'someStyle';
          fieldsToGetBuilt.push(
            createElement(Input, {
              ref: setRef,
              parsedConfig: field.template[0].input,
              key: field.id,
              className: classes
            })
          );
          break
        }
        //default case needed to build warning div notifying the missed tag
        default: {
          let classes = 'someOther danger style';
          let child = `<${Object.keys(field.template[0])[0]}/> not built`;
          fieldsToGetBuilt.push(
            createElement('div', {
              key: field.id,
              className: classes
            }, child)
          );
        }
      }
    })

    const onSubmitHandler = (e) => {
      //every time we click on submit button 
      //we receive the inputs`es values in console
      e.preventDefault();
      inputRefs.map(e =>
        console.log(e.value)
      )
    }

    return (
      <div className='m-2 d-flex flex-column'>
        <form onSubmit={onSubmitHandler}>
          <h5 className='text-center'>{setConfig.title}</h5>
          <div className='d-flex flex-row justify-content-center align-items-center'>
            {fieldsToGetBuilt.map(e => e)}
          </div>
          <input type="submit" onClick={onSubmitHandler} className='btn-info' />
        </form>
      </div >
    )
  } 
  // if in json there are no any fields to get built
  else return <div>no Page has been built</div>
};

export default FormField;

结果在这里

在此处输入图片说明 以及更改输入字段并单击“提交”按钮后在控制台中看到的内容

在此处输入图片说明

PS在我的另一个答案中,我基于json实现了动态模块上传

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM