Search code examples
vue.jsvuetify.jsv-select

Get value of clicked item in Vuetify multiple select component


I have the following v-select in my code:

<v-select
    v-if='d.length'
    v-model='ci'
    :items='d'
    item-text='value.name'
    item-value='value.name'
    label='label'
    multiple='multiple'
    height='60'
    small-chips
    single-line
    solo
    @change='itemChanged'
  >
  <template v-slot:prepend-item v-if='multiple && title && d.length'>
    <v-list-tile
      ripple
      @click="action"
    >
      <v-list-tile-action>
        <v-icon :color="ci.length > 0 ? 'indigo darken-4' : ''">{{ icon }}</v-icon>
      </v-list-tile-action>
      <v-list-tile-content>
        <v-list-tile-title>{{title}}</v-list-tile-title>
      </v-list-tile-content>
    </v-list-tile>
    <v-divider class="mt-2"></v-divider>
  </template>
  <template v-slot:selection="{ item, index }">
    <v-chip v-if="index === 0">
      <span>{{ item.text }}</span>
    </v-chip>
    <span
      v-if="index === 1"
      class="grey--text caption"
    >(+{{ checkedItems.length - 1 }} others)</span>
  </template>
</v-select>

It receives its model, items and other defs as props. Model and Items are identical arrays of objects with the following structure:

{text: 'text', value: {name: 'foo'}}

So essentially all the items are selected when the component is mounted.

Once the user clicks on an item from the list, I want to receive in my itemChanged method either the entire object, or at least the value object. For time being I only want to console log the received object:

itemChanged(value) {
  console.log('Changed item', value);
}

But it prints the entire model array, minus the clicked item

Tried to use return-object, tried to change the item-value and change the objects structure - always the same result.

Any ideas how can I get only the clicked item object/value?


Solution

  • Does something like this work, or am I misunderstanding your question? This outputs selected items (as objects) back on the page versus console.log(...).

    CodePen mirror


    Edit: (answering your question below) ~~Slot Props~~: (not to be confused with 'named slots') essentially allow you to take properties from the child component and use them to render in the parent. You can read more on scoped slots (also known as 'slot props') here

    Take the follow code block for example:

              <template v-slot:item='data'>
                <v-list-tile-content>
                  <v-list-tile-title>
                    {{ data.item.firstName }} {{ data.item.lastName }}
                  </v-list-tile-title>
                </v-list-tile-content>
              </template>
    

    v-slot:item='data' - you could use any name you wanted in place of data: v-slot:item="theItems" would also work (note: you would then use {{ theItems.item.firstName }}...

    The reason you have to use data.ITEM.x (referring to ITEM) is because that is what Vuetify calls that scoped slot for v-select - you can read more on that here .. Hope this helps!

    enter image description here


    new Vue({
      el: "#app",
      props: {
        value: {
          type: [String, Object]
        }
      },
      data() {
        return {
          chosenItems: [],
          items: [{
              firstName: "John",
              lastName: "Smith",
              Age: 44
            },
            {
              firstName: "Sarah",
              lastName: "Martin",
              Age: 32
            },
            {
              firstName: "Derick",
              lastName: "Johnson",
              Age: 39
            },
            {
              firstName: "Mary",
              lastName: "Spitzer",
              Age: 22
            },
            {
              firstName: "Wendy",
              lastName: "Macdonald",
              Age: 57
            },
          ]
        };
      },
      computed: {
        selectedItems: {
          get() {
            return this.value;
          },
          set(item) {
            // Could either emit (so you can use v-model on the parent)
            // or add to array
            this.chosenItems.push(item)
            this.$emit("input", item);
          }
        }
      }
    });
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet" />
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"></script>
    
    
    <div id="app">
      <v-app>
        <v-content>
          <v-container>
            <v-layout row>
              <v-flex>
                <v-select 
                  v-model='selectedItems' 
                  label='Select One Or Many' 
                  :items="items" 
                  item-text="firstName" 
                  chips 
                  clearable 
                  multiple 
                  return-object
                >
                  <template v-slot:selection='data'>
                   <v-chip
                    :key="JSON.stringify(data.item)"
                    close
                    class="chip--select-multi"
                    @input="data.parent.selectItem(data.item)"
                   >
                     {{ data.item.firstName }} {{ data.item.lastName }}
                    </v-chip>
                  </template>
                  <template v-slot:item='data'>
                    <v-list-tile-content>
                      <v-list-tile-title>
                        {{ data.item.firstName }} {{ data.item.lastName }}
                      </v-list-tile-title>
                    </v-list-tile-content>
                  </template>
                </v-select>
              </v-flex>
            </v-layout>
            <div class="mt-5">
              <v-layout>
                <v-flex>
                  <h3>Chosen Items Will Be Displayed Here:</h3>
                </v-flex>
              </v-layout>
              <div v-for="(chosen, index) in chosenItems">
                <hr/>
                <div v-for="(eachChosen, i) in chosen">
                  {{ eachChosen }}
                </div>
                <hr/><br/>
              </div>
            </div>
          </v-container>
        </v-content>
      </v-app>
    </div>