您好,登錄后才能下訂單哦!
這篇文章主要介紹vue如何實(shí)現(xiàn)自定義多選與單選的答題功能,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
Vue是一套用于構(gòu)建用戶界面的漸進(jìn)式JavaScript框架,Vue與其它大型框架的區(qū)別是,使用Vue可以自底向上逐層應(yīng)用,其核心庫只關(guān)注視圖層,方便與第三方庫和項(xiàng)目整合,且使用Vue可以采用單文件組件和Vue生態(tài)系統(tǒng)支持的庫開發(fā)復(fù)雜的單頁應(yīng)用。
ui圖:
可悲的是,這個(gè)項(xiàng)目兩個(gè)月后,我才來做項(xiàng)目復(fù)盤,
話說也就在此時(shí),我才發(fā)現(xiàn)有一種更簡單的方式來實(shí)現(xiàn)并且應(yīng)用上v-model,
為什么要為了樣式放棄功能然后自己吭哧吭哧傻-滴-呼呼的用js來實(shí)現(xiàn)了類似雙向綁定的感覺?。?!
flag:今天先專注把我費(fèi)勁巴拉手動(dòng)搬得磚總結(jié)一下,明天(07-05)我再把所謂的最簡單的方法做出來貼這里~
這個(gè)需求的難點(diǎn)在于以下幾點(diǎn):
1.單選點(diǎn)擊后選中狀態(tài),需滿足如下:X
a) 每次點(diǎn)擊只能選中其中一個(gè)
b) 當(dāng)選中時(shí)再次點(diǎn)擊其他選項(xiàng)需要切換選擇對應(yīng)點(diǎn)擊項(xiàng)
c) 選中時(shí)點(diǎn)擊自身無顯示上的反應(yīng)(同樣的邏輯再做一遍也無妨,即再加一遍類名也看不出來)
2.多選樣式展示,需滿足如下:
a) 同時(shí)可以選中多個(gè)X
b) 多選已選中狀態(tài)再次點(diǎn)擊取消選中X
3.多選選中項(xiàng)的記錄,需滿足如下:
a) 選擇幾個(gè)記錄幾個(gè)
b) 選中再取消時(shí)需要將本條記錄的數(shù)據(jù)通時(shí)消除(依據(jù)點(diǎn)擊事件,事件點(diǎn)擊觸發(fā)判斷哪個(gè)被選中了)
4.單選選中項(xiàng)的記錄,方便提交數(shù)據(jù)
5.未點(diǎn)擊選項(xiàng)不可提交,并給提示
6.可提交狀態(tài),需滿足如下:
a) 單選選中任意一個(gè),即可提交。再次修改對提交沒有影響
b) 多選至少選中一個(gè)可提交,再次修改需判斷是不是沒選東西
7.第十四題點(diǎn)下一題切換提交按鈕
8.快速點(diǎn)擊下一題,多次提交
9.點(diǎn)擊下一題提交數(shù)據(jù)后,拿響應(yīng)結(jié)果調(diào)取彈層提示用戶選擇是否正確
=============接下來一 一解決====================
首先先說結(jié)構(gòu)
看似十道題,其實(shí)是一道題不停的換數(shù)據(jù),所以我的外部結(jié)構(gòu)就是一個(gè)form加一個(gè)空的div
別問我為什么多余一個(gè)空的,我也很無措。
form.question(v-if="state.ExamInfo") div
然后題目標(biāo)題很傻瓜式得使用了h4
h4.qus-title(:data-id="state.ExamInfo.QuestionID") {{state.ExamInfo.ExamQuestionNo}} {{state.ExamInfo.Description}}
選項(xiàng)上,我使用ul>li的形式描述了多個(gè)選項(xiàng)
ul.qus-list li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" @click="choosed(index)" v-bind:class="{'li-focus' : chooseNum==index}" ref="liId") {{item.Code}}、{{item.Description}}
大致幾個(gè)屬性
v-for是為了遍歷題中的每一個(gè)選項(xiàng),
click綁定了點(diǎn)擊當(dāng)前l(fā)i時(shí)的事件,v-bind同步click綁定了動(dòng)態(tài)的類名,用于展示選中狀態(tài)。
這里值得注意的一個(gè)點(diǎn)也是當(dāng)時(shí)抓蝦的一個(gè)點(diǎn)是,v-on:click和v-bind:class結(jié)合,
click的時(shí)候,每次把當(dāng)前點(diǎn)擊的li的index值傳出去,
然后定義一個(gè)變量chooseNum,點(diǎn)擊函數(shù)中,將參數(shù)index賦給他this.chooseNum = index;
靠這種間接拿到點(diǎn)擊索引值得曲線救國方式,在v-bind的監(jiān)視下,每次點(diǎn)擊獲得的索引chooseNum
和這幾個(gè)li中自己的index對上眼以后,就如正確的鑰匙對上了合適的鎖,類名綁定就成了。
也就是那十幾條難題中的第一個(gè)被輕松干掉的難題的前半部分: 單選點(diǎn)擊后選中狀態(tài) 。
費(fèi)這么半天勁,才解決一個(gè)點(diǎn)??!我不服!別急,接下來還有好戲。
但其實(shí)這個(gè)思路還是挺重要的,靠這一點(diǎn)“死皮賴臉”拉關(guān)系的勁頭,這個(gè)法子以后還倒是可以有很多用武之地。
好戲在下一個(gè)屬性,沒錯(cuò)就是ref,這也是我步入萬丈深淵一去不復(fù)返的梯子??!
ref
要知道人家可以vue里邊的特殊特性,
要知道人家可是很有能力的,
要知道我老是連著打不出妖之道這三個(gè)字!我就不行了?。?/p>
好了不皮了。
官網(wǎng)記載ref這個(gè)特殊特性,被用來給元素或子組件注冊引用信息。 引用信息將會注冊在父組件的 $refs 對象上。
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子組件上,引用就指向組件實(shí)例。
我的理解大白話來說,他就是給dom元素或者組件實(shí)例一個(gè)身份證號,身份證號有的特性他也就有,那就是唯一不重復(fù)。
如果配合上v-for,就能獲取這一批帶有ref特性所組成的數(shù)組。
通過數(shù)組下標(biāo)索引出來的個(gè)體,也就是對應(yīng)的dom元素本身或者組件實(shí)例本人無疑了。
就好像拿著身份證號去公安局查人一樣,快速不說,還很高效有沒有,一查一個(gè)準(zhǔn)!
但需要說明的是,在created鉤子中,這個(gè)特性拿不到東西,生命周期鉤子里只有在mounted里(可能還有后邊的鉤子里,我沒用過不準(zhǔn)確)可以用,畢竟你想啊,身份證號雖說一出生就有了,但是只有掛載到網(wǎng)上你才能查到大的嘛!
所以,我究竟用它做了什么呢?那就是多選功能??!
還是先回到上邊說的,綁定了一個(gè)事件,并且會傳遞一個(gè)當(dāng)前點(diǎn)擊li的索引號,并且前邊也提到過,
ref返回的是數(shù)組,有數(shù)組有索引號,簡直是萬事俱備啊。于是乎讓我們來呼喚東風(fēng)(別看了,就是說我們自己)!
在choose點(diǎn)擊函數(shù)中就有了這么一段:
if(this.$refs.liId[index].className.length <= 0){ //首先先判斷當(dāng)前l(fā)i有沒有被選中,因?yàn)槲疫@里li除了選中狀態(tài)的有類名,其他沒有類名,所以我就這么判斷了。 //這么看有時(shí)候舍棄一小丟丟規(guī)范的東西反而省力。 this.$refs.liId[index].className = 'li-focus';// 添加類 }else{ //當(dāng)前l(fā)i已經(jīng)被選中,那么在多選的邏輯里,是允許人們選中后再取消選中的,所以前端展示層面上把樣式去掉。 this.$refs.liId[index].className = '';// 選中再取消的情況 }
好了,第二個(gè) 多選樣式 搞定。
那么接下來,選擇的結(jié)果呢,能不能來一次“趁火打劫”趁點(diǎn)擊的時(shí)候偷偷記錄下用戶的選擇?答案當(dāng)然是可以的啦!
首先說多選功能的趁火打劫吧,就著上邊增刪類名的熱乎勁,緊接著在每次點(diǎn)擊時(shí)我們記錄下當(dāng)前點(diǎn)擊的是誰
// 獲取選中結(jié)果 for (let i = 0; i < this.$refs.liId.length; i++) { if(this.$refs.liId[i].className.length > 0){ this.chooseNumStr += this.$refs.liId[i].innerText.substring(0,1); } }
這一段再次利用了ref的特性,找到當(dāng)前點(diǎn)擊的dom,截取人家選項(xiàng)里的第一個(gè)字,那就是ABC or D;
拼接到事先準(zhǔn)備好的字符串chooseNumStr中(要發(fā)給數(shù)據(jù)用的),因?yàn)檫@里和后端提前約定的就是將選擇結(jié)果以字符串的形式提交。
if判斷那里,條件再次是利用了li誰有類名就是選了誰的不講理原則。第三個(gè) 多選記錄選項(xiàng)功能 問題搞定。
第四個(gè)問題是,既然多選記錄搞定了,那么單選呢,也應(yīng)該在每次點(diǎn)擊的時(shí)候搞定他吧?那是自然!
這里我剛剛突然又想到了一個(gè)解決方法,于是這里我將呈現(xiàn)倆個(gè):
1.那就是我當(dāng)時(shí)腦殘的解決方法,不過這種方法唯一的好處可能是,
產(chǎn)品大大過來說,那sei,你把選項(xiàng)中的ABCD去掉吧,不好看,那我就傻逼了。
事實(shí)上,本來人家設(shè)計(jì)稿里選項(xiàng)處就沒有ABCD,我本著你好我好大家好的原則,說服了他們加上的。。。。。
不廢話了,我發(fā)現(xiàn)我進(jìn)入中年了,絮絮叨叨總是進(jìn)不了正題,或許這和我上課愛走神有關(guān)吧。
//索引0-3對應(yīng)答案A-B,依次類推 // 注意,這里看看最多的選項(xiàng)是多少個(gè),進(jìn)行下配置,當(dāng)前只是配置到了F switch(index){ case 0: this.chooseNumStr = 'A'; break; case 1: this.chooseNumStr = 'B'; break; case 2: this.chooseNumStr = 'C'; break; case 3: this.chooseNumStr = 'D'; break; case 4: this.chooseNumStr = 'E'; break; case 5: this.chooseNumStr = 'F'; break; }
沒錯(cuò),還是在choose中,我判斷是單選后,用switch來判斷index的值,進(jìn)而匹配到chooseNumStr的結(jié)果。
雖然這種方法很笨拙,而且有超出設(shè)置范圍的選項(xiàng)的危險(xiǎn),但是,我傻?。∧挠惺裁捶椒?!
當(dāng)初就是覺得這么干很不妥,可是直到今天我再看自己的代碼才想到更好的解決方案的啊!那他是啥?。?!那就是:
2. 就還是強(qiáng)大的ref登場,規(guī)則和選擇多選一樣,只不過不用for循環(huán)。你是不是已經(jīng)想到了啊哈!
對的,每次單選點(diǎn)的是那個(gè)就截取 this.$refs.liId[i].innerText.substring(0,1);
簡直soeasy
好了,第四個(gè)問題 單選的答案記錄 問題解決。
然后,我們接著趁熱打鐵(才發(fā)現(xiàn)他和趁火打劫好像是兄弟啊?。?解決下邊點(diǎn)擊按鈕的問題。
需求是沒選是灰色,選擇選項(xiàng)后可提交:
首先是兩個(gè)按鈕的結(jié)構(gòu),為了避免后期下一題和提交按鈕的交班時(shí)我還得判斷點(diǎn)擊事件是他倆誰和誰,
所以我用了兩個(gè)按鈕,綁了兩個(gè)事件,把不同功能的事件分開綁定了。
.public-btn(v-if="!isLast" @click="nextItem" v-bind:class="{'public-btn-gray': unclickable}") 下一題 .public-btn(v-else @click="submitItem" v-bind:class="{'public-btn-gray': unclickable}") 提交
可以看到,除了事件我還綁定了class,那個(gè)public-btn-gray的生存與否取決于unclickable。
先說沒選是灰色的處理:
這個(gè)思路上就是肯定是默認(rèn)提交按鈕就是灰色的,也就是有著public-btn-gray類名的。
這里有一個(gè)用于描述按鈕是不可點(diǎn)擊狀態(tài)的變量unclickable,專門管理按鈕是否可點(diǎn)擊的。
初始化時(shí)是true不可點(diǎn)擊的。這樣,按鈕的gray類名public-btn-gray就加了。
邏輯上,點(diǎn)擊按鈕的時(shí)候先判斷這個(gè)值,如果為true就提示用戶要先選擇答案:
if(this.unclickable){ alert('您還沒有選擇答案哦!'); }else{// do someting you wanted;}
然后是 選擇選項(xiàng)后可提交 。
那這不好說嘛!我只要點(diǎn)擊事件一觸發(fā),就把可點(diǎn)擊狀態(tài)放開不就好了嘛!
那好,我是用戶,我在如圖第15題選擇a、c解鎖提交按鈕,然后我再點(diǎn)擊a、c抹掉我的記錄,
但這時(shí)我的提交按鈕已打開,我可以在他毫無防備的情況下趁虛而入(中華文化真博大,這是第三個(gè)同意義的成語了)!哈哈哈。
這當(dāng)然不可以了,直接點(diǎn)擊事件就放開下一題按鈕,在單選場景下是通的。但是多選的時(shí)候我們還要再防御一層。
那就是:
// 置灰提交按鈕與否 if(this.chooseNumStr.length > 0){ //多選的時(shí)候,因?yàn)樵俅吸c(diǎn)擊會把記錄抹除,所以chooseNumStr會是動(dòng)態(tài)改變的, //如果一個(gè)也沒選擇,多選也好單選也罷,這個(gè)字符串肯定是空的,故而判斷長度小于0就不讓他提交! this.unclickable = false; }else{ // 沒有選東西,就置灰按鈕 this.unclickable = true; }
耶!第六點(diǎn) 多選功能與下一題 按鈕高亮可跳轉(zhuǎn)功能 的結(jié)合也完成啦
至此,關(guān)于按鈕的樣式和邏輯就完畢了,每次點(diǎn)擊下一題下一題的功能就跑通了。
但是,一直跑到 第十四題點(diǎn)擊下一題 ,15題內(nèi)按鈕文案還是下一題,可是這是最后一題了啊,講點(diǎn)理吧!
好,那就講理點(diǎn),讓他改成提交,這時(shí)下一題和提交按鈕換崗。
換崗的時(shí)機(jī)我是在數(shù)據(jù)響應(yīng)回來后判斷本題目的題號/id,如果是14題,那么下一題就是最后一題,點(diǎn)擊下一題就讓提交按鈕上崗,下一題退休。
說了這么多,說的最多的是點(diǎn)擊下一題。所以在下一題里綁定的事件,就有一個(gè)角落是來干這個(gè)事的:
// 下一題 if(_this.state.ExamInfo.QuestionID == 14){ //點(diǎn)擊下一題,數(shù)據(jù)響應(yīng)回來后,新數(shù)據(jù)替換前,判斷如果當(dāng)前是第14題就改變按鈕。 //判斷切換下一題和提交按鈕 _this.isLast = true; }
然后,提交和下一題倆按鈕的樣式就靠這個(gè)狀態(tài)值控制,只要在事實(shí)的時(shí)候改變狀態(tài)值讓他倆交崗即可。
(仔細(xì)總結(jié)會發(fā)現(xiàn),都是這么一個(gè)套路,數(shù)據(jù)改變某個(gè)狀態(tài)值,狀態(tài)值綁定在結(jié)構(gòu)上,影響視圖的不同展示)
后來,還發(fā)現(xiàn)一個(gè)隱藏的問題:
點(diǎn)擊下一題后,因?yàn)槭菃雾搼?yīng)用,頁面結(jié)構(gòu)和數(shù)據(jù)都沒有刷新, 上一道題用戶選擇的結(jié)果綁在li上邊的樣式還需要清空,
所以每次點(diǎn)擊下一題甚至提交后都需要在重新填新題目數(shù)據(jù)時(shí)把li的樣式選中都清空,也就是把類名都清空。
// 樣式清空 for (let i = 0; i < _this.$refs.liId.length; i++) { _this.$refs.liId[i].className = ''; }
也需要把上一題的選擇數(shù)據(jù)清空,也就是 chooseNumStr字符串='';
且如果用戶翻到下邊再還數(shù)據(jù),雖然用戶看著像換了頁面,但其實(shí)還在這一頁。為了把假象做的更逼真點(diǎn),需要頁面定位到頂部:
// 點(diǎn)擊下一題,新頁面應(yīng)該定位到頂頭題干位置 document.body.scrollTop = 0;
正當(dāng)我看著這個(gè)天衣無縫的假功能玩的開心的時(shí)候,測試大大跑過來說:
~我快速點(diǎn)擊多次提交就提交了好多次。。
~exm??!你沒事一直點(diǎn)提交干嘛?
~我是測試
~好,大大,你別說了,我這就改嘎。
第⑧個(gè)問題: 多次點(diǎn)擊下一題/提交按鈕
好吧,這個(gè)問題確實(shí)是我沒考慮到,以后做這種表單提交的,肯定要防御用戶多次點(diǎn)擊提交。
有了上面幾次的經(jīng)驗(yàn),我現(xiàn)在很會利用data里某個(gè)變量來充當(dāng)狀態(tài)記錄了!
定義一個(gè)變量isClicked專門用于看管按鈕是否被提交過,如果在可點(diǎn)擊的狀態(tài)下點(diǎn)擊過,那么抱歉,邏輯中斷!
初始化這個(gè)isClicked肯定是沒有點(diǎn)擊狀態(tài),為false,然后在下一題和提交按鈕的點(diǎn)擊事件中判斷
if(!this.isClicked){//沒點(diǎn)擊過 //該干啥干啥! }else{ //該干嘛干嘛去! }
所以,到底應(yīng)該干嗎?!
終于說到最后,我好困,如果不是自娛自樂我可能坐著睜眼就睡著了,不,我已經(jīng)進(jìn)入夢鄉(xiāng)了
說到拿響應(yīng)結(jié)果,,這無非就是 根據(jù)相應(yīng)結(jié)果彈層 而已,我不想說什么了。
睡了。晚安世界~
以上是“vue如何實(shí)現(xiàn)自定義多選與單選的答題功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。