av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Vue模仿ElementUI的form表單實(shí)例代碼

瀏覽:4日期:2022-10-02 16:55:32
實(shí)現(xiàn)要求

模仿 ElementUI 的表單,分為四層結(jié)構(gòu):index 組件、Form 表單組件、FormItem 表單項(xiàng)組件、Input 和 CheckBox 組件,具體分工如下:

index 組件:

實(shí)現(xiàn):分別引入 Form 組件、FormItem 組件、Input 組件,實(shí)現(xiàn)組裝;

Form 表單組件:

實(shí)現(xiàn):預(yù)留插槽、管理數(shù)據(jù)模型 model、自定義校驗(yàn)規(guī)則 rules、全局校驗(yàn)方法 validate;

FormItem 表單項(xiàng)組件:

實(shí)現(xiàn):預(yù)留插槽、顯示 label 標(biāo)簽、執(zhí)行數(shù)據(jù)校驗(yàn)、顯示校驗(yàn)結(jié)果;

Input 和 CheckBox 組件:

實(shí)現(xiàn):綁定數(shù)據(jù)模型 v-model、通知 FormItem 組件執(zhí)行校驗(yàn);Input 組件

具體實(shí)現(xiàn)如下:

1、自定義組件要實(shí)現(xiàn) v-model 必須實(shí)現(xiàn) :value 和 @input。

2、當(dāng)輸入框中的數(shù)據(jù)發(fā)生變化時(shí),通知父組件執(zhí)行校驗(yàn)。

3、當(dāng) Input 組件綁定的 type 類型為 password 時(shí),在組件內(nèi)部使用 v-bind='$attrs' 獲取 props 之外的內(nèi)容。

4、設(shè)置 inheritAttrs 為 false, 從而避免頂層容器繼承屬性。

Input 組件實(shí)現(xiàn):

