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

时间:2016-04-09 04:14:40

标签: javascript reactjs

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

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

}

render() {

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

}
}

我有一个按钮和onClick事件我想触发一个函数,将另一个函数追加到div id&#34; dynamicInput&#34;,是否可能?

3 个答案:

答案 0 :(得分:2)

我没有使用formy-react但是我解决了同样的问题,发布在这里,以防有人试图做同样的事情而没有形式。

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>
    )
  }
}

答案 1 :(得分:0)

以下是此

的完整解决方案
    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>

        );
    }
   });

答案 2 :(得分:0)

这是一种现代的动态解决方案,其工作方式是根据json文件将输入组件与 React Hooks 重复使用。 外观如下:

enter image description here

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

缺点要复杂得多。 这是简化的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;

结果在这里

enter image description here 以及更改输入字段并单击“提交”按钮后在控制台中看到的内容

enter image description here

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