问题背景
以前刚用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、prop
和el-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介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!