Search code examples
javascriptvue.jsbabeljs

how to bind v-model to child component that contains input


I have some components that look like this.

<template>
  <q-layout>
    <v-input v-model="something" />
  </q-layout>
</template>

<script>
import { QLayout } from 'quasar'
import { Input } from 'vedana'

export default {
  name: 'index',
  components: {
    QLayout,
    Input
  },
  data () {
    return {
      something: ''
    }
  }
}

this v-input component looks like this:

<template>
    <input
        :type="type ? type : 'text'"
        class="v-input"/>
</template>

<script>
export default {
    props: ['type'],
    name: 'v-input'
}
</script>

When I enter data into the input something does not bind to whatever is in the value of the input that is inside of v-input.

How do I achieve this?


Solution

  • To enable the use of v-model the inner component must take a value property.

    Bind the value to the inner <input> using :value, not v-model (this would mutate the prop coming from the parent). And when the inner <input> is edited, emit an input event for the parent, to update its value (input event will update the variable the parent has on v-model).

    Also, if you have a default value for the type prop, declare it in props, not in the template.

    Here's how your code should be

    <template>
        <input
            :type="type"
            :value="value"
            @input="$emit('input', $event.target.value)"
            class="v-input" />
    </template>
    
    <script>
    export default {
        props: {
          type: {default() { return 'text'; }},
          value: {}                              // you can also add more restrictions here
        },
        name: 'v-input'
    }
    </script>
    

    Info about what props can have: Components / Passing data With Props.

    Demo below.

    Vue.component('v-input', {
      template: '#v-input-template',
      props: {
        type: {default() { return 'text'; }},
        value: {}                              // you can also add more restrictions here
      },
      name: 'v-input'
    });
    
    new Vue({
      el: '#app',
      data: {
        something: "I'm something"
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    
    <div id="app">
      <p>Parent something: {{ something }}</p>
      <hr>
      Child: <v-input v-model="something" />
    </div>
    
    <template id="v-input-template">
       <input
          :type="type"
          :value="value"
          @input="$emit('input', $event.target.value)"
          class="v-input" />
    </template>