Get value of clicked item in Vuetify multiple select component

Igal picture Igal · Apr 14, 2019 · Viewed 8.8k times · Source

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?

Answer

Matt Oestreich picture Matt Oestreich · Apr 14, 2019

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>