You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring Boot实现数据库驱动的动态HTML表单构建需求

实现Spring Boot动态HTML表单方案

一、数据库表优化

原表结构不足以支撑下拉选项和关联逻辑,建议调整为以下结构:

  • id:自增主键
  • display_name:表单元素显示名称
  • field_type:元素类型(text/password/select等)
  • options:下拉选项,用JSON格式存储(例:["选项A","选项B"])
  • parent_select_value:关联父下拉选中值(用来实现选择下拉后展示对应表单元素)

二、后端实现(Spring Boot)

1. 实体类(JPA)

@Entity
@Table(name = "field_elements")
public class FieldElement {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String displayName;
    private String type;
    @Column(columnDefinition = "JSON")
    private String options;
    private String parentSelectValue;

    // getter、setter、构造方法
}

2. Repository层

public interface FieldElementRepository extends JpaRepository<FieldElement, Long> {
    // 查询无父关联的初始表单元素
    List<FieldElement> findByParentSelectValueIsNull();
    // 根据父下拉值查询对应表单元素
    List<FieldElement> findByParentSelectValue(String parentValue);
}

3. Controller层

@RestController
@RequestMapping("/api/fields")
public class FieldElementController {
    @Autowired
    private FieldElementRepository fieldRepo;

    @GetMapping("/initial")
    public List<FieldElement> getInitialFields() {
        return fieldRepo.findByParentSelectValueIsNull();
    }

    @GetMapping("/by-parent")
    public List<FieldElement> getFieldsByParentValue(@RequestParam String parentValue) {
        return fieldRepo.findByParentSelectValue(parentValue);
    }
}

三、前端实现(原生JS)

无需依赖任何前端框架,用原生JS即可完成动态渲染:

1. 初始页面(index.html)

<!DOCTYPE html>
<html>
<head>
    <title>动态表单</title>
</head>
<body>
    <div id="form-container"></div>

    <script>
        window.onload = () => {
            fetch('/api/fields/initial')
                .then(res => res.json())
                .then(fields => renderFields(fields));
        };

        function renderFields(fields) {
            const container = document.getElementById('form-container');
            container.innerHTML = '';

            fields.forEach(field => {
                const fieldWrap = document.createElement('div');
                fieldWrap.innerHTML = `<label>${field.displayName}</label>`;

                switch(field.type) {
                    case 'Text':
                        const textInput = document.createElement('input');
                        textInput.type = 'text';
                        textInput.name = field.displayName.toLowerCase().replace(' ', '_');
                        fieldWrap.appendChild(textInput);
                        break;
                    case 'Password':
                        const pwdInput = document.createElement('input');
                        pwdInput.type = 'password';
                        pwdInput.name = field.displayName.toLowerCase().replace(' ', '_');
                        fieldWrap.appendChild(pwdInput);
                        break;
                    case 'Select':
                        const select = document.createElement('select');
                        select.name = field.displayName.toLowerCase().replace(' ', '_');
                        const options = JSON.parse(field.options);
                        options.forEach(opt => {
                            const option = document.createElement('option');
                            option.value = opt;
                            option.textContent = opt;
                            select.appendChild(option);
                        });
                        select.onchange = () => {
                            window.location.href = `/sub-form.html?parent=${select.value}`;
                        };
                        fieldWrap.appendChild(select);
                        break;
                }

                container.appendChild(fieldWrap);
            });
        }
    </script>
</body>
</html>

2. 跳转后的子表单页面(sub-form.html)

<!DOCTYPE html>
<html>
<head>
    <title>子表单</title>
</head>
<body>
    <div id="form-container"></div>

    <script>
        const urlParams = new URLSearchParams(window.location.search);
        const parentValue = urlParams.get('parent');

        fetch(`/api/fields/by-parent?parentValue=${parentValue}`)
            .then(res => res.json())
            .then(fields => renderFields(fields));

        function renderFields(fields) {
            const container = document.getElementById('form-container');
            fields.forEach(field => {
                const fieldWrap = document.createElement('div');
                fieldWrap.innerHTML = `<label>${field.displayName}</label>`;

                if(field.type === 'Text') {
                    const input = document.createElement('input');
                    input.type = 'text';
                    input.name = field.displayName.toLowerCase().replace(' ', '_');
                    fieldWrap.appendChild(input);
                } else if(field.type === 'Password') {
                    const input = document.createElement('input');
                    input.type = 'password';
                    input.name = field.displayName.toLowerCase().replace(' ', '_');
                    fieldWrap.appendChild(input);
                }

                container.appendChild(fieldWrap);
            });
        }
    </script>
</body>
</html>

四、核心逻辑说明

  • 数据库新增字段后,后端接口会自动查询到新元素,前端渲染时会自动生成对应表单控件,无需修改前端代码
  • 下拉选择后通过URL参数传递选中值,子页面根据参数拉取匹配的表单元素
  • 全程用原生JS实现,无额外框架依赖,上手成本低

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 05:55:31