最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 数据驱动的公共表单组件

    正文概述 掘金(道格温_胖虎)   2020-11-29   735

    问题背景

    以前刚用element-ui的时候,新增表单,对于每个form-item都是写死的,form-item里面的内容也是写死的,比如下面

    <el-form  label-width="80px" :model="formLabelAlign">
      <el-form-item label="名称">
        <el-input v-model="formLabelAlign.name"></el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-input v-model="formLabelAlign.region"></el-input>
      </el-form-item>
      <el-form-item label="活动形式">
        <el-input v-model="formLabelAlign.type"></el-input>
      </el-form-item>
      ....
    </el-form>
    

    这样写有个问题,就是每次新增一个form-item都要手动复制一次,然后修改里面的label、propel-input绑定的v-model,而且for-item越多.vue就越大,文件就越臃肿

    初代解决方案

    熟练了以后,针对上面的例子,发现el-form-item可以通过v-for循环生成,因为每个form-item的结构和分发的内容都是一样的,于是自然想到了下面的解决方案:

    <template>
      <el-form  :model="formLabelAlign">
        <el-form-item 
            v-for="item in FORM_ITEMS" 
            :key="item.prop" 
            :label="item.label"
            :prop="item.prop"
            :label-width="item.lableWidth"
        >
          <el-input v-model="formLabelAlign[prop]"></el-input>
        </el-form-item>
      </el-form>
    </template>
    <script>
    export default {
        name: 'CommonForm',
        props: {
            formLabelAlign: {
                type: Object,
                require: true,
                defalut: () => ({})
            }
        },
        data() {
            return {
                FORM_ITEMS: [
                    {prop: 'name', label: '名称', lableWidth: 100 },
                    {prop: 'region', label: '活动区域', lableWidth: 100 },
                    {prop: 'type', label: '活动形式', lableWidth: 100 },
                ]
            }
        },
    }
    </script>
    

    上面通过data声明在FORM_ITEMS,每个item定义了一些想要的属性label、prop等等,循环生成我们想要form-item,这样一来我们的template并不需求去修改,每次新增只要在FORM_ITEMS新增一条数据即可

    更多的场景

    上面的例子适用的场景很单一,限制了我们里面的form-item的内容只是input,问题我们日常的业务场景表单包含了许多类型,比如下拉框,多选框,文本域,单选框,多选框,上传等等,那么我们上面的封装明显已经不适用

    针对不同类型的form-item内容的解决方案

    针对上面说的更多场景,我们很自然可以想到通过枚举去实现不同类型form-item的内容,像下面这样:

    公共表单第二版

    <template>
      <el-form :model="formLabelAlign">
        <el-form-item
          v-for="item in FORM_ITEMS"
          :key="item.prop"
          :label="item.label"
          :prop="item.prop"
        >
          <el-input
            v-if="item.el === 'input'"
            :placeholder="item.placeholder"
            v-model="formLabelAlign[item.prop]"
          ></el-input>
    
          <el-input
            v-if="item.el === 'textarea'"
            type="textarea"
            :rows="item.row || 5"
            :placeholder="item.placeholder"
            v-model="formLabelAlign[item.prop]"
          >
          </el-input>
    
          <el-switch
            v-if="item.el === 'switch'"
            v-model="formLabelAlign[item.prop]"
          ></el-switch>
    
          <el-slider
            v-if="item.el === 'slider'"
            v-model="formLabelAlign[item.prop]"
          ></el-slider>
    
          <el-select v-if="item.el === 'select'">
            <el-option
              v-for="option in item.options"
              :key="option.label"
              :label="option.label"
              :value="option.value"
            ></el-option>
          </el-select>
    
          <el-radio-group
            v-if="item.el === 'radio'"
            v-model="formLabelAlign[item.prop]"
          >
            <el-radio
              v-for="radio in item.radios"
              :key="radio.label"
              :label="radio.label"
              v-mode="radio.value"
              >{{ radio.label }}</el-radio
            >
          </el-radio-group>
          ...等等等
        </el-form-item>
      </el-form>
    </template>
    <script>
    export default {
      name: "CommonForm",
      props: {
        formLabelAlign: {
          type: Object,
          require: true,
          defalut: () => ({}),
        },
      },
      data() {
        return {
          FORM_ITEMS: [
            { prop: "name", label: "名称", lableWidth: 100, el: "input" },
            { prop: "region", label: "活动区域", lableWidth: 100, el: "input" },
            { prop: "type", label: "活动形式", lableWidth: 100, el: "input" },
            {
              prop: "info",
              label: "信息介绍",
              lableWidth: 100,
              el: "textarea",
              row: 3,
            },
            { prop: "switch", label: "是否启动", lableWidth: 100, el: "switch" },
            { prop: "percent", label: "比例", lableWidth: 100, el: "slider" },
            {
              prop: "types",
              label: "类型选择",
              lableWidth: 100,
              el: "radio",
              radios: [
                { value: 0, label: "yes" },
                { value: 1, label: "no" },
              ],
            },
            {
              prop: "city",
              label: "城市选择",
              lableWidth: 100,
              el: "radio",
              radios: [
                { value: 0, label: "北京" },
                { value: 1, label: "深圳" },
                { value: 2, label: "珠海" },
              ],
            },
          ],
        };
      },
    };
    </script>
    

    简单的思路就是我们通过枚举各种可能elment-ui表单元素,通过el这个属性来决定这次要渲染是什么类型的标签元素,主要你不嫌麻烦,可以在template枚举所有的element-ui元素

    遗留的问题:

    • 真实开发不一定每个form-item里面只是一个input或者checkbox, 有可能是input和button的组合,也有可能form-item里面直接嵌套了另外一个表单

    比如下面:

      <el-form-item label="活动名称">
        <el-input v-model="form.name"></el-input>
         <el-radio-group v-model="radio">
        	<el-radio :label="3">备选项</el-radio>
        	<el-radio :label="6">备选项</el-radio>
        	<el-radio :label="9">备选项</el-radio>
         </el-radio-group>
      </el-form-item>
       <el-form-item label="活动区域">
        <el-form :model="formData">
        	....
        </el-form>
      </el-form-item>
    

    第三版公共表单组件

    上面的场景我们的上面封装的版本2已不能用了,这个时候应该怎么处理呢,很自然的我们想到了具名插槽slot,比如不适应的某个表单项, 把prop传给slot作为他的插槽名称,于是我又把公共表单改成了下面的样子:

    <template>
      <el-form :model="formLabelAlign">
        <el-form-item
          v-for="item in FORM_ITEMS"
          :key="item.prop"
          :label="item.label"
          :prop="item.prop"
        >
          <el-input
            v-if="item.el === 'input'"
            :placeholder="item.placeholder"
            v-model="formLabelAlign[item.prop]"
          ></el-input>
    
          ...上面的内容缩略
    
         <slot :slot="item.prop" />
    
        </el-form-item>
      </el-form>
    </template>
    <script>
    export default {
      name: "CommonForm",
      props: {
        formLabelAlign: {
          type: Object,
          require: true,
          defalut: () => ({}),
        },
      },
      data() {
        return {
          FORM_ITEMS: [
            { prop: "name", label: "姓名", lableWidth: 100, el: "input" },
            { prop: "alias", label: "花名", lableWidth: 100, el: "custom" },
          ],
        };
      },
    };
    </script>
    
    

    如何使用我们的第三版表单组件

    上面的FORM_ITEMS为了演示我们都是在data里面写死,实际上需要通过props传入,需要改成这样

    <template>
      <el-form :model="formLabelAlign">
        <el-form-item
          v-for="item in formItems"
          :key="item.prop"
          :label="item.label"
          :prop="item.prop"
        >
          <el-input
            v-if="item.el === 'input'"
            :placeholder="item.placeholder"
            v-model="formLabelAlign[item.prop]"
          ></el-input>
    
          ...上面的内容缩略
    
         <slot :slot="item.prop" />
    
        </el-form-item>
      </el-form>
    </template>
    <script>
    export default {
      name: "CommonForm",
      props: {
        formLabelAlign: {
          type: Object,
          require: true,
          defalut: () => ({}),
        },
        formItems: {
          type: Array,
          require: true,
          
        },
      },
    
    };
    </script>
    

    具体使用

    <template >
        <CommonForm :form-label-align="formData" :form-items="FORM_ITEMS">
            <!-- 因为我们应该在公共组件注册过这个插槽, alias这项不符合我们的格式,这里就可以自定义 -->
            <template slot="alias">
                <h3>{{formData.alias}}</h3>
                <el-input v-model="formData.alias" />
            </template>
        <CommonForm>
    </template>
    <script>
    import CommonForm from "./common-form";
    export default {
      data() {
        return {
          formData: {
            name: "dogeWin",
            alias: "道格温·狗胜",
          },
          FORM_ITEMS: [
            { prop: "name", label: "姓名", lableWidth: 100, el: "input" },
            { prop: "alias", label: "花名", lableWidth: 100, el: "custom" },
            {
              prop: "info",
              label: "信息介绍",
              lableWidth: 100,
              el: "textarea",
              row: 3,
            },
            { prop: "switch", label: "是否启动", lableWidth: 100, el: "switch" },
            { prop: "percent", label: "比例", lableWidth: 100, el: "slider" },
          ],
        };
      },
      components: {
        CommonForm,
      },
    };
    </script>
    

    第四版公共表单组件

    还有一种方式可以自定义传入的内容,函数式组件functional componet,这里直接贴出实现,有兴趣的可以去研究下

    <template>
      <el-form :model="formLabelAlign">
        <el-form-item
          v-for="item in FORM_ITEMS"
          :key="item.prop"
          :label="item.label"
          :prop="item.prop"
        >
          <el-input
            v-if="item.el === 'input'"
            :placeholder="item.placeholder"
            v-model="formLabelAlign[item.prop]"
          ></el-input>
    
          ...上面的内容缩略
        <!-- 如果传入的item有render方法,那么就由我们的函数式组件去渲染,绑定params为我们传入的formData -->
         <functionalComponent v-if="item.render"  :params="formLabelAlign" />
    
         <slot :slot="item.prop" />
        </el-form-item>
      </el-form>
    </template>
    <script>
    // 这个函数组件十分简单,就是把传入的render作为自己的render,params是我们传入的formData
    const functionalComponent = {
      functional: true,
      props: {
        render: Function,
        params: Object,
      },
    
      render(h, ctx) {
        const params = { ...ctx.props.params };
        return ctx.props.render.call(ctx, h, params);
      }
    }
    export default {
      name: "CommonForm",
      props: {
        formLabelAlign: {
          type: Object,
          require: true,
          defalut: () => ({}),
        },
        formItems: {
          type: Array,
          require: true,
          
        },
      },
      components: {
          functionalComponent
      }
    };
    </script>
    
    

    第四版公共表单组件的使用

    <template >
      <CommonForm :form-data="formData" :form-items="FORM_ITEMS" />
    </template>
    <script>
    import CommonForm from "./common-form";
    export default {
      data() {
        return {
          formData: {
            name: "dogeWin",
            alias: "道格温·狗胜",
          },
          FORM_ITEMS: [
            { prop: "name", label: "姓名", lableWidth: 100, el: "input" },
            { prop: "alias", label: "花名", lableWidth: 100, 
            	// jsx语法,使用请先安装对于版本的jsx插件
                render(h, params) {
                    return (
                        <div>
                            <h3>{params.alias}</h3>
                            <el-input value={params.alias} onInput={e => params.alias = e} />
                        <div>
                    )
                }
            },
          ],
        };
      },
      components: {
        CommonForm,
      },
    };
    </script>
    

    总结

    通过我们的渐进式学习,一个实用的公共表单组件就完成了,我直接就是一个好家伙~~=!


    起源地下载网 » 数据驱动的公共表单组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元