• element中elformitem属性prop踩坑


    最近负责前后端项目开发,有个需求是实现Djangorestframework(drf)+element实现动态渲染form表单,drf后端提供json,前端从json中获取form表单元素,并且绑定表单验证规则
    在el-form-item属性prop上遇到报错或者没绑定到数据,报错如下
    [Vue warn]: Error in render: "TypeError: Cannot read properties of undefined (reading 'prop')"
    element官方文档解释el-form-item的prop必须是el-form属性model的直接子属性
    后端返回数据如下

    {
        "status": "success",
        "code": 200,
        "data": {
            "form_attributes": {
                "inline": true,
                "label-width": "auto",
                "size": "small"
            },
            "form_data": {
                "name": null,
                "path": null,
                "component": null,
                "hidden": false,
                "meta": {
                    "icon": null,
                    "title": null
                },
                "pid": null
            },
            "form_item_list": [
                {
                    "type": "text",
                    "prop": "name",
                    "label": "菜单名",
                    "placeholder": "请输入菜单名",
                    "rules": [
                        {
                            "required": true,
                            "message": "请输入菜单名",
                            "trigger": "blur"
                        }
                    ]
                },
                {
                    "type": "text",
                    "prop": "path",
                    "label": "链接地址",
                    "placeholder": "'/'开头",
                    "rules": [
                        {
                            "required": true,
                            "message": "'/'开头",
                            "trigger": "blur"
                        }
                    ]
                },
                {
                    "type": "text",
                    "prop": "component",
                    "label": "组件",
                    "placeholder": "参考前端组件填写",
                    "rules": [
                        {
                            "required": true,
                            "message": "参考前端组件填写",
                            "trigger": "blur"
                        }
                    ]
                },
                {
                    "type": "switch",
                    "prop": "hidden",
                    "label": "是否隐藏",
                    "value": false
                },
                {
                    "type": "json",
                    "prop": "meta",
                    "item": [
                        {
                            "type": "text",
                            "prop": "icon",
                            "label": "图标",
                            "placeholder": "图标名字,参考前端图标",
                            "rules": [
                                {
                                    "required": true,
                                    "message": "图标名字,参考前端图标",
                                    "trigger": "blur"
                                }
                            ]
                        },
                        {
                            "type": "text",
                            "prop": "title",
                            "label": "标题",
                            "placeholder": "请输入菜单名",
                            "rules": [
                                {
                                    "required": true,
                                    "message": "请输入菜单名",
                                    "trigger": "blur"
                                }
                            ]
                        }
                    ]
                },
                {
                    "type": "select",
                    "prop": "pid",
                    "label": "父菜单",
                    "clearable": true,
                    "filterable": false,
                    "multiple": false,
                    "options": [
                        {
                            "label": "系统管理",
                            "value": 2
                        },
                        {
                            "label": "用户管理",
                            "value": 3
                        },
                        {
                            "label": "菜单管理",
                            "value": 4
                        },
                        {
                            "label": "权限管理",
                            "value": 5
                        },
                        {
                            "label": "角色管理",
                            "value": 6
                        }
                    ]
                }
            ]
        },
        "message": null
    }
    

    从上面可以看到form表单元素对应的是 form_item_list,表单提交数据是 form_data,这两个是分开,也就是el-form中model绑定是 form_data 而el-form-item遍历的是 form_item_list,注意 form_item_list 中含有嵌套类型json,对应的后端是json字段渲染表单,单独提供form_data返回字段设计是为了控制前端json内容,动态字段中过于灵活,可以随意修改json包含的字段和类型,所以返回字段由后端控制和校验

    截取前端渲染表单代码如下,注意这个时候能渲染但是rules绑定是失败的

    <!-- 添加菜单对话框 -->
        <el-dialog title="添加菜单" :visible.sync="addDialogVisible" width="50%" :close-on-click-modal="false" @close="addDialogClosed">
    
          <!-- 表单内容主体 -->
          <el-form ref="addFormRef" :model="formData" :size="formAttributes.size" :inline="formAttributes.inline" :label-width="formAttributes.labelWidth">
            <div v-for="(item, index) in formItemList" :key="index">
              <el-form-item :prop="formItemList + index + item.prop" :label="item.label" :rules="item.rules">
                <!-- text输入框 -->
                <el-input v-if="item.type==='text'" v-model="formData[item.prop]" clearable :placeholder="item.placeholder"></el-input>
                <!-- textarea输入框 -->
                <el-input v-if="item.type==='textarea'" v-model="formData[item.prop]" clearable autosize :type="textarea" :placeholder="item.placeholder"></el-input>
                <!-- 下拉框 -->
                <el-select v-if="item.type==='select'" v-model="formData[item.prop]" clearable :multiple="item.multiple">
                  <el-option v-for="op in item.options" :key="op.value" :label="op.label" :value="op.value"></el-option>
                </el-select>
                <el-switch v-if="item.type==='switch'" v-model="formData[item.prop]" :label="item.label"></el-switch>
    
                <template v-if="item.type==='json'">
                  <div v-for="(json_item, json_index) in item.item" :key="json_index">
                    <el-form-item :prop="item.item + json_index + json_item.prop" :label="json_item.label" :rules="json_item.rules" style="margin-left: -80px;">
                      <!-- text输入框 -->
                      <el-input v-if="json_item.type==='text'" v-model="formData[item.prop][json_item.prop]" clearable :placeholder="json_item.placeholder"></el-input>
                      <!-- textarea输入框 -->
                      <el-input v-if="json_item.type==='textarea'" v-model="formData[json_item.prop]" clearable autosize :type="textarea" :placeholder="json_item.placeholder"></el-input>
                      <!-- 下拉框 -->
                      <!-- <el-select v-if="item.type==='select'" v-model="formData[item.prop]" clearable :placeholder="item.label" :multiple="item.multiple" @change="item.change(formData[item.prop])"> -->
                      <el-select v-if="json_item.type==='select'" v-model="formData[json_item.prop]" clearable :multiple="json_item.multiple">
                        <el-option v-for="op in json_item.options" :key="op.value" :label="op.label" :value="op.value"></el-option>
                      </el-select>
                      <el-switch v-if="json_item.type==='switch'" v-model="formData[json_item.prop]" :label="json_item.label"></el-switch>
                    </el-form-item>
                  </div>
                </template>
    
              </el-form-item>
            </div>
          </el-form>
    
          <!-- 底部 -->
          <el-divider></el-divider>
          <span slot="footer" class="dialog-footer">
            <el-button @click="addDialogVisible = false">取消</el-button>
            <el-button type="primary" @click="addForm">确定</el-button>
          </span>
        </el-dialog>
    

    在前端渲染无法绑定到表单规则,如下图

    经过研究,从上面的数据结构可以发现,form_data 中每个元素对应是 form_item_list 中的 prop,那么有两个写法可以定位
    第一种写法如下,这种写法没那么直观

    <!-- 普通字段渲染 -->
    <el-form-item :prop="item.prop" :label="item.label" :rules="item.rules">
    <!-- json字段渲染 -->
    <el-form-item :prop="item.prop + '.' + json_item.prop" :label="json_item.label" :rules="json_item.rules" style="margin-left: -80px;">
    

    更好的第二种写法如下

    <el-form-item :prop="formItemList[index].prop" :label="item.label" :rules="item.rules">
    <el-form-item :prop="formItemList[index].prop + '.' + item.item[json_index].prop" :label="json_item.label" :rules="json_item.rules" style="margin-left: -80px;">
    

    此时能动态渲染表单和绑定表单项验证规则,如下图

    总结:上面两种写法都是为了定位到表单元素对应的表单的model直接子属性,踩坑的原因是遍历表单的列表(v-for)和表单提交数据(model)不是在一个对象下,尤其是遍历表单下,还包括嵌套表单json处理,需要注意定位json提交数据

  • 相关阅读:
    Knockout.js之初印象
    GroupBy之后加ToList和不加ToList有什么区别吗?
    值类型前加ref和out的区别
    引用类型前需要加ref?
    KEPServerEX连接SQLServer数据库操作
    nginx发布网站常用指令
    ASP.NET Core 布局 _Layout.cshtml
    ASP.NET Core-------appsettings.json文件配置与加载
    ASP.NET Core 入门程序
    ASP.NET.Core网站centerOS上部署发布
  • 原文地址:https://www.cnblogs.com/AutoSmart/p/15757085.html
Copyright © 2020-2023  润新知