javascriptvue.jsdomvuejs3

Vue.js input value not reflecting value in component data


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?


Solution

  • 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:

    1. Value of inputAmount is for example '123' (reflected in <input>)
    2. User types a
    3. Your @input handler is called. It receives the value '123a', do it's job creating cleaned value '123' and assigns it into inputAmount
    4. From Vue POV the value of 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>