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