ui庫用的是iview .
radio、radioGroup是咱們很是經常使用的組件。radio有一個特徵是選中以後沒法取消。現實中取消radio的需求是常見且能夠理解的。
因此看到這個需求以後第一嘗試 在iview組件之上搞一搞,這一搞就入坑了,如今就來理一理個人入坑之路吧。css
首先咱們來看一下在 vue 中使用原生的radio組件如何使用取消。 原生radio取消選中的文章很是多,隨便拎一篇看看就行,好比 取消radio的三種方法。
嗯,原理就是給radio元素的checked屬性賦值爲 false。
具體在 vue 中使用是這樣的:vue
const { name, value } = data; <div> <input class="xxx" type="radio" name={this.id} ref={name} value={value} onClick={(vlaue) => this.radioGroupChange(value, name)} /> {name} </div>; // 方法 radioGroupChange (value, name) { if (this.checked === value) { // 取消選中 this.$refs[name].checked = false; // 當前選中值爲空 this.checked = ''; } else { this.checked = value; } }
這樣就OK了。大概惟一區別是 在 vue中經過 ref取到真實 dom。vuex
借鑑原生的radio,看來取消也不難嘛,監聽radio或者 radio的change事件嘛。然而你會發現,重複點選某一個 radio的時候,iview中的on-change函數跟本木有響應。這是爲何呢? 去 iview 源碼中看一看瀏覽器
先上代碼爲敬:
先看下 radio.vue的 templateiview
<template> <label :class="wrapClasses"> <span :class="radioClasses"> <span :class="innerClasses"></span> <input type="radio" :class="inputClasses" :disabled="disabled" :checked="currentValue" :name="groupName" @change="change" @focus="onFocus" @blur="onBlur"> </span><slot>{{ label }}</slot> </label> </template>
再看下 change的響應函數dom
change (event) { // debugger if (this.disabled) { return false; } const checked = event.target.checked; this.currentValue = checked; const value = checked ? this.trueValue : this.falseValue; this.$emit('input', value); if (this.group) { if (this.label !== undefined) { this.parent.change({ value: this.label, checked: this.value }); } } else { this.$emit('on-change', value); this.dispatch('FormItem', 'on-form-change', value); } },
一開始懷疑 change事件中對 value作了處理,只有 不同的時候才函數
emit,仔細看,change函數中並無這部分處理。
問題在於 input 這裏監聽的 change 事件而不是 click事件,因此反覆點擊同一個 radio的時候沒有 響應是正常的。
佈局
看到上面是否是就想 我在 radio上綁定一個click 事件就能夠了;事實證實是不能夠的,想一下就知道了,由於 iview並無監聽click事件天然也沒有把click事件 emit到父組件,因此是沒法監聽click的。
既然 Radio 不支持click,那就想辦法在能讓元素響應 click 唄測試
這裏的思路是在 RadioGroup的Radio 外包裹一層 div/span,在這個元素上綁定 click 函數,這個元素確定會響應 click,在響應函數中判斷 當前 選中的 value(這個變量一般會維護在data中,暫且用 this.value 表示) 和點擊元素包裹的 value 值是否相同,相同則將 this.value 置爲空,則能夠達到取消的效果。
通過實踐,該方法是可行的。 值得注意的是這種狀況下須要取消RadioGroup的on-change事件監聽,不然radio改變了this.value,會觸發RadioGroup的on-change事件,致使沒法取消。 還有一個弊端是 須要給 Radio增長一個包裹元素,可能還須要對元素寫個樣式,不要影響到原Radio 元素佈局, 那麼還有沒有更好的方法呢?ui
vue中提供了監聽原生事件的修飾符,在jsx形式的vue監聽原生事件的寫法以下:
<Radio nativeOnClick={this.handleRadioClick.bind(this, value)} label={value} disabled={disabled} > <span>{label}</span> </Radio>
因此咱們不用增長元素也能監聽到click事件,接下里就能夠在響應函數中 處理取消/選中的邏輯了。
嗯, 終於找到一個很好的綁定 click的方法,然而測試的時候,卻發現 點擊一次卻響應了 兩次點擊函數,百思不得其解嗎? 這時候 baidu 或者google給了咱們答案: 一個文章說的特別好,解釋了兩次的緣由:
以下文章連接:
觸發的事件源分別爲input和label;
觸發條件很簡單:
一、監聽的是label和input的上層元素click事件
二、label和input關聯(for或者input在label下)
問題緣由::
點擊label的時候,事件冒泡一次,同時會觸發關聯的input的click事件,致使事件再次冒泡。
解決辦法:
方法有很中拉,
有了上面的分析,下面咱們完整是總結一下 讓radio能夠取消的步驟:
注意: 我下面寫的是 vue的jsx 形式,因此若是是vue形式,請自行修改。
首先: template
<RadioGroup onOn-change={this.sync.bind(this)} value={this.value} type={this.mode} size={this.size} style={this.css} class={this.theme} > <Radio nativeOnClick={this.handleRadioClick.bind(this, value)} label={value} disabled={disabled} > <span>{label}</span> </Radio> <Radio nativeOnClick={this.handleRadioClick.bind(this, value)} label={value} disabled={disabled} > <span>{label}</span> </Radio> </RadioGroup>
data 和 methods:
data() { return { value: '' } } methods: { handleRadioClick (value) { let now = +new Date(); if (now- this.evTimeStamp < 100) { return; } this.evTimeStamp = now; value = this.value === value ? '' : value; this.update(value); // 能夠理解爲vuex 通知更新 this.value }, }
限於自己實現還有其餘關聯部分,這裏沒有加上 更新 this.value具體代碼,可是我相信看到這裏應該已經知在使用 ui 庫的狀況如何 是 radio 能夠取消了。
遇到問題歡迎評論提問,不足之處也歡迎指正。