vue構(gòu)建動(dòng)態(tài)表單的方法示例
概述
后臺(tái)管理系統(tǒng)里面有非常多的表單需求,我們希望能夠通過(guò)寫(xiě)一個(gè)json格式的數(shù)據(jù),通過(guò)vue的循環(huán)動(dòng)態(tài)地去渲染動(dòng)態(tài)表單。并且能夠在外部得到渲染出來(lái)的表單的數(shù)據(jù),可以對(duì)表單進(jìn)行重置操作。我結(jié)合element ui的控件的下拉框,輸入框,時(shí)間選擇控件和vue-treeselect,做了一個(gè)動(dòng)態(tài)表單。

v-model的理解
先簡(jiǎn)單講一下vue-model是怎么玩的。其實(shí)vue-model相當(dāng)于給表單元素傳遞一個(gè)value,外部監(jiān)聽(tīng)input事件。所以我們自己封裝表單組件的時(shí)候也是可以傳遞一個(gè)value值,監(jiān)聽(tīng)input事件獲取輸入的值。
<input type="text" v-model="something">
<!--等價(jià)于-->
<input type="text"
v-bind:value="something"
v-on:input="something = $event.target.value">
封裝表單組件
組件最重要的開(kāi)發(fā)思想就是設(shè)計(jì)好輸入輸出。這里就以下拉框組件為例吧。使用的是element ui的下拉框,進(jìn)行一個(gè)簡(jiǎn)單封裝。
輸入:name:每個(gè)表單的數(shù)據(jù)標(biāo)識(shí),如區(qū)域編碼輸入框,父元素應(yīng)該傳遞areaCode過(guò)來(lái)。
value: 表單選擇/輸入的值,從父元素獲取后賦值給currentValue,通過(guò)監(jiān)聽(tīng)父元素的值實(shí)現(xiàn)同步變
化。
options:下拉框要渲染的選項(xiàng)值,一般是個(gè)對(duì)象數(shù)組。
輸出:onInputEvent,emit一個(gè)input事件,讓父元素能夠感知組件的數(shù)據(jù)變化。
也就是可以在組件使用的地方監(jiān)聽(tīng)input事件
<template>
<el-form-item :label="label">
<el-select v-model="currentValue" @input="onInputEvent">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</template>
<script>
import formMixins from '../../../mixins/form-model'
export default {
name: "SelectList",
props: ['name', 'label', 'value','options'],
mixins: [formMixins],
data() {
return {
currentValue: this.value
}
},
methods: {
onInputEvent(value) {
this.$emit('input', this.name, value);
}
},
watch: {
value(val) {
this.currentValue= val;
}
}
}
</script>
一點(diǎn)封裝
由于每個(gè)表單組件都是監(jiān)聽(tīng)父元素的value值變化,數(shù)據(jù)變化時(shí)都是觸發(fā)onInputEvent并執(zhí)行this.$emit('input'),所以我們可以把這部分內(nèi)容抽取出來(lái)放在mixins里面。
form-model.js
export default {
props: ['name', 'value'],
data () {
return {
currentValue: this.value
};
},
methods: {
onInputEvent(value) {
this.$emit('input', this.name, value);
},
reset() {
this.currentValue = "";
}
},
watch: {
value (val) {
this.currentValue = val;
}
}
};
然后我們的下拉框組件就可以少寫(xiě)一些共用的代碼,直接用 mixins: [formMixins]
<template>
<el-form-item :label="label">
<el-select v-model="currentValue" @input="onInputEvent">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</template>
<script>
import formMixins from '../../../mixins/form-model'
export default {
name: "SelectList",
props: ['name', 'label', 'value', 'options'],
mixins: [formMixins],
data() {
return {
currentValue: this.value
}
}
}
</script>
動(dòng)態(tài)生成表單
這里主要是根據(jù)配置的數(shù)據(jù),循環(huán)生成表單組件。默認(rèn)提供提交和重置按鈕,如果不需要可以通過(guò)slot傳遞其他操作按鈕。這里的要點(diǎn)主要有:
監(jiān)聽(tīng)表單組件的數(shù)據(jù)變化:
每個(gè)表單組件都有一個(gè)name標(biāo)識(shí)它的業(yè)務(wù)含義,綁定的數(shù)據(jù)也是formData[field.name],@input事件傳遞updateForm,在updateForm里面更新this.formData[name],保證了this.formData里面的數(shù)據(jù)是和表單組件選擇/填寫(xiě)的內(nèi)容一致。
重置時(shí)改變表單組件的數(shù)據(jù):
因?yàn)榻M件內(nèi)部會(huì)監(jiān)聽(tīng)父元素的value,所以這里只要清空this.formData的值,組件內(nèi)部的數(shù)據(jù)也會(huì)跟著清空。
<template>
<div>
<el-form :inline="true" ref="form" :model="formData" class="demo-form-inline">
<el-col :span="field.cols" v-for="(field, index) in config.fieldsConfig" v-bind:key="index">
<component :key="index"
:is="field.fieldType"
:label="field.label"
:value="formData[field.name]"
:multiple="field.multiple"
@input="updateForm"
v-bind="field"
:options="field.options"
:ref="field.name"
>
</component>
</el-col>
<slot name="buttons">
<el-button type="primary" @click="submit" size="small">{{onSubmitText}}</el-button>
<el-button type="default" @click="reset" size="small">{{onResetText}}</el-button>
</slot>
</el-form>
</div>
</template>
<script>
import SelectList from './basicComponent/SelectList'
import TextInput from './basicComponent/TextInput'
import TimeSelector from './basicComponent/TimeSelector'
import SelectTree from './basicComponent/SelectTree'
import StaffSelectPopedit from './businessComponent/StaffSelectPopedit'
export default {
name: "FormGenerator",
components: { SelectList, TextInput, TimeSelector, SelectTree, StaffSelectPopedit},
props: ["config", "value"],
data() {
return {
formData: this.value,
onSubmitText: this.config.buttons.onSubmitText || '提交',
onResetText: this.config.buttons.onResetText || '重置'
}
},
methods: {
updateForm(fieldName, value) {
this.formData[fieldName] = value;
},
submit() {
this.$emit("submit");
},
reset() {
for(var name in this.formData) {
if(typeof this.formData === "String") {
this.formData[name] = "";
} else {
this.formData[name] = null;
}
}
}
}
}
</script>
業(yè)務(wù)使用的地方
像下拉框的選擇數(shù)據(jù),這些應(yīng)該是后臺(tái)渲染的,所以我們暫時(shí)用setTimeout模擬一下。感覺(jué)這里this.config.fieldsConfig[4].options寫(xiě)的不太優(yōu)雅,依賴于配置數(shù)據(jù)的順序肯定不是啥好事情。求大神指點(diǎn)。
<template>
<div>
<form-generator :config="config"
@submit="getFormData"
v-model="formData"
>
</form-generator>
</div>
</template>
<script>
import FormGenerator from '../components/form/FormGenerator'
export default {
name: "FormGeneratorDemo",
components: { FormGenerator },
created () {
this.queryOrderType();
this.queryAreaTree();
},
data() {
return {
formData: {
orderCode: "",
orderType: "",
beginTime: "",
endTime: "",
area: [],
staff:""
},
config: {
fieldsConfig: [
{
name: 'orderCode',
label: '定單編碼',
fieldType: 'TextInput',
cols: 8
},
{
name: 'orderType',
label: '定單類型',
fieldType: 'SelectList',
options: [],
cols: 8
},
{
name: 'beginTime',
label: '開(kāi)始時(shí)間',
fieldType: 'TimeSelector',
cols: 8
},
{
name: 'endTime',
label: '結(jié)束時(shí)間',
fieldType: 'TimeSelector',
cols: 8
},
{
name: 'area',
label: '區(qū)域',
fieldType: 'selectTree',
options: [],
multiple: true,
cols: 8
},
{
name: 'staff',
label: '人員選擇',
fieldType: 'StaffSelectPopedit',
cols: 8
}
],
buttons: {
onSubmitText: '提交',
onResetText: '重置'
}
}
}
},
methods: {
getFormData() {
console.log(this.formData);
},
queryOrderType() {
setTimeout(() => {
this.config.fieldsConfig[1].options = [
{ label: 'select1', value: 'key1'},
{ label: 'select2', value: 'key2'},
{ label: 'select3', value: 'key3'}
];
}, 100)
},
queryAreaTree() {
this.config.fieldsConfig[4].options = [
{
id: 'a',
label: 'a',
children: [{
id: 'aa',
label: 'AA',
}, {
id: 'ab',
label: 'AB',
}],
}, {
id: 'b',
label: 'B',
}, {
id: 'c',
label: 'C',
}
]
}
}
}
</script>
大概就是這樣的思路,我們希望我們只要寫(xiě)上面那樣子的配置數(shù)據(jù)就可以動(dòng)態(tài)生成各種這樣的表單組件,不用寫(xiě)一大堆重復(fù)代碼。如果有更好的解決辦法,歡迎和我聯(lián)系。另外,代碼路徑https://github.com/supportlss/vue-dynamic-form
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
nuxt框架中對(duì)vuex進(jìn)行模塊化設(shè)置的實(shí)現(xiàn)方法
這篇文章主要介紹了nuxt框架中對(duì)vuex進(jìn)行模塊化設(shè)置的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
vue項(xiàng)目中使用bpmn-自定義platter的示例代碼
這篇文章主要介紹了vue項(xiàng)目中使用bpmn-自定義platter的實(shí)例代碼,本文通過(guò)代碼截圖相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Vue響應(yīng)式原理模擬實(shí)現(xiàn)原理探究
這篇文章主要介紹了Vue響應(yīng)式原理,響應(yīng)式就是當(dāng)對(duì)象本身(對(duì)象的增刪值)或者對(duì)象屬性(重新賦值)發(fā)生了改變的時(shí)候,就會(huì)運(yùn)行一些函數(shù),最常見(jiàn)的示render函數(shù)2022-09-09
基于uniapp+vue3自定義增強(qiáng)版table表格組件「兼容H5+小程序+App端」
uv3-table:一款基于uniapp+vue3跨端自定義手機(jī)端增強(qiáng)版表格組件,支持固定表頭/列、邊框、斑馬紋、單選/多選,自定義表頭/表體插槽、左右固定列陰影高亮顯示,支持編譯兼容H5+小程序端+App端,H5+小程序+App端,多端運(yùn)行一致2024-05-05
vue2+elementui的el-table固定列會(huì)遮住橫向滾動(dòng)條及錯(cuò)位問(wèn)題解決方案
這篇文章主要介紹了vue2+elementui的el-table固定列會(huì)遮住橫向滾動(dòng)條及錯(cuò)位問(wèn)題解決方案,主要解決固定列錯(cuò)位后, 接下來(lái)就是把固定列往上提滾動(dòng)條的高度就不會(huì)影響了,需要的朋友可以參考下2024-01-01
Vite+TS+Vue開(kāi)啟eslint和prettier規(guī)范及校驗(yàn)方式
這篇文章主要介紹了Vite+TS+Vue開(kāi)啟eslint和prettier規(guī)范及校驗(yàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06

