javascriptvue.jsvue-componentvuejs3

Vue3 use v-model in Child Components


I just found out that in Vue3, v-model is not working responsively / reactively with child Component.

This code will update the username data

<template>
  <div>
    <input type="text" v-model="username" placeholder="Insert your username" />
    <p>{{ username }}</p>
  </div>
</template>

<script>
// Home.vue
export default {
  name: 'Home',
  data() {
    return {
      username: 'admin'
    }
  }
}
</script>

If I type something in the input, the username data will change too.

But, when I use Component like this example:

<template>
    <input type="text" :class="'input-text ' + additionalClass" :placeholder="placeholder" />
</template>

<script>
// InputText.vue
import { defineComponent } from "vue"

export default defineComponent({
    name: 'InputText',
    props: {
        placeholder: {
            type: String,
            default: ''
        },
        additionalClass: {
            type: String,
            default: ''
        }
    }
})
</script>

Then I updated my code to use the Component.

Note: The Component is registered successfully.

<template>
  <div>
    <input-text v-model="username" :placeholder="`Insert your username`" />
    <p>{{ username }}</p>
  </div>
</template>

<script>
// Home.vue
export default {
  name: 'Home',
  data() {
    return {
      username: 'admin'
    }
  }
}
</script>

When I type something, the username data not updated, different with the previous one.

Is there any solution or at least reference of what I'm trying to achieve?


Solution

  • You can't expect v-model to implicitly update the underlying element for you. In other words, you'll still need to handle that within the component itself and expose modelValue as a prop for this to really work. Something like that:

    <template>
      <input
        type="text"
        @input="onChanged"
        :value="modelValue"
        :class="'input-text ' + additionalClass"
        :placeholder="placeholder" />
    </template>
    
    <script>
      // InputText.vue
      import { defineComponent } from "vue"
    
      export default defineComponent({
        name: 'InputText',
    
        emits: ['update:modelValue'],
    
        props: {
          modelValue: String,
          placeholder: {
            type: String,
            default: ''
          },
          additionalClass: {
            type: String,
            default: ''
          }
        },
    
        setup(props, { emit }) {
          function onChanged(e) {
            emit('update:modelValue', e.currentTarget.value);
          }
    
          return {
            onChanged
          }
        }
      })
    </script>