後臺管理系統裏面有很是多的表單需求,咱們但願可以經過寫一個json格式的數據,經過vue的循環動態地去渲染動態表單。而且可以在外部獲得渲染出來的表單的數據,能夠對錶單進行重置操做。我結合element ui的控件的下拉框,輸入框,時間選擇控件和vue-treeselect,作了一個動態表單。vue
先簡單講一下vue-model是怎麼玩的。其實vue-model至關於給表單元素傳遞一個value,外部監聽input事件。因此咱們本身封裝表單組件的時候也是能夠傳遞一個value值,監聽input事件獲取輸入的值。git
<input type="text" v-model="something"> <!--等價於--> <input type="text" v-bind:value="something" v-on:input="something = $event.target.value">
組件最重要的開發思想就是設計好輸入輸出。這裏就如下拉框組件爲例吧。使用的是element ui的下拉框,進行一個簡單封裝。
輸入:name:每一個表單的數據標識,如區域編碼輸入框,父元素應該傳遞areaCode過來。github
value: 表單選擇/輸入的值,從父元素獲取後賦值給currentValue,經過監聽父元素的值實現同步變 化。 options:下拉框要渲染的選項值,通常是個對象數組。
輸出:onInputEvent,emit一個input事件,讓父元素可以感知組件的數據變化。
也就是能夠在組件使用的地方監聽input事件json
<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>
因爲每一個表單組件都是監聽父元素的value值變化,數據變化時都是觸發onInputEvent並執行this.$emit('input'),因此咱們能夠把這部份內容抽取出來放在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; } } };
而後咱們的下拉框組件就能夠少寫一些共用的代碼,直接用 mixins: [formMixins]ui
<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>
這裏主要是根據配置的數據,循環生成表單組件。默認提供提交和重置按鈕,若是不須要能夠經過slot傳遞其餘操做按鈕。這裏的要點主要有:
監聽表單組件的數據變化:
每一個表單組件都有一個name標識它的業務含義,綁定的數據也是formData[field.name],@input事件傳遞updateForm,在updateForm裏面更新this.formData[name],保證了this.formData裏面的數據是和表單組件選擇/填寫的內容一致。
重置時改變表單組件的數據:
由於組件內部會監聽父元素的value,因此這裏只要清空this.formData的值,組件內部的數據也會跟着清空。this
<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>
像下拉框的選擇數據,這些應該是後臺渲染的,因此咱們暫時用setTimeout模擬一下。感受這裏this.config.fieldsConfig[4].options寫的不太優雅,依賴於配置數據的順序確定不是啥好事情。求大神指點。編碼
<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: '開始時間', fieldType: 'TimeSelector', cols: 8 }, { name: 'endTime', label: '結束時間', fieldType: 'TimeSelector', cols: 8 }, { name: 'area', label: '區域', 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>
大概就是這樣的思路,咱們但願咱們只要寫上面那樣子的配置數據就能夠動態生成各類這樣的表單組件,不用寫一大堆重複代碼。若是有更好的解決辦法,歡迎和我聯繫。另外,代碼路徑https://github.com/supportlss...spa