React JS 在 json 循环上创建动态元素

问题描述 投票:0回答:1

我正在使用 React JS 构建表单创建器。现在我专注于在表单内渲染元素的过程,基本上它是一个包含其他一些 div 的 div,作为一个子元素。 我有我的主要组件(表单)和一些可以是不同组件的子组件,例如文本框或下拉列表等,具有一组通用属性和一组特定属性(例如下拉选项未插入到文本框组件)。

我还从 API 中获取数据,该 API 使用带有“type”参数(“textbox”、“dropdown”等)的 json 进行响应。

我正在 json 上循环,当然我可以检查类型,如果它是文本框,则创建一个文本框,如果它是一个下拉列表,则创建一个下拉列表...但我认为有更好的解决方案。

这是我的代码:

import {useState, useEffect} from 'react';

import axios from 'axios';
import Textbox from './Textbox';
import Dropdown from './Dropdown';

function Form(){

const [loading, setLoading] = useState(true);
const [components, setComponents] = useState([]);

useEffect(() => {
    if(loading){
        fetchComponents()
    }
    
});


const fetchComponents = () => {
    axios({
        method: 'get',
        url: 'http://localhost:8000/api/position'
    })
    .then(res => {      
        setComponents(res.data);
        setLoading(false);
    });
}

const allItems = () => {

    return( 
        components.map((component, key) => {
            /** THIS IS WHERE I WANT TO CREATE THE ELEMENT DYNAMICALLY */
            const  options = JSON.parse(component.options);
            if(component.type === 'textbox'){
                return <Textbox 
                            key={component.id}
                            id={ component.id}  
                            name={component.name} 
                            default={{
                                x: options.position.x,
                                y: options.position.y,
                                width: options.size.width,
                                height: options.size.height
                            }}
                            minWidth={100}
                            minHeight={100}
                            resizeGrid={[20, 20]}
                            dragGrid={[20, 20]}
                            label={component.label}
                            value={component.value}
                            bounds="window"
                        />
            }
            if(component.type === 'dropdown'){
                return <Dropdown 
                            key={component.id}
                            id={ component.id}  
                            name={component.name} 
                            default={{
                                x: options.position.x,
                                y: options.position.y,
                                width: options.size.width,
                                height: options.size.height
                            }}
                            minWidth={100}
                            minHeight={100}
                            resizeGrid={[20, 20]}
                            dragGrid={[20, 20]}
                            label={component.label}
                            value={component.value}
                            dropdown_options={component.dropdown_options}
                            bounds="window"
            />
            }

        })
    )
}


return(
    <div>
          
    
    <div className="box background-quaderno" style={{height: '500px', width: '500px', position: 'relative', overflow: 'auto', padding: '0'}}>
      <div style={{height: '100%', width: '100%', padding: '0px', overflow: 'hidden'}}>

        {loading ? <></> : allItems()}
        
      </div>
    </div>

      </div>
    )
}

export default Form;

谢谢!

javascript json reactjs components
1个回答
0
投票

作为示例,使用 react-declarative 源代码。它支持使用递归渲染嵌套的字段组

import { TypedField, FieldType } from "react-declarative";

const CC_FULL_HEIGHT = "calc(100vh - 80px)";

declare var MonthProgressWrapper;
declare var TimeLossWrapper;
declare var IndicatorProgressWrapper;
declare var IndicatorWaitingWrapper;
declare var IndicatorArchiveWrapper;
declare var IndicatorDoneWrapper;
declare var IndicatorAllWrapper;

