Как изменить данные в 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 шт):
Вы вешаете обработчик на пользовательский компонент, поэтому именно этот компонент и должен заниматься генерированием события с помощью встроенного метода $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>