JSON的React组件的动态布局

时间:2018-11-28 14:29:15

标签: javascript reactjs

我正在编写一个反应应用程序,它是金融交易应用程序的瘦客户端UI。核心要求是应用程序必须完全动态且可配置,包括表格。具体来说,我有一些贸易录入表格,需要在服务器端定义并存储在数据库中,以便在客户端上动态呈现,但是布局很重要,并且需要能够支持多种不同格式。我见过一些采用JSON表单架构并从其中创建静态表单的库,但是它们似乎都不支持我所需的那种布局灵活性。例如,我需要支持组件的选项卡,列和行。我的问题是-有人可以建议一个可以做我要找的ReactJs库吗?如果没有,我将如何自己实施?

这是一个更具体的例子;假设我有一个通过REST调用从服务器获取的架构,如下所示:

{
title: "Securities Finance Trade Entry",
children: [
 {
   containerType: "tabs",
   children: [
      {
          title: "Common",
          children: [
             {
                containerType: "row", 
                children: [
                  {
                    input: "ComboBox",
                    label: "Trade Type",
                    options: ["Repo", "Buy/Sell", "FeeBased"]
                  },
                  {
                    input: "ComboBox",
                    label: "Direction",
                    options: ["Loan", "Borrow"]
                  }
                ]
             },
             {
               containerType: "row",
               children: [
                 {
                    containerType: "column",
                    children: [
                      {
                        containerType: "row",
                        children: [
                          {
                            input: "text",
                            label: "Book"
                          },
                          {
                            input: "text",
                            label: "Counterparty"
                          }
                        ]
                      },
                      {
                        containerType: "row",
                        children: [
                          {
                            input: "date",
                            label: "StartDate"
                          },
                          {
                            input: "date",
                            label: "EndDate"
                          }
                        ]
                      },
                      {
                        containerType: "row",
                        children: [
                          {
                            input: "text",
                            label: "Security"
                          },
                          {
                            input: "numeric",
                            label: "Quantity"
                          }
                        ]
                      }
                    ]
                 }
               ]
             }
          ]
      }
   ]
 }
]
} 

我希望渲染以下内容: enter image description here 基本上,在该架构中,仅会显示一个选项卡,但可能会有多个选项卡,每个选项卡在行和列中都包含多个子项,并且也可能包含嵌套的选项卡容器。如果我自己做出反应,我会考虑使用.map遍历json和一些if语句,以在适当的地方插入标签。但是,项目需要嵌套,所以我不知道如何渲染它,以使所选标签是动态的并且可以有子级...例如,我可以这样写: {如果(container.containerType ===“ column”){()},但随后我需要以某种方式将其余控件嵌入该标记中,我认为我不能在结尾处仅发出() ...

我考虑过的另一个选项是将上述json转换为服务器端的JSX并将其发送。在Java服务器端编写一个解析器将上述json转换为JSX文档并将其返回给客户端将是相当容易的,但是我将如何呈现它呢?有什么办法可以做类似的事情:

onComponentMount() {
   fetch(webserviceUrl + 'getJsxForForm/' + props.formName)
   .then(result => {this.setState({form : result});
}
render()  {
   return ({this.state.form});
}

但是,再次,我认为这行不通。如果我从服务器获取文档,它将以纯文本形式呈现,而实际上没有将其转换为有效的html,对吧?

那么,我有什么选择?我正在寻找可以做到这一点的现有库的建议,或者关于我提到的其他两种方法(它们会行得通吗,我怎么做?)的建议,或者其他想法。 谢谢, 特洛伊

1 个答案:

答案 0 :(得分:3)

我喜欢从某种JSON配置动态呈现页面的概念。

键将定义Components以匹配containerTypesinputs,然后通过递归函数遍历JSON配置。在您的JSON配置中,建议您在希望呈现组件的任何地方使用组件命名约定。因此,大写TabsRowColumn

这里是该功能的一个示例。注意,在每个containerType组件中,都有一个对此函数的调用,其中传递了children

看这支笔:https://codepen.io/wesleylhandy/pen/oQaExK/

示例组件:

const Container = props => {
    return (
        <div className="container">
            <h1>{props.title}</h1>
            {renderChildren(props.children)}
        </div>
    )  
 }

递归式子级渲染示例

const renderChildren = children => {

    return children ? children.map((child, ind) => {
        const newChildren = child.children ? [...child.children] : [];
        const {containerType, title, input, label, options} = child
        let key;

        if (newChildren.length) {
            key = `${containerType}-${ind}`;
            switch (containerType) {
                case "Tabs":
                    return <Tabs 
                        key={key} 
                        title={title} 
                        children={newChildren}
                    />
                case "Column":
                    return <Column 
                        key={key} 
                        title={title} 
                       children={newChildren}
                    />
                case "Row":
                      return <Row 
                      key={key} 
                      title={title} 
                      children={newChildren}
                   /> 
                default:
                    return <Common 
                        key={key} 
                        title={title} 
                        children={newChildren}
                    />    
            }
        } else {
            key=`${input}-${ind}`
            switch (input) {
                case "ComboBox":
                    return <SelectGroup
                        key={key}
                        label={label}
                        options={options}
                   />
               case "Text":
               case "Date":
               case "Numeric":
                   return <InputGroup
                       key={key}
                       label={label}
                       type={input}
                   />           
           }
       }
   }) : null
}