在ReactJS中有条件地显示/隐藏自动生成的表单元素

时间:2017-08-24 00:27:00

标签: javascript reactjs react-dom

我有一个ReactJS应用程序

A)读取描述表单结构的JSON输入

B)动态从此JSON输入生成表单(使用document.createElement(..))

JSON看起来像这样:

{
   formElements: [
      {
          id: “dd1”, 
          type: “dropdown”,
          options: [ {value: “first”}, {value: “second”}]
      },
      {
          id: “tf1”, 
          type: “textfield”,
          showIf: “dd1 == ‘second’”
      }
   ]
}

现在棘手的是,JSON输入文件不仅描述了哪些表单元素(例如下拉列表,单选按钮组,文本字段)等,而且还描述了每个元素的显示/隐藏逻辑。例如,如果选择了特定的下拉列表,则应显示文本字段(否则应保持隐藏状态)。

这通常是在jQuery中完成的,但我听说jQuery对React不是一个好主意。

如果这些是硬编码的表单元素,我可以轻松编写此显示/隐藏逻辑。问题是表单元素是动态生成的(通过读取该JSON文件),我需要动态地将这个show / hide逻辑应用于这些自动生成的表单元素。

我不知道该怎么做。

如果有人对这里的方法提出建议,特别是举例,那将非常感激。谢谢!

1 个答案:

答案 0 :(得分:0)

您仍然可以将条件呈现逻辑应用于生成表单的JSX代码,但是您是否考虑过使用现有的表单库(如react-form或redux-forms)?如果你的反应比较新,那么这将是一个更容易获得你想要的结果的途径。我不能推荐一个特定的表单库,但是它会处理动态数据的反应表单。

这是我如何在不使用redux或内置表单库的情况下管理它的粗略草图。这是一个想象但从未执行过的草稿,所以把它当作伪代码对待它并且绝对没有优化:

    //import base form components up here (input, checkbox, etc)
    // Map the strings in your field object to the component imported or defined above

    const fieldSelector = {
      input : Input,
      textarea: TextArea,
      checkbox: CheckBox
    }

    Class CustomForm extends React.Component {
      constructor(props) {
        super(props);
        const fields = {}
        const byId = []

        // Note if there is any asynchronous data, you might want to put this logic
        // in componentDidMount
        // Create an array with each 'id'
        const byId = this.props.formData.map( item => item.id );
        // Create a map object listing each field by its id 
        this.props.formData.forEach( (field) => {
          fields[field.id] = field;
        }

        this.state = {
          fields, 
          byId,
        }

        this.handleChange = this.handleChange.bind(this);
        this.checkVisibility = this.checkVisibility.bind(this);
      }

      // Need to add some additional logic if you're using checkboxes
      // Creates an event handler for each type of field
      handleChange(id) {
        return (event) => {
          const updatedFields = {...this.state.fields};
          updatedFields[id].value = event.target.value
          this.state.byId.forEach( fieldId => {
            updatedFields[fieldId].visible = checkVisibility(updatedFields, fieldId)};
          }  
          this.setState({fields: updatedFields})
        }
      }

      // You can either restructure your showIf or include a function above
      // to parse our the elements of the expression. 
      checkVisibility(updatedFields, fieldId) {
        const field = updatedFields[fieldId];
        const showIfId = field.showIf.triggerFieldId;
        const showIfValue = field.showIf.value;
        const operator = field.showIf.operator;
        switch(operator){
          case '===':
            return updatedFields[showIfId].value === ShowIfValue;
          case '<':
            return updatedFields[showIfId].value < ShowIfValue;
          //...fill in rest of operators here
          default: 
            return field.visible;
        }
      }

      render() {
        return this.state.byId.map( fieldId => {
          const field = this.state.fields[fieldId];
          const CustomField = FieldSelector[field.type]
          return (
            {field.visible && 
              <CustomField {insert whatever props from field} /> 
            }
          );
        });
      }
    }

    export default CustomForm