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