'VueSelect How to set initial value with object's property

I'm using VueSelect for address select. There are 3 select components for country, state, city.

With using v-model, it's working well to get the specified value instead of option object by using reduce prop. Here my problem is to not set default value by key value.

I expected that it set the default value by id like html select. but it's showing the key value as it is, not being bound by the label value.
Hope you can make sense from my explanation.
Expecting like v-select of vuetify. vuetify example

<template>
  <div class="col-sm-6 ">
    <v-select
        v-model="address.country_id"
        :options="countryOptions"
        :reduce="country => country.id"
        label="name"
        @input="resetStateAndCity"
        :resetOnOptionsChange="true"
    />
  </div>

  <div class="col-sm-6">
    <v-select
        v-model="address.state_id"
        :options="stateOptions"
        :reduce="state => state.id"
        label="name"
        @input="resetCity"
        :resetOnOptionsChange="true"
    />
  </div>

  <div class="col-sm-6">
    <v-select
        v-model="address.city_id"
        :options="cityOptions"
        :reduce="city => city.id"
        label="name"
        :resetOnOptionsChange="true"
    />
  </div>
</template>
<script>
  import VSelect from 'vue-select'

  export default {
    components: {
      VSelect
    },
    data: function () {
      return {
        address: {
          city_id: null,
          state_id: null,
          country_id: null
        },
        countryOptions: [],
        stateOptions: [],
        cityOptions: []
      }
    },
    mounted() {
      this.$axios.get('/countries.json')
        .then(response => {
          this.countryOptions = response.data
        })
    },
    methods: {
      resetStateAndCity() {
        if (!this.address.country_id) return
        this.$axios.get(`/countries/${this.address.country_id}/states.json`)
          .then(response => {
            this.stateOptions = response.data
          })
        this.address.state_id = null
        this.address.city_id = null
        this.cityOptions = []
      },
      resetCity() {
        if (!this.address.country_id || !this.address.state_id) return
        this.address.city_id = null
        this.$axios.get(`/countries/${this.address.country_id}/states/${this.address.state_id}/cities.json`)
          .then(response => {
            this.cityOptions = response.data
          })
      }
    }
  }
</script>


Solution 1:[1]

I've outlined two different approaches below. Overall I think I prefer the second approach but it may depend on your circumstances which works best for you.

new Vue({
  el: '#app',

  components: {
    'v-select': VueSelect.VueSelect
  },

  data: function() {
    return {
      input: {
        user_id: 2
      },
      users: []
    }
  },
  
  methods: {
    getOptionLabel (option) {
      return (option && option.name) || ''
    }
  },
  
  created () {
    setTimeout(() => {
      this.users = [
        {
          id: 1,
          name: "John",
          last: "Doe"
        },
        {
          id: 2,
          name: "Harry",
          last: "Potter"
        },
        {
          id: 3,
          name: "George",
          last: "Bush"
        }
      ]
      
      const currentUser = this.users.find(user => user.id === this.input.user_id)
      
      this.$refs.select.updateValue(currentUser)
    }, 1000)
  }
})
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/vue-select.css">
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue-select.js"></script>
<div id="app">
  <v-select
    ref="select"
    v-model="input.user_id"
    :options="users"
    :reduce="user => user.id"
    :get-option-label="getOptionLabel"
  ></v-select>
</div>

A major hurdle is that Vue Select uses internal state to track the currently selected value when reduce is used. See:

https://github.com/sagalbot/vue-select/blob/master/src/components/Select.vue#L833

Worse, the value stored in that state is not the reduced id but the whole object from the options. So if there aren't any options initially it gets in a right mess and it isn't particularly easy to coax it back out of that mess.

I've used a call to updateValue to give it a kick after the options are loaded. I've also used the prop get-option-label rather than label to avoid showing the number while the data is loading.

An alternative would be to only create the v-select once the options are available, as it works fine if they're all present when it's first created. A dummy v-select could be used as a placeholder while waiting for the data. That looks like this:

new Vue({
  el: '#app',

  components: {
    'v-select': VueSelect.VueSelect
  },

  data: function() {
    return {
      input: {
        user_id: 2
      },
      users: []
    }
  },
  
  created () {
    setTimeout(() => {
      this.users = [
        {
          id: 1,
          name: "John",
          last: "Doe"
        },
        {
          id: 2,
          name: "Harry",
          last: "Potter"
        },
        {
          id: 3,
          name: "George",
          last: "Bush"
        }
      ]
    }, 1000)
  }
})
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/vue-select.css">
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue-select.js"></script>
<div id="app">
  <v-select
    v-if="users.length === 0"
    key="dummy"
    disabled
    placeholder="Loading..."
  ></v-select>
  <v-select
    v-else
    v-model="input.user_id"
    :options="users"
    :reduce="user => user.id"
    label="name"
  ></v-select>
</div>

The use of the key here is important as it ensures that Vue will not reuse the same v-select and will instead create a new one. There are other ways to achieve that but a key is probably the cleanest.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 skirtle