<template> <div> <input :value='value' @input='onInput' v-bind='$attrs' /> </div></template><script>export default { inheritAttrs: false, // 避免頂層容器繼承屬性 props: { value: { type: String, default: '' } }, data() { return {}; }, methods: { onInput(e) { // 通知父組件數(shù)值發(fā)生變化 this.$emit('input', e.target.value); // 通知 FormItem 執(zhí)行校驗(yàn) // 這種寫(xiě)法不健壯,因?yàn)?Input 組件和 FormItem 組件之間可能會(huì)隔代 this.$parent.$emit('validate'); } }};</script><style scoped></style>

注意:代碼中使用 this.$parent 派發(fā)事件,這種寫(xiě)法不健壯,當(dāng) Input 組件和 FormItem 組件之間隔代時(shí)會(huì)出現(xiàn)問(wèn)題。具體解決方式見(jiàn)文章尾部代碼優(yōu)化部分。

CheckBox 組件

1、自定義實(shí)現(xiàn) checkBox 的雙向數(shù)據(jù)綁定,和 input 大同小異,必須實(shí)現(xiàn) :checked 和 @change。

CheckBox 組件實(shí)現(xiàn):

<template> <section> <input type='checkbox' :checked='checked' @change='onChange' /> </section></template><script>export default { props: { checked: { type: Boolean, default: false } }, model: { prop: 'checked', event: 'change' }, methods: { onChange(e) { this.$emit('change', e.target.checked); this.$parent.$emit('validate'); } }};</script><style scoped lang='less'></style>FormItem 組件

具體實(shí)現(xiàn)如下:

1、給 Input 組件或者 CheckBox 組件預(yù)留插槽。

2、如果用戶在組件上設(shè)置 label 屬性,要展示 label 標(biāo)簽。

3、監(jiān)聽(tīng)校驗(yàn)事件,并執(zhí)行校驗(yàn)(使用 async-validator 插件進(jìn)行校驗(yàn))。

4、如果不符合校驗(yàn)規(guī)則,需要顯示校驗(yàn)結(jié)果。

在開(kāi)發(fā)的過(guò)程中,我們需要思考幾個(gè)問(wèn)題:

1、在組件內(nèi)部,如何得到需要校驗(yàn)的數(shù)據(jù)和校驗(yàn)規(guī)則?

2、在 Form 表單中會(huì)有多個(gè)菜單項(xiàng),如:用戶名、密碼、郵箱...等等,那么 FormItem 組件是如何得知現(xiàn)在校驗(yàn)的是哪個(gè)菜單呢?

FormItem 組件實(shí)現(xiàn):

<template> <div class='formItem-wrapper'> <div class='content'> <label v-if='label' :style='{ width: labelWidth }'>{{ label }}:</label> <slot></slot> </div> <p v-if='errorMessage' class='errorStyle'>{{ errorMessage }}</p> </div></template><script>import Schema from 'async-validator';export default { inject: ['formModel'], props: { label: { type: String, default: '' }, prop: String }, data() { return { errorMessage: '', labelWidth: this.formModel.labelWidth }; }, mounted() { // 監(jiān)聽(tīng)校驗(yàn)事件,并執(zhí)行校驗(yàn) this.$on('validate', () => { this.validate(); }); }, methods: { validate() { // 執(zhí)行組件的校驗(yàn) // 1、獲取數(shù)據(jù) const values = this.formModel.model[this.prop]; // 2、獲取校驗(yàn)規(guī)則 const rules = this.formModel.rules[this.prop]; // 3、執(zhí)行校驗(yàn) const schema = new Schema({ [this.prop]: rules }); // 參數(shù)1是值,餐數(shù)2是校驗(yàn)錯(cuò)誤對(duì)象數(shù)組 // validate 返回的是 Promise<Boolean> return schema.validate({ [this.prop]: values }, errors => { if (errors) { this.errorMessage = errors[0].message; } else { this.errorMessage = ''; } }); } }};</script><style scoped lang='less'>@labelWidth: 90px;.formItem-wrapper { padding-bottom: 10px;}.content { display: flex;}.errorStyle { font-size: 12px; color: red; margin: 0; padding-left: @labelWidth;}</style>

我們先回答一下上面提出的兩個(gè)問(wèn)題,此處會(huì)涉及到組件之間傳值,可以參考之前的文章《組件傳值、通訊》:首先表單的數(shù)據(jù)和校驗(yàn)規(guī)則是定義在 index 組件內(nèi)部,并且掛載在 Form 組件上,表單的校驗(yàn)項(xiàng)發(fā)生在 FormItem 組件中,先在 Form 組件內(nèi)部通過(guò) props 接受到傳遞的數(shù)據(jù),然后通過(guò) provide/inject 的方式在 FormItem 組件中傳遞給后代組件。

我們?nèi)粘T谟?ElementUI 的表單校驗(yàn)是會(huì)發(fā)現(xiàn),在每一個(gè)需要校驗(yàn)的表單上會(huì)設(shè)置一個(gè) prop 屬性,并且屬性值和綁定的數(shù)據(jù)一致。此處的用途是為了能夠在 FormItem 組件中執(zhí)行校驗(yàn)時(shí)獲取相對(duì)的校驗(yàn)規(guī)則和數(shù)據(jù)對(duì)象。

在 FormItem 組件中通過(guò)使用 inject 獲取注入的 Form 實(shí)例,和 prop 屬性組合使用,可以獲取到表單數(shù)據(jù)和校驗(yàn)規(guī)則。

// 1、獲取數(shù)據(jù)const values = this.formModel.model[this.prop];// 2、獲取校驗(yàn)規(guī)則const rules = this.formModel.rules[this.prop];

使用 async-validator 插件實(shí)例化一個(gè) schema 對(duì)象,用來(lái)執(zhí)行校驗(yàn),schema.validate 需要傳遞兩個(gè)參數(shù),參數(shù)1是當(dāng)前需要校驗(yàn)的字段和相對(duì)應(yīng)的 rules 組成的鍵值對(duì)對(duì)象,參數(shù)2是一個(gè) callback 函數(shù),用來(lái)獲取錯(cuò)誤信息(是一個(gè)數(shù)組)。validate 方法返回的是一個(gè) Promise<Boolean>。

注意:此組件的 validate 方法中,最后使用 return 的目的是為了在 Form 組件中執(zhí)行全局校驗(yàn)使用。

Form 組件

具體實(shí)現(xiàn)如下:

1、給 FormItem 組件預(yù)留插槽。

2、傳遞 Form 實(shí)例給后代,比如 FormItem 用來(lái)獲取校驗(yàn)的數(shù)據(jù)和規(guī)則。

3、執(zhí)行全局校驗(yàn)

Form 組件實(shí)現(xiàn):

<template> <div> <slot></slot> </div></template><script>export default { provide() { return { formModel: this // 傳遞 Form 實(shí)例給后代,比如 FormItem 用來(lái)獲取校驗(yàn)的數(shù)據(jù)和規(guī)則 }; }, props: { model: { type: Object, required: true }, rules: { type: Object }, labelWidth: String }, data() { return {}; }, methods: { validate(cb) { // 執(zhí)行全局校驗(yàn) // map 結(jié)果是若干 Promise 數(shù)組 const tasks = this.$children.filter(item => item.prop).map(item => item.validate()); // 所有任務(wù)必須全部校驗(yàn)成功才算校驗(yàn)通過(guò) Promise.all(tasks) .then(() => { cb(true); }) .catch(() => { cb(false); }); } }};</script><style scoped></style>

我們?cè)?Form 組件中使用 provide 注入當(dāng)前組件對(duì)象,方便后續(xù)子孫代獲取數(shù)據(jù)/方法使用。

執(zhí)行全局校驗(yàn)的時(shí)候,先使用 filter 過(guò)濾掉不需要校驗(yàn)的組件(我們?cè)?FormItem 組件上設(shè)置的 prop 屬性,只要有此屬性,就是需要校驗(yàn)的),然后分別執(zhí)行組件中的 validate 方法(如果在 FormItem 組件中不使用 return 數(shù)據(jù),最后獲取到的全都是 undefined),返回的是一個(gè)若干 Promise 數(shù)組。

簡(jiǎn)單介紹一個(gè) Promise.all() 方法:

Promise.all() 方法接收一個(gè)promise的iterable類型(注:Array,Map,Set都屬于ES6的iterable類型)的輸入,并且只返回一個(gè)Promise實(shí)例, 那個(gè)輸入的所有promise的resolve回調(diào)的結(jié)果是一個(gè)數(shù)組。這個(gè)Promise的resolve回調(diào)執(zhí)行是在所有輸入的promise的resolve回調(diào)都結(jié)束,或者輸入的iterable里沒(méi)有promise了的時(shí)候。它的reject回調(diào)執(zhí)行是,只要任何一個(gè)輸入的promise的reject回調(diào)執(zhí)行或者輸入不合法的promise就會(huì)立即拋出錯(cuò)誤,并且reject的是第一個(gè)拋出的錯(cuò)誤信息。

index 組件

定義模型數(shù)據(jù)、校驗(yàn)規(guī)則等等,分別引入 Form 組件、FormItem 組件、Input 組件,實(shí)現(xiàn)組裝。

index 組件實(shí)現(xiàn):

<template> <div> <Form :model='formModel' :rules='rules' ref='loginForm' label-width='90px'> <FormItem label='用戶名' prop='username'> <Input v-model='formModel.username'></Input> </FormItem> <FormItem label='密碼' prop='password'> <Input type='password' v-model='formModel.password'></Input> </FormItem> <FormItem label='記住密碼' prop='remember'> <CheckBox v-model='formModel.remember'></CheckBox> </FormItem> <FormItem> <button @click='onLogin'>登錄</button> </FormItem> </Form> </div></template><script>import Input from '@/components/form/Input';import CheckBox from ’@/components/form/CheckBox’import FormItem from '@/components/form/FormItem';import Form from '@/components/form/Form';export default { data() { const validateName = (rule, value, callback) => { if (!value) { callback(new Error('用戶名不能為空')); } else if (value !== 'admin') { callback(new Error('用戶名錯(cuò)誤 - admin')); } else { callback(); } }; const validatePass = (rule, value, callback) => { if (!value) { callback(false); } else { callback(); } }; return { formModel: { username: '', password: '', remember: false }, rules: { username: [{ required: true, validator: validateName }], password: [{ required: true, message: '密碼必填' }], remember: [{ required: true, message: '記住密碼必選', validator: validatePass }] } }; }, methods: { onLogin() { this.$refs.loginForm.validate(isValid => { if (isValid) { alert('登錄成功'); } else { alert('登錄失敗'); } }); } }, components: { Input, CheckBox, FormItem, Form }};</script><style scoped></style>

當(dāng)我們點(diǎn)擊登錄按鈕時(shí),會(huì)執(zhí)行全局校驗(yàn)方法,我們可以使用 this.$refs.xxx 獲取 DOM 元素和組件實(shí)例。

在上面我們還留了一個(gè)小尾巴~,就是在 Input 組件中通知父組件執(zhí)行校驗(yàn),目前使用的是 this.$parent.$emit(),這樣寫(xiě)有一個(gè)弊端,就是當(dāng) Input 組件和 FormItem 組件之后隔代的時(shí)候,再使用 this.$parent 獲取不到 FormItem 組件。我們可以封裝一個(gè) dispatch 方法,主要實(shí)現(xiàn)向上循環(huán)查找父元素并且派發(fā)事件,代碼實(shí)現(xiàn)如下:

dispatch(eventName, data) { let parent = this.$parent; // 查找父元素 while (parent) { // 父元素用$emit觸發(fā) parent.$emit(eventName, data); // 遞歸查找父元素 parent = parent.$parent; }}

該方法可以借用 mixins 引入使用:mixins/emmiters.js

export default { methods: { dispatch(eventName, data) { let parent = this.$parent; // 查找父元素 while (parent) { // 父元素用$emit觸發(fā) parent.$emit(eventName, data); // 遞歸查找父元素 parent = parent.$parent; } } }};

修改 Input 組件:

<template> <div> <input :value='value' @input='onInput' v-bind='$attrs' /> </div></template><script>import emmiter from '@/mixins/emmiter';export default { inheritAttrs: false, // 避免頂層容器繼承屬性 mixins: [emmiter], props: { value: { type: String, default: '' } }, data() { return {}; }, methods: { onInput(e) { // 通知父組件數(shù)值發(fā)生變化 this.$emit('input', e.target.value); // 通知 FormItem 執(zhí)行校驗(yàn) // 這種寫(xiě)法不健壯,因?yàn)?Input 組件和 FormItem 組件之間可能會(huì)隔代 // this.$parent.$emit('validate'); this.dispatch('validate'); // 使用 mixin 中 emmiter 的 dispatch,解決跨級(jí)問(wèn)題 } }};</script><style scoped></style>總結(jié)

到此這篇關(guān)于Vue模仿ElementUI的form表單的文章就介紹到這了,更多相關(guān)Vue模仿ElementUI form表單內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Vue
相關(guān)文章:
主站蜘蛛池模板: 亚洲精品一区二区在线观看 | 久久99国产精一区二区三区 | 91精品国产自产精品男人的天堂 | 午夜私人影院 | 91xxx在线观看 | 热re99久久精品国产99热 | 91网站在线看 | 国产成人一区二区三区 | 国产一区二区三区久久久久久久久 | 亚洲社区在线 | 久久人人爽人人爽 | 欧美精品一区二区在线观看 | 自拍第一页 | 欧美成人一区二区 | 精彩视频一区二区三区 | 操网站| 日本久久综合 | 中文精品视频 | 神马久久久久久久久久 | 秋霞精品 | 一区二区日韩 | 激情网五月天 | 精品国产一区二区在线 | 国产在线成人 | 国产精品99 | 毛片99| 成年人在线观看 | 日韩二区| 午夜欧美a级理论片915影院 | 天天看天天爽 | 欧美成人影院在线 | 狠狠做深爱婷婷综合一区 | 亚洲视频一区在线 | 亚洲国产成人精 | 亚洲 中文 欧美 日韩 在线观看 | 欧美日韩中文在线 | 免费在线看黄 | 亚洲精品一区中文字幕乱码 | 欧美韩一区二区 | 97日日碰人人模人人澡分享吧 | 黄色一级大片视频 |