Home > Enterprise >  Vue.js v-select with v-if
Vue.js v-select with v-if

Time:06-29

I need a little help with v-select. I would like to have two v-select, but one will change when I choose a value on the over. For example :

I have a list of countries (France, England and Spain) I have a list of districts (Alsace, Lorraine, Greater London, Andalousie and Catalogne)

In my first v-select (build like this : )

<v-select style="grid-column: 1; width: 100%" :options="pays" label="title" v-model="paysChoose"></v-select>

I have my list of countries, and I have a v-model to get the name of the country choose. After that, I have a second v-select (build like this : )

<v-select style="grid-column: 1; width: calc(100%-50px)" :options="regions" v-if="regions.pays == paysChoose" label="titre" v-model="regionChoose"></v-select>

I try to filter the list of district in terms of the country selected. So I try with v-if, but It doesn't work (the template doesn't appear). After that I try to filter with a computed value and I try this :

<v-select style="grid-column: 1; width: calc(100%-50px)" :options="renderRegion" label="titre" v-model="regionChoose"></v-select>
computed: {
          renderRegion() {
            return this.regions.filter(item => item.pays == this.paysChoose)
          }
        }

But it still doesn't work (The template appear, but never print the districts when I choose a country)

If someone have a solution i really appreciate it

Thanks by advance

CodePudding user response:

In my opinion, your computed idea seems to be the best one according to your problem.

Note : that I added a clear of the subcountry if the value of the country is changed.

Example

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  computed: {
    subCountryItemsFiltered() {
      return this.subCountry.filter(subCountry => subCountry.country === this.countrySelected)
    }
  },
  data() {
    return {
      country: ['france', 'spain', 'uk'],
      countrySelected: null,
      subCountry: [
        {value: 'Alsace', country: 'france'},
        {value: 'Catalogne', country: 'spain'},
        {value: 'Greater London', country: 'uk'},
      ],
      subCountrySelected: null
    }
  },
})
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css'>
<script src='https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js'></script>
<script src='https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js'></script>

<div id="app" data-app>
  <v-select :items="country" v-model="countrySelected" @change="subCountrySelected = null"></v-select>
  <v-select 
  :items="subCountryItemsFiltered" 
  v-model="subCountrySelected"
  item-text="value"
  ></v-select>
</div>

CodePudding user response:

Considering the following data:

data: () => ({
  places: [
    { value: 'Alsace', country: 'Spain' },
    { value: 'Lorraine', country: 'France'},
    { value: 'Greater London', country: 'England'},
    { value: 'Andalousie', country: 'Spain' },
    { value: 'Catalogne', country: 'Spain' }
  ]
})

You can extract the countries with this computed:

  countries() {
    return [ ...new Set(this.places.map(p => p.country))]
  }

And you can get the available places for the current country using another computed:

  availablePlaces() {
    return this.places
      .filter(p => p.country === selectedCountry)
      .map(p => p.value)
  }

Example, using Options API:

const { createApp, defineComponent } = Vue;

const App = defineComponent({
  template: '#app-tpl',
  data: () => ({
    places: [
      { value: 'Alsace', country: 'France' },
      { value: 'Lorraine', country: 'France'},
      { value: 'Greater London', country: 'England'},
      { value: 'Andalousie', country: 'Spain' },
      { value: 'Catalogne', country: 'Spain' }
    ],
    selectedCountry: '',
    selectedPlace: ''
  }),
  computed: {
    countries() {
      return [...new Set(this.places.map(p => p.country))]
    },
    availablePlaces() {
      return this.places
        .filter(p => p.country === this.selectedCountry)
        .map(p => p.value)
    }
  },
  watch: {
    selectedCountry() {
      // reset selectedPlace when changing the country
      if (!this.availablePlaces.includes(this.selectedPlace)) {
        this.selectedPlace = ''
      }
    }
  }
});

createApp(App).mount('#app');
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>

<div id="app"></div>

<template id="app-tpl">
  <select v-model="selectedCountry">
    <option></option>
    <option v-for="country in countries"
            :key="country"
            v-text="country"></option>
  </select>
  <select v-model="selectedPlace">
    <option></option>
    <option v-for="place in availablePlaces"
           :key="place"
           v-text="place"></option>
  </select>
  <pre 
    v-text="JSON.stringify({selectedCountry, selectedPlace, availablePlaces }, null, 2)"></pre>
</template>

Same example, with Composition API:

const { createApp, reactive, computed, toRefs, watchEffect, defineComponent } = Vue;

const places = [
  { value: 'Alsace', country: 'France' },
  { value: 'Lorraine', country: 'France'},
  { value: 'Greater London', country: 'England'},
  { value: 'Andalousie', country: 'Spain' },
  { value: 'Catalogne', country: 'Spain' }
];

const App = defineComponent({
  template: '#app-tpl',
  setup() {
    const state = reactive({
      selectedCountry: '',
      selectedPlace: '',
      countries: [...new Set(places.map(p => p.country))],
      availablePlaces: computed(() => places
        .filter(p => p.country === state.selectedCountry)
        .map(p => p.value)
      )
    });
    watchEffect(() => {
      if (!state.availablePlaces.includes(state.selectedPlace)) {
        state.selectedPlace = ''
      }
    })
    return { ...toRefs(state) }
  }
})

createApp(App).mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>

<div id="app"></div>

<template id="app-tpl">
  <select v-model="selectedCountry">
    <option></option>
    <option v-for="country in countries" :key="country">{{country}}</option>
  </select>
  <select v-model="selectedPlace">
    <option></option>
    <option v-for="place in availablePlaces" :key="place">{{place}}</option>
  </select>
  <pre v-text="JSON.stringify({selectedCountry, selectedPlace, availablePlaces }, null, 2)"></pre>
</template>

  • Related