Milano
Milano

Reputation: 18745

v-model on custom Vue component not changing value on input

I'm trying to create a customCombobox component that works like a normal v-combobox with one addition - after user presses tab key, it will select the first option automatically (if there are any).

What I've done so far looks good but v-model on this field doesn't work (it's always null).

<template>
  <v-combobox ref="combobox" :rules="rules"
              @keydown.tab.prevent="selectFirst"
              :value="innerValue" :label="label"
              :items="items"

  >

  </v-combobox>
</template>
<script>
module.exports = {
  props: ['value', 'label', 'items', 'rules'],
  data() {
    return {
      innerValue:null,
    }
  },
  watch:{
    value(_new){
       this.innerValue = _new
      this.$emit('input',[_new])
      this.$emit('change')
    }
  },
  methods: {
    selectFirst() {
      var combobox = this.$refs.combobox
      var filteredItems = combobox.filteredItems
      if (filteredItems.length){
          this.innerValue = filteredItems[0]
      }
    }
  },
  computed: {

  }
}
</script>

Do you know why?

Upvotes: 3

Views: 2921

Answers (1)

Dan
Dan

Reputation: 63129

You can use a computed setter in the custom component to handle the v-model from the parent and pass it down to its own child v-combobox:

Custom component:

<v-combobox v-model="selected" v-bind="$attrs"></v-combobox>
computed: {
  selected: {
    get() {
      return this.value;
    },
    set(val) {
      this.$emit('input', val);
    }
  }
}

Using v-bind="$attrs" also passes down all of the props from the parent. Here's a demo:

Vue.component('comp', {
  props: ['value'],
    template: `
    <v-combobox v-model="selected" v-bind="$attrs"></v-combobox>
  `,
  computed: {
    selected: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit('input', val);
      }
    }
  }
})

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data() {
    return {
      selected: 'Vue',
      label: 'This is my label',
      rules: ['These are my rules'],
      items: ['Programming', 'Design', 'Vue', 'Vuetify']
    }
  }
})
#app {
  padding: 48px;
}
<div id="app">
  <v-app id="inspire">
    Selected in parent: {{ selected }}
    <comp
         v-model="selected"
         :label="label"
         :items="items"></comp>
  </v-app> 
</div>

<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>

Upvotes: 5

Related Questions