>[success] # 動態表單組件
~~~
1.公司有個項目需求是用戶可以拖拽,自己定義表單內容然后根據
自己的需求做自己的表單驗證和表單的打印
2.整套代碼我雖然不是動態表單組件的模塊的開發人員,也大概看了
這部分代碼,我們公司的思路是通過'element-ui' 提供的組件基礎上
我們吧需要的對應字段和'form-item' 放到一起變成一個自定義的組件
,相比下面我通過'思否'學到的這種相比,我們的好處這是在定制化
程度上可能略高,實際體驗和代碼感覺略差,后續有機會,也會簡單
整理一下我們公司這套分享出來
3.下面會主要對'思否' 這套進行說明
~~~
>[info] ## 創建動態組件準備 -- 數據渲染表單字段篇
~~~
1.需要知道'vue' 提供的動態組件'component' 和'is' 相關知識
2.需要 了解'element-ui' 或者'iview-ui' , 接下來的內容是通過'iview-ui'
做的講解說明
3.要知道表單組件整體分類,在這里我個人給表單組件分成兩種,
'普通組件' 和 '組合組件','普通組件'指的就是那些不需要配合直接使用
的,例如'input','組合組件' 指的是那些其實是兩個部分或者多個部分整合
一起才能構成一個表單項,例如'select' 給和'option'一起才能使用
4.動態組件需要和后臺規定好各自所需要的字段,下面的案例規定方式
不是唯一方式,可以根據實際需求來自行規定
5.這里將動態組件分成兩個版本,第一個版本就是普通用來渲染,第二個是
加上了驗證,拆成兩個版本主要是為了方便理解
~~~
>[danger] ##### 在views 文件下創建視圖組件 -- FormTest.vue
~~~
1.在vue的視圖組件文件下創建一個任意的vue文件
~~~
~~~
1.創建一個動態表單組件'form-group'
2.用formList 模擬后臺返回數據
2.1 name -- 用來做驗證的key
2.2 type -- 組件類型為了動態組件渲染時候使用
2.3 value -- 顯示返回的內容值,例如input 需要綁定的v-model 就是這個value
2.4 label -- 在form-item 用來展示字段文字
2.5 children -- 組合組件
3.這里使用的是iview的ui,因此type 這里對應好相應的iview組件
4.在整個formList 模型中注意他們type對應的組件在去iview 文檔中查詢自己需要的屬性即可
5.簡單組件例如input 和 range一類他們只需要單個配合即可渲染,復雜的例如select 需要配合
option等多個組合,因此數據結構也會略微需要調整,可以對這種組合形式組件增加自己以用來識別
定義的字段'children',或者根據需求自定義自己想要的
6.例如radio 和 checkbox 無論是在iview 還是 在element 他們看似是一個組件構成
但實際他們用起來也可以說是組合組件的存在,他們都有一個 group 組的存在
~~~
~~~
<template>
<div>
<form-group :list="formList" :url="url"></form-group>
</div>
</template>
<script>
import FormGroup from '@/components/form-group'
export default {
name: 'FormTest',
components: {
FormGroup,
},
data(){
return {
url: '/data/formData',
formList:[{
name:'name',
type:'i-input',
value:'',
label:'姓名',
rule:[
{ required: true, message: 'The name cannot be empty', trigger: 'blur' }
],
},{
name:'range',
type:'slider',
value:[10,40],
range:true,
label:'范圍'
},{
name:'select',
type:'i-select',
value:'',
label:'性別',
children:{
type:'i-option',
list:[
{value:'man',title:'男'},
{value:'woman',title:'女'},
]
}
},{
name:'education',
type:'radio-group',
value:1,
label:'學歷',
children:{
type:'radio',
list:[
{label:'man',title:'男'},
{label:'woman',title:'女'},
]
}
},{
name:'skill',
type:'checkbox-group',
value:[],
label:'技能',
children:{
type:'checkbox',
list:[
{label:'man',title:'大學'},
{label:'woman',title:'高中'},
]
}
}]
}
}
}
</script>
~~~
>[danger] ##### 在components 文件下動態渲染表單組件 -- form-group.vue(無驗證版)
~~~
1.動態組件 -- component 是vue自帶的
2.雖然使用了vue自帶的component,但這里要注意因為不同類型的表單所需要的
渲染屬性也是略有區別的,因此需要合理設計,例如這里的range 就是針對iview的
范圍組件range才加上的屬性
3.真對簡單類型的組件例如:'input' 和 'range' 等只需要,數據匹配正確利用'componet'
即可動態生成:
<component :is="item.type" :range="item.range" v-model="item.value"></component>
4.針對復雜的例如select 等 這里就涉及到需要使用多個'component' 組件進行渲染
5.小技巧利用_uid 和 index 搭配生成唯一的 key
~~~
~~~
<template>
<Form :lable-width="lableWidth">
<FormItem
v-for="(item,index) in list"
:label="item.label"
label-position="left"
:key = "`${_uid}_${index}`"
>
<component :is="item.type" :range="item.range" v-model="item.value">
<!--做判斷是否有children 屬性,有的話就做組合組件效果 label 是給radio用的 用來單選-->
<template v-if="item.children">
<component
v-for="(child,i) in item.children.list"
:key="`${_uid}_${index}_${i}`"
:value="child.value"
:label="child.label"
:is="item.children.type"
>
{{child.title}}
</component>
</template>
</component>
</FormItem>
</Form>
</template>
<script>
/**
* 1.list 接受從父組件傳遞過來的定義渲染接口的內容值
* 2.利用動態根據傳遞過來的list 中的type 進行組件渲染
* 3.小技巧利用_uid 和 index 搭配生成唯一的 key
**/
export default {
name: "FormGroup",
props:{
list:{
type:Array,
default:()=>{}
},
lableWidth:{
type:Number,
default:100
}
}
}
</script>
<style scoped>
</style>
~~~
>[danger] ##### 在components 文件下動態渲染表單組件 -- form-group.vue(有驗證版)
~~~
1.做表單驗證前需要思考,整個項目結構邏輯,需要一個'重置按鈕',
,因為數據還需要和'Form'組件進行匹配,'Form'組件需要'rules',
'model',因此還需要將數據重洗
2.根據上面分析因此在'data' 中定義了幾個參數分別是:
2.1 initValueObj -- 重置時候需要的數據保存參數
2.2 rules -- 驗證規則的保存位置
2.3 valueObj -- 給Form 組件model 數據存儲
2.4 errorStore -- 后臺返回的錯誤信息展示用的
3.因為這里用的是'valueObj' 存儲最后提交表單的值,也因此在動態組件
渲染的時候也要變成從這個對象去拿對應值即可
4. 表單提交的時候,變化的是接口url,因此實際我們可以,在使用動態組件
頁,傳遞一個動態的url,讓整個動態組件做一個內置提交功能
~~~
~~~
<template>
<Form :lable-width="lableWidth"
v-if="Object.keys(valueObj).length"
:rules="rules"
:model="valueObj">
<FormItem
v-for="(item,index) in list"
:label="item.label"
:prop="item.name"
label-position="left"
:error="errorStore[item.name]"
@click.native="handleFocus(item.name)"
:key = "`${_uid}_${index}`"
>
<component :is="item.type" :range="item.range" v-model="valueObj[item.name]">
<!--做判斷是否有children 屬性,有的話就做組合組件效果 label 是給radio用的 用來單選-->
<template v-if="item.children">
<component
v-for="(child,i) in item.children.list"
:key="`${_uid}_${index}_${i}`"
:value="child.value"
:label="child.label"
:is="item.children.type"
>
{{child.title}}
</component>
</template>
</component>
</FormItem>
<FormItem>
<!--<Button @click="handleSubmit" type="primary">提交</Button>-->
<Button @click="handleReset">重置</Button>
</FormItem>
</Form>
</template>
<script>
import clonedeep from 'clonedeep'
// import { sentFormData } from '@/api/data'
export default {
name: "FormGroup",
props:{
list:{
type:Array,
default:()=>{}
},
lableWidth:{
type:Number,
default:100
},
url: {
type: String,
required: true
}
},
data(){
return{
initValueObj:{},
rules:{},
valueObj:{},// 給model
errorStore: {}
}
},
watch: {
list () {
this.setInitValue()
}
},
methods:{
setInitValue(){
let initValueObj={}
let rules={}
let valueObj={}
let errorStore = {}
this.list.forEach(item => {
rules[item.name] = item.rule
initValueObj[item.name] = item.value
valueObj[item.name] = item.value
})
this.rules = rules
this.valueObj = valueObj
this.initValueObj = initValueObj
this.errorStore = errorStore
},
handleReset () {
this.valueObj = clonedeep(this.initValueObj)
},
handleFocus (name) {
this.errorStore[name] = ''
},
handleSubmit () {
this.$refs.form.validate(valid => {
if (valid) {
sentFormData({
url: this.url,
data: this.valueList
}).then(res => {
this.$emit('on-submit-success', res)
}).catch(err => {
this.$emit('on-submit-error', err)
for (let key in err) {
this.errorStore[key] = err[key]
}
})
}
})
},
},
mounted () {
this.setInitValue()
}
}
</script>
<style scoped>
</style>
~~~
* 上面使用的api寫法
~~~
export const sentFormData = ({ url, data }) => {
return axios.request({
url,
data,
method: 'post'
})
}
~~~
- Vue--基礎篇章
- Vue -- 介紹
- Vue -- MVVM
- Vue -- 創建Vue實例
- Vue -- 模板語法
- Vue -- 指令用法
- v-cloak -- 遮蓋
- v-bind -- 標簽屬性動態綁定
- v-on -- 綁定事件
- v-model -- 雙向數據綁定
- v-for -- 只是循環沒那么簡單
- 小知識點 -- 計劃內屬性
- key -- 屬性為什么要加
- 案例說明
- v-if/v-show -- 顯示隱藏
- v-for 和 v-if 同時使用
- v-pre -- 不渲染大大胡語法
- v-once -- 只渲染一次
- Vue -- class和style綁定
- Vue -- filter 過濾器
- Vue--watch/computed/fun
- watch -- 巧妙利用watch思想
- Vue -- 自定義指令
- Vue -- $方法
- Vue--生命周期
- Vue -- 專屬ajax
- Vue -- transition過渡動畫
- 前面章節的案例
- 案例 -- 跑馬燈效果
- 案例 -- 選項卡內容切換
- 案例-- 篩選商品
- 案例 -- 搜索/刪除/更改
- 案例 -- 用computed做多選
- 案例 -- checked 多選
- Vue--組件篇章
- component -- 介紹
- component -- 使用全局組件
- component -- 使用局部組件
- component -- 組件深入
- component -- 組件傳值父傳子
- component -- 組件傳值子傳父
- component -- 子傳父語法糖拆解
- component -- 父組件操作子組件
- component -- is 動態切換組件
- component -- 用v-if/v-show控制子組件
- component -- 組件切換的動畫效果
- component -- slot 插槽
- component -- 插槽2.6
- component -- 組件的生命周期
- component -- 基礎組件全局注冊
- VueRouter--獲取路由參數
- VueRouter -- 介紹路由
- VueRouter -- 安裝
- VueRouter -- 使用
- VueRouter--router-link簡單參數
- VueRouter--router-link樣式問題
- VueRouter--router-view動畫效果
- VueRouter -- 匹配優先級
- vueRouter -- 動態路由
- VueRouter -- 命名路由
- VueRouter -- 命名視圖
- VueRouter--$router 獲取函數
- VueRouter--$route獲取參數
- VueRouter--路由嵌套
- VueRouter -- 導航守衛
- VueRouter -- 寫在最后
- Vue--模塊化方式結構
- webpack--自定義配置
- webpack -- 自定義Vue操作
- VueCli -- 3.0可視化配置
- VueCli -- 3.0 項目目錄
- Vue -- 組件升級篇
- Vue -- 組件種類與組件組成
- Vue -- 組件prop、event、slot 技巧
- Vue -- 組件通信(一)
- Vue -- 組件通信(二)
- Vue -- 組件通信(三)
- Vue -- 組件通信(四)
- Vue -- 組件通信(五)
- Vue -- 組件通信(六)
- Vue -- bus非父子組件通信
- Vue -- 封裝js插件成vue組件
- vue組件分裝 -- 進階篇
- Vue -- 組件封裝splitpane(分割面板)
- UI -- 正式封裝
- Vue -- iview 可編輯表格案例
- Ui -- iview 可以同時編輯多行
- Vue -- 了解遞歸組件
- UI -- 正式使用遞歸菜單
- Vue -- iview Tree組件
- Vue -- 利用通信仿寫一個form驗證
- Vue -- 使用自己的Form
- Vue -- Checkbox 組件
- Vue -- CheckboxGroup.vue
- Vue -- Alert 組件
- Vue -- 手動掛載組件
- Vue -- Alert開始封裝
- Vue -- 動態表單組件
- Vue -- Vuex組件的狀態管理
- Vuex -- 參數使用理解
- Vuex -- state擴展
- Vuex -- getters擴展
- Vuex--mutations擴展
- Vuex -- Action 異步
- Vuex -- plugins插件
- Vuex -- v-model寫法
- Vuex -- 更多
- VueCli -- 技巧總結篇
- CLI -- 路由基礎
- CLI -- 路由升級篇
- CLI --異步axios
- axios -- 封裝axios
- CLI -- 登錄寫法
- CLI -- 權限
- CLI -- 簡單權限
- CLI -- 動態路由加載
- CLI -- 數據性能優化
- ES6 -- 類的概念
- ES6類 -- 基礎
- ES6 -- 繼承
- ES6 -- 工作實戰用類數據管理
- JS -- 適配器模式
- ES7 -- 裝飾器(Decorator)
- 裝飾器 -- 裝飾器修飾類
- 裝飾器--修飾類方法(知識擴展)
- 裝飾器 -- 裝飾器修飾類中的方法
- 裝飾器 -- 執行順序
- Reflect -- es6 自帶版本
- Reflect -- reflect-metadata 版本
- 實戰 -- 驗證篇章(基礎)
- 驗證篇章 -- 搭建和目錄
- 驗證篇章 -- 創建基本模板
- 驗證篇章 -- 使用
- 實戰 -- 更新模型(為了迎合ui升級)
- 實戰 -- 模型與接口對接
- TypeSprict -- 基礎篇章
- TS-- 搭建(一)webpack版本
- TS -- 搭建(二)直接使用
- TS -- 基礎類型
- TS -- 枚舉類型
- TS -- Symbol
- TS -- interface 接口
- TS -- 函數
- TS -- 泛型
- TS -- 類
- TS -- 類型推論和兼容
- TS -- 高級類型(一)
- TS -- 高級類型(二)
- TS -- 關于模塊解析
- TS -- 聲明合并
- TS -- 混入
- Vue -- TS項目模擬
- TS -- vue和以前代碼對比
- TS -- vue簡單案例上手
- Vue -- 簡單弄懂VueRouter過程
- VueRouter -- 實現簡單Router
- Vue-- 原理2.x源碼簡單理解
- 了解 -- 簡單的響應式工作原理
- 準備工作 -- 了解發布訂閱和觀察者模式
- 了解 -- 響應式工作原理(一)
- 了解 -- 響應式工作原理(二)
- 手寫 -- 簡單的vue數據響應(一)
- 手寫 -- 簡單的vue數據響應(二)
- 模板引擎可以做的
- 了解 -- 虛擬DOM
- 虛擬dom -- 使用Snabbdom
- 閱讀 -- Snabbdom
- 分析snabbdom源碼 -- h函數
- 分析snabbdom -- init 方法
- init 方法 -- patch方法分析(一)
- init 方法 -- patch方法分析(二)
- init方法 -- patch方法分析(三)
- 手寫 -- 簡單的虛擬dom渲染
- 函數表達解析 - h 和 create-element
- dom操作 -- patch.js
- Vue -- 完成一個minVue
- minVue -- 打包入口
- Vue -- new實例做了什么
- Vue -- $mount 模板編譯階段
- 模板編譯 -- 分析入口
- 模板編譯 -- 分析模板轉譯
- Vue -- mountComponent 掛載階段
- 掛載階段 -- vm._render()
- 掛載階段 -- vnode
- 備份章節
- Vue -- Nuxt.js
- Vue3 -- 學習
- Vue3.x --基本功能快速預覽
- Vue3.x -- createApp
- Vue3.x -- 生命周期
- Vue3.x -- 組件
- vue3.x -- 異步組件???
- vue3.x -- Teleport???
- vue3.x -- 動畫章節 ??
- vue3.x -- 自定義指令 ???
- 深入響應性原理 ???
- vue3.x -- Option API VS Composition API
- Vue3.x -- 使用set up
- Vue3.x -- 響應性API
- 其他 Api 使用
- 計算屬性和監聽屬性
- 生命周期
- 小的案例(一)
- 小的案例(二)-- 泛型
- Vue2.x => Vue3.x 導讀
- v-for 中的 Ref 數組 -- 非兼容
- 異步組件
- attribute 強制行為 -- 非兼容
- $attrs 包括 class & style -- 非兼容
- $children -- 移除
- 自定義指令 -- 非兼容
- 自定義元素交互 -- 非兼容
- Data選項 -- 非兼容
- emits Option -- 新增
- 事件 API -- 非兼容
- 過濾器 -- 移除
- 片段 -- 新增
- 函數式組件 -- 非兼容
- 全局 API -- 非兼容
- 全局 API Treeshaking -- 非兼容
- 內聯模板 Attribute -- 非兼容
- key attribute -- 非兼容
- 按鍵修飾符 -- 非兼容
- 移除 $listeners 和 v-on.native -- 非兼容
- 在 prop 的默認函數中訪問 this -- ??
- 組件使用 v-model -- 非兼容
- 渲染函數 API -- ??
- Slot 統一 ??
- 過渡的 class 名更改 ???
- Transition Group 根元素 -- ??
- v-if 與 v-for 的優先級對比 -- 非兼容
- v-bind 合并行為 非兼容
- 監聽數組 -- 非兼容