export const fields: TypedField[] = [
  {
    type: FieldType.Group,
    fields: [
      {
        type: FieldType.Group,
        fields: [
          {
            type: FieldType.Group,
            columnsOverride: '5',
            fields: [
              {
                type: FieldType.Hero,
                height: `calc(${CC_FULL_HEIGHT} / 3)`,
                columns: '1',
                phoneColumns: '5',
                right: '10px',
                phoneRight: "0px",
                bottom: '10px',
                minHeight: '225px',
                child: {
                  type: FieldType.Component,
                  element: IndicatorArchiveWrapper,
                },
              },
              {
                type: FieldType.Hero,
                height: `calc(${CC_FULL_HEIGHT} / 3)`,
                columns: '1',
                phoneColumns: '5',
                right: '10px',
                phoneRight: "0px",
                bottom: '10px',
                minHeight: '225px',
                child: {
                  type: FieldType.Component,
                  element: IndicatorWaitingWrapper,
                },
              },
              {
                type: FieldType.Hero,
                height: `calc(${CC_FULL_HEIGHT} / 3)`,
                columns: '1',
                phoneColumns: '5',
                right: '10px',
                phoneRight: "0px",
                bottom: '10px',
                minHeight: '225px',
                child: {
                  type: FieldType.Component,
                  element: IndicatorProgressWrapper,
                },
              },
              {
                type: FieldType.Hero,
                height: `calc(${CC_FULL_HEIGHT} / 3)`,
                columns: '1',
                phoneColumns: '5',
                right: '10px',
                phoneRight: "0px",
                bottom: '10px',
                minHeight: '225px',
                child: {
                  type: FieldType.Component,
                  element: IndicatorDoneWrapper,
                },
              },
              {
                type: FieldType.Hero,
                height: `calc(${CC_FULL_HEIGHT} / 3)`,
                columns: '1',
                phoneColumns: '5',
                bottom: '10px',
                minHeight: '225px',
                child: {
                  type: FieldType.Component,
                  element: IndicatorAllWrapper,
                },
              },
            ]
          },
          {
            type: FieldType.Hero,
            height: `calc(${CC_FULL_HEIGHT} / 3 * 2)`,
            columns: '6',
            phoneColumns: '12',
            phoneBottom: '10px',
            tabletBottom: '10px',
            right: '10px',
            phoneRight: "0px",
            minHeight: '400px',
            child: {
              type: FieldType.Component,
              element: MonthProgressWrapper,
            },
          },
          {
            type: FieldType.Hero,
            height: `calc(${CC_FULL_HEIGHT} / 3 * 2)`,
            columns: '6',
            phoneColumns: '12',
            phoneBottom: '10px',
            tabletBottom: '10px',
            minHeight: '400px',
            child: {
              type: FieldType.Component,
              element: TimeLossWrapper,
            },
          },
        ]
      }
    ]
  },
  {
    type: FieldType.Group,
    fieldBottomMargin: "0",
    fieldRightMargin: "0",
    columnsOverride: '5',
    fields: [
      {
        type: FieldType.Group,
        fieldBottomMargin: "0",
        columns: "1",
        phoneColumns: "5",
        tabletColumns: "5",
        fields: [
          {
            type: FieldType.Line,
            lineTransparent: true,
            title: "Archive",
          },
          {
            type: FieldType.Slider,
            name: "archive_count",
            minSlider: 0,
            maxSlider: 500,
            defaultValue: 50,
          },
          {
            type: FieldType.Combo,
            name: "archive_limit_bg",
            title: 'Archive limit',
            itemList: [
              "semi",
              "solid",
              "unset"
            ],
            defaultValue: "semi",
            tr: async (entry) => {
              const trMap = {
                "semi": "Semi",
                "solid": "Solid",
                "unset": "Unset",
              }
              return trMap[entry] || entry;
            },
            noDeselect: true,
          },
          {
            type: FieldType.Text,
            name: "archive_low_limit",
            defaultValue: "100",
            title: 'Lower archive limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "archive_medium_limit",
            defaultValue: "200",
            title: 'Average archive limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "archive_high_limit",
            defaultValue: "300",
            title: 'Upper archive limit',
            fieldBottomMargin: "0",
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
        ],
      },
      {
        type: FieldType.Group,
        fieldBottomMargin: "0",
        columns: "1",
        phoneColumns: "5",
        tabletColumns: "5",
        fields: [
          {
            type: FieldType.Line,
            lineTransparent: true,
            title: "Waiting",
          },
          {
            type: FieldType.Slider,
            name: "waiting_count",
            minSlider: 0,
            maxSlider: 500,
            defaultValue: 50,
          },
          {
            type: FieldType.Combo,
            name: "waiting_limit_bg",
            title: 'Waiting limit',
            defaultValue: "semi",
            itemList: [
              "semi",
              "solid",
              "unset"
            ],
            tr: async (entry) => {
              const trMap = {
                "semi": "Semi",
                "solid": "Solid",
                "unset": "Unset",
              }
              return trMap[entry] || entry;
            },
            noDeselect: true,
          },
          {
            type: FieldType.Text,
            name: "waiting_low_limit",
            defaultValue: "100",
            title: 'Lower expectation limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "waiting_medium_limit",
            defaultValue: "200",
            title: 'Average expectation limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "waiting_high_limit",
            defaultValue: "300",
            title: 'Upper Expectation Limit',
            fieldBottomMargin: "0",
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
        ],
      },
      {
        type: FieldType.Group,
        fieldBottomMargin: "0",
        columns: "1",
        phoneColumns: "5",
        tabletColumns: "5",
        fields: [
          {
            type: FieldType.Line,
            lineTransparent: true,
            title: "Progress",
          },
          {
            type: FieldType.Slider,
            name: "inprogress_count",
            minSlider: 0,
            maxSlider: 500,
            defaultValue: 50,
          },
          {
            type: FieldType.Combo,
            name: "inprogress_limit_bg",
            title: 'Progress limit',
            defaultValue: "semi",
            itemList: [
              "semi",
              "solid",
              "unset",
            ],
            tr: async (entry) => {
              const trMap = {
                "semi": "Semi",
                "solid": "Solid",
                "unset": "Unset",
              }
              return trMap[entry] || entry;
            },
            noDeselect: true,
          },
          {
            type: FieldType.Text,
            name: "inprogress_low_limit",
            defaultValue: "100",
            title: 'Lower progress limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "inprogress_medium_limit",
            defaultValue: "200",
            title: 'Average progress limit',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "inprogress_high_limit",
            defaultValue: "300",
            title: 'Upper progress limit',
            fieldBottomMargin: "0",
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "000000000000000",
          },
        ]
      },
      {
        type: FieldType.Group,
        fieldBottomMargin: "0",
        columns: "1",
        phoneColumns: "5",
        tabletColumns: "5",
        fields: [
          {
            type: FieldType.Line,
            lineTransparent: true,
            title: "Done",
          },
          {
            type: FieldType.Slider,
            name: "done_count",
            minSlider: 0,
            maxSlider: 500,
            defaultValue: 50,
          },
          {
            type: FieldType.Combo,
            name: "done_limit_bg",
            title: 'Execution limit',
            defaultValue: "semi",
            itemList: [
              "semi",
              "solid",
              "unset",
            ],
            tr: async (entry) => {
              const trMap = {
                "semi": "Semi",
                "solid": "Solid",
                "unset": "Unset",
              }
              return trMap[entry] || entry;
            },
            noDeselect: true,
          },
          {
            type: FieldType.Text,
            name: "done_low_limit",
            title: 'Lower Execution Limit',
            defaultValue: '100',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "done_medium_limit",
            title: 'Average Execution Limit',
            defaultValue: '200',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            fieldBottomMargin: "0",
            name: "done_high_limit",
            title: 'Upper Execution Limit',
            defaultValue: '300',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
        ],
      },
      {
        type: FieldType.Group,
        fieldBottomMargin: "0",
        columns: "1",
        phoneColumns: "5",
        tabletColumns: "5",
        fields: [
          {
            type: FieldType.Line,
            lineTransparent: true,
            title: "Total",
          },
          {
            type: FieldType.Slider,
            name: "all_count",
            minSlider: 0,
            maxSlider: 500,
            defaultValue: 50,
          },
          {
            type: FieldType.Combo,
            name: "all_limit_bg",
            title: 'Limit of all',
            defaultValue: "semi",
            itemList: [
              "semi",
              "solid",
              "unset",
            ],
            tr: async (entry) => {
              const trMap = {
                "semi": "Semi",
                "solid": "Solid",
                "unset": "Unset",
              }
              return trMap[entry] || entry;
            },
            noDeselect: true,
          },
          {
            type: FieldType.Text,
            name: "all_low_limit",
            title: "Lower limit of all",
            defaultValue: '100',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "all_medium_limit",
            title: 'Average limit of all',
            defaultValue: '200',
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
          {
            type: FieldType.Text,
            name: "all_high_limit",
            title: 'Upper limit of all',
            defaultValue: '300',
            fieldBottomMargin: "0",
            inputFormatterAllowed: /^[0-9]/,
            inputFormatterTemplate: "0000000000000000",
          },
        ],
      },
    ]
  },
  {
    type: FieldType.Group,
    desktopColumns: "6",
    tabletColumns: "12",
    phoneColumns: "12",
    fields: [
      {
        type: FieldType.Line,
        lineTransparent: true,
        title: "Monthly progress",
      },
      {
        type: FieldType.Slider,
        name: "monthprogress_count",
        minSlider: 0,
        maxSlider: 500,
        defaultValue: 50,
      },
      {
        type: FieldType.Text,
        name: "monthprogress_low_limit",
        defaultValue: '100',
        title: 'Lower limit of monthly progress',
        inputFormatterAllowed: /^[0-9]/,
        inputFormatterTemplate: "0000000000000000",
      },
      {
        type: FieldType.Text,
        name: "monthprogress_medium_limit",
        title: 'Average progress limit per month',
        defaultValue: '200',
        inputFormatterAllowed: /^[0-9]/,
        inputFormatterTemplate: "0000000000000000",
      },
      {
        type: FieldType.Text,
        name: "monthprogress_high_limit",
        title: 'Upper limit of monthly progress',
        defaultValue: '300',
        inputFormatterAllowed: /^[0-9]/,
        inputFormatterTemplate: "0000000000000000",
      },
    ],
  },
  {
    type: FieldType.Component,
    element: ({ _fieldData: data }) => <pre>{JSON.stringify(data, null, 2)}</pre>,
  }
]

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.