最近在改一個舊專案的 input 欄位的千分位問題,它一般可以正常運作,也就是輸入到超過千位時,每三位數自動加上千分位點。但是有時候壞掉,就會變成出現千分位點之後,輸入的數字會重複,我檢查之後發現是一次輸入會觸發兩次 input 事件。
前輩看過之後表示,這個 input 元件寫得太肥,props 傳入許多用不到的參數,很難測出是哪個環節出了問題,不如重寫一個。所以我就去研究了 input 欄位判斷千分位點的作法。
先假設有一個父元件會打 API 接收一個之前填入的數值,用 props 傳入設有 <input>輸入框的子元件,當作預設的數值。
<template>
<div id="app">
<inputNumber :base="baseValue"></inputNumber>
</div>
</template>
<script>
import inputNumber from './components/InputNumber.vue';
export default {
components: {
inputNumber,
},
data() {
return {
baseValue: 223,
};
},
};
</script>
子元件的部分,用base 這個 props 承接父元件傳入的 baseValue。
因為單向資料流的關係,我們不能直接去修改父元件傳入的資料,所以在 mounted 用 value
這個變數承接 base 的資料。
<template>
<div>
<input v-model="formatterValue" />
</div>
</template>
<script>
// <https://codesandbox.io/p/sandbox/vue-template-s6jo9?file=%2Fsrc%2FApp.vue%3A1%2C1-61%2C1>
// <https://stackoverflow.com/questions/63305785/vuejs-add-thousands-separator-to-input>
export default {
props: {
base: {
type: Number,
default: Infinity,
},
},
data() {
return {
value: '',
};
},
mounted() {
this.value = this.base;
},
};
</script>
然後在 methods 設計一個加上千分位點的 formatValue(num) 函式及 parseValue(text) 去除千分位點的函式。
methods: {
formatValue(num) {
// 加上千分位
return Number(num)
.toString()
.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');
// `${value}`.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ','
// /(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, '$1,'
},
parseValue(text) {
// 去除千分位
return Number(text.replace(/,/g, ''));
},
},
然後在 computed 中設計一個 getter,偵測 input 輸入的數值,加上千分位點。setter 的部分則是去除千分位點,寫回 value。
computed: {
formatterValue: {
// getter 加上千分位
get: function () {
if (this.value !== '') {
return this.formatValue(this.value);
}
},
// setter 複寫 value
set: function (newValue) {
this.value = this.parseValue(newValue);
},
},
},
然後在 <input> 用 v-model 綁上 computed 的 formatterValue。
<input v-model="formatterValue" />
這樣就大功告成了,但是如果輸入文字,input欄位就會出現 NaN,這部分要再想想怎麼解決。
子元件原始碼:
<template>
<div id="app">
<h4>外部傳入{{ base }}</h4>
<h3>內部承接{{ value }}</h3>
<input v-model="formatterValue" />
</div>
</template>
<script>
export default {
props: {
base: {
type: Number,
default: Infinity,
},
},
data() {
return {
value: '',
};
},
computed: {
formatterValue: {
// getter 加上千分位
get: function () {
if (this.value !== '') {
return this.formatValue(this.value);
}
},
// setter 複寫 value
set: function (newValue) {
this.value = this.parseValue(newValue);
},
},
},
methods: {
formatValue(num) {
// 加上千分位
return Number(num)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
},
parseValue(text) {
// 去除千分位
return Number(text.replace(/,/g, ''));
},
},
mounted() {
this.value = this.base;
},
};
</script>
