Как изменить данные в data() при клике на элемент в v-for?

Недавно начал изучать vue, но столкнулся с проблемой: перебирая в v-for компонент и вешая на этот компонент слушатель я надеюсь "выбрать" этот элемент, поместив в activeItem id карточки, на которую кликнули, но в итоге при клике ничего не меняется. Код основного компонента:

<template>
  <div v-for="card in cards" :key="card.id">
    <Cards :Card="card" @click="this.activeItem = card.id"></Cards>
  </div>

</template>


<script>
import Cards from "@/components/Cards";
export default {
  name: 'App',
  components: {
    Cards
  },
  data() {
    return {
      cards: [
        {id: 1, name: 'first', description: 'card 1'},
        {id: 2, name: 'second', description: 'card 2'},
        {id: 3, name: 'third', description: 'card 3'}
      ],
      activeItem: 0,
    }
  },
  computed: {
  },
  methods: {
    
  }
}
</script>

<style>
</style>

Вот код компонента карточки:

<template>
  <div>
    {{Card.id}}
  </div>
  <div>
    {{Card.name}}
  </div>
</template>

<script>
export default {
 
  name: 'Cards',
  props: {
    Card: Object,
  }
}
</script>

<style scoped>

</style>


Ответы (1 шт):

Автор решения: Mikalai Parakhnevich

Вы вешаете обработчик на пользовательский компонент, поэтому именно этот компонент и должен заниматься генерированием события с помощью встроенного метода $emit, передавая ему имя события.

В вашем случае достаточно добавить в компонент обработчик на верхний элемент и с помощью $emit('click') вызвать метод, который был передан из родительского компонента и в качестве первого аргумента можете передать id вашей карточки:

 <div @click="$emit('click', card.id)">

Учтите также, что каждый компонент Vue должен содержать один корневой элемент:

<template>
  <div>
   <div>
     {{Card.id}}
   </div>
   <div>
     {{Card.name}}
   </div>
  </div>
</template>

Рабочий пример:

const card = Vue.component = {
  name: 'card',
  props: {
    card: Object,
  },
  template: `
  <div @click="$emit('click', card.id)">
    <div>
      {{card.id}}
    </div>
    <div>
      {{card.name}}
    </div>
  </div>
`
}
new Vue({
  el: '#app',
  name: 'App',
  components: {
    card
  },
  data() {
    return {
      cards: [{
          id: 1,
          name: 'first',
          description: 'card 1'
        },
        {
          id: 2,
          name: 'second',
          description: 'card 2'
        },
        {
          id: 3,
          name: 'third',
          description: 'card 3'
        }
      ],
      activeItem: 0,
    }
  },
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <p>
    activeItem = {{ activeItem }}
  </p>
  <div v-for="card in cards" :key="card.id">
    <Card :Card="card" @click="(val) => activeItem = val"></Card>
  </div>
</div>

Второй вариант - использовать нативные события, применив модификатор .native для директивы v-on:

@click.native="activeItem = card.id"

const card = Vue.component = {
  name: 'card',
  props: {
    card: Object,
  },
  template: `
  <div>
    <div>
      {{card.id}}
    </div>
    <div>
      {{card.name}}
    </div>
  </div>
`
}
new Vue({
  el: '#app',
  name: 'App',
  components: {
    card
  },
  data() {
    return {
      cards: [{
          id: 1,
          name: 'first',
          description: 'card 1'
        },
        {
          id: 2,
          name: 'second',
          description: 'card 2'
        },
        {
          id: 3,
          name: 'third',
          description: 'card 3'
        }
      ],
      activeItem: 0,
    }
  },
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <p>
    activeItem = {{ activeItem }}
  </p>
  <div v-for="card in cards" :key="card.id">
    <Card :Card="card" @click.native="activeItem = card.id"></Card>
  </div>
</div>

Пример для Vue 3 (все за исключением синтаксиса тоже, только в дочернем компоненте нужно подписаться на событие click, как emits: ['click'],):

const app = Vue.createApp({
  data() {
    return {
      cards: [{
          id: 1,
          name: 'first',
          description: 'card 1'
        },
        {
          id: 2,
          name: 'second',
          description: 'card 2'
        },
        {
          id: 3,
          name: 'third',
          description: 'card 3'
        }
      ],
      activeItem: 0,
    }
  },
})

app.component('card', {
  props: {
    card: Object,
  },
  emits: ['click'],
  template: `
  <div @click="$emit('click', card.id)">
    <div>
      {{card.id}}
    </div>
    <div>
      {{card.name}}
    </div>
  </div>
`
})

app.mount('#app');
<script src="https://unpkg.com/vue@next"></script>
<div id="app">
  <p>
    activeItem = {{ activeItem }}
  </p>
  <div v-for="card in cards" :key="card.id">
    <Card :Card="card" @click="(val) => activeItem = val"></Card>
  </div>
</div>

→ Ссылка