Reputation: 661
I have the following input:
<input :value="inputAmount" @input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
I don't want 2-way binding with inputAmount
because I want to clean the input of non-numeric characters in the handleAmountInput()
function whenever the user inputs something:
handleAmountInput(e) {
const cleanInput = e.target.value.replace(/\D/g, '');
this.inputAmount = cleanInput;
console.log(cleanInput);
},
The issue is, the input itself doesn't reflect this cleaned up string set to inputAmount
. If I show inputAmount
in a separate element or console.log
it like in the snippet above, it shows up just fine, but binding the value to the input with :value
doesn't seem to work and shows the full inputted string, including non-numeric characters. What am I doing wrong here and how do I get the input to show the cleaned up string?
Upvotes: 3
Views: 2430
Reputation: 1484
While the above solution by Michal Levý works, it results in invalid values when you observe the inputAmount
value. For instance when you type 12a
, the JS console will print:
I think a better pattern is to use value
, and force the component to rerender using this.$forceUpdate()
. In this case, you will see:
Note that SolidJS can also enter this inconsistent state. ReactJS, however, will restore the DOM element to the state of the VDOM and will not become inconsistent.
A better solution I think for VueJS is to always update the DOM element that triggered an event when it did not trigger a re-render so we do
const app = Vue.createApp({
data() {
return {
inputAmount: '12'
}
},
watch: {
inputAmount(value) {
console.log('inputAmount was', value);
// using v-model, this watch would trigger with invalid values
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
// if this.inputAmount does not change, vuejs does not rerender
// so, force it
this.$forceUpdate()
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input v-model="inputAmount" @input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Upvotes: 0
Reputation: 1
use v-model="inputAmount"
? please see: https://cn.vuejs.org/v2/guide/forms.html
then you can just edit like this.inputAmount= this.inputAmount.replace(/\D/g, '');
Upvotes: 0
Reputation: 5
Use computed getter setter instead, Link :
example :
computed: {
inputAmount: {
get(){
//perform your logic
return 'value'
},
set(newValue){
this.value= newValue;
}
}
}
Upvotes: 0
Reputation: 37953
I'm not yet sure why exactly your code doesn't work as I would expect it to, but the way to fix it is to use both v-model
and @input
handler at the same time...
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input v-model="inputAmount" @input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Update
Ok, I now understand the reason why your code does not work. What happens:
inputAmount
is for example '123'
(reflected in <input>
)a
@input
handler is called. It receives the value '123a'
, do it's job creating cleaned value '123'
and assigns it into inputAmount
inputAmount
did not changed at all so no re-render is required and <input>
still shows '123a'
even tho inputAmount
has a value of '123'
So another way of fixing your code is just to assign some value <input>
can never produce into inputAmount
1st just to trigger the update (demo below)
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = null
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue@3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input :value="inputAmount" @input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Upvotes: 4
Reputation: 620
Have you tried using @change event
<input :value="message" @change="getInput($event)" placeholder="edit me" />
Upvotes: 0