VueJS 與 Input Number 的兩三事
一些我在開發過程中踩到的雷
用 VueJS 開發前端時,若要讓使用者自行輸入數值(數值必須是大於零的整數,且不能超過一個特定值),或是透過旁邊的按鈕加減數值,初步的想法大概是這樣:
<input type="number" v-model="quantity" :min="min" :max="max" />
<button @click="subQuantity"> - </button>
<button @click="addQuantity"> + </button><script>
export default {
data() {
return {
max: 99,
min: 1,
quantity: 1
}
},
methods: {
addQuantity() {
if (this.quantity < this.max) this.quantity++;
},
subQuantity() {
if (this.quantity > this.min) this.quantity--;
},
}
}
</script>
看似沒什麼問題,但實際測試時出現許多 bug。。。
一、數值突然變字串
使用自訂的按鈕加加減減,或透過鍵盤的上下鍵操作都能很快計算並修改數值;但只要一自行輸入,兩個數值就直接合併起來了,數值相加變成了字串相接 (囧)。
雖然將 <input> 的 type 設成 number 了,但 html 實際回傳的 value 依然是 string,若希望能夠自動轉換型態,可使用 v-model
的 Modifiers 。
<input type="number" v-model.number="quantity" :min="min" :max="max" />
二、標籤內的 min 與 max 屬性只對鍵盤事件有用
<input> 標籤內雖然有設置 min 與 max 屬性,但這似乎只對鍵盤的上下鍵調整數值時有用,當按著上下鍵遞增或遞減數值時,會自動停在標籤設置的最大或最小值(自定義的按鈕因有設條件,因此也不會超過);但自行輸入若超過邊界則不會被限制住。
也許再加個 onchange
檢驗一下就可以了吧。
<input type="number" v-model.number="quantity" :min="min" :max="max" @change="verifyQuantity" />
三、數值可以輸入 e 跟負號是常識?
e 是數學符號、數值可以輸入負數,可以輸入這兩個符號是想當然的事情,也不是第一天知道;但像這種『12e43』、『432–221e213』、『ee2e-』、『 −−23−−323−−』也可以輸入真是頭一次發現。。。
反正在我們的 case 中也不會輸入這兩個符號,索性將它們全鎖了吧!問題是。。。怎麼做呢?
四、標籤內的 pattern 屬性只在表單中有用
看到 <input> 標籤內有 pattern
屬性時開心了一下,先前寫不少正規表達式,這點要求可難不倒我。但是後來發現這個 pattern
屬性只有在送表單時才會被觸發 Orz
五、連 onchange 也不行?!
既然 pattern
的方式不行,那就在 onchange
事件中處理吧;就在寫好判斷繼續測試時,竟發現連onchange
也不會被觸發 (囧)
把輸入的數值實際印出來觀察,發現上面那些亂打的數值都會被前面所講的 v-model
的 Modifiers 轉為空字串;既然都是一樣的空字串,自然也不會觸發 onchange
事件了 (苦笑)
六、NaN 不等於 NaN
在把字串透過 parseInt()
強行轉成整數時,會得到 NaN
。 NaN
的型別是 number,所以透過型別區分 NaN
跟一般數值是行不通的。有趣的是透過 value === NaN
來判斷時,即使 value
是 NaN
,也會得到 false
的結果;但用 if (! value)
來判斷時則可以得到 true
。
最後是用 computed
的雙向綁定方式來處理這個問題
<input type="number" v-model.number="verifiedQuantity" :min="min" :max="max" />
<button @click="subQuantity"> - </button>
<button @click="addQuantity"> + </button><script>
export default {
data() {
min: 1,
max: 99,
quantity: 1
}
computed: {
verifiedQuantity: {
get() {
return this.quantity;
},
set(value) {
if (! value || typeof value === 'string') {
return;
} else if (value >= this.min && value <= this.max) {
this.quantity = value;
}
}
}
},
methods: {
addQuantity() {
this.verifiedQuantity++;
},
subQuantity() {
this.verifiedQuantity--;
},
}
}
</script>