Привет, поговорим о vue-приемчиках… Под приемами я подразумеваю способы передачи данных между компонентами. Сразу скажу, что это не все приемы, а лишь те которые я использую и о которых помню на момент написания статьи. Погнали!
Events
Наверное самая простая вещь в фреймворке которая полностью идентична js-концепции событий. К примеру событие клика по кнопке
<!-- HTML --> <button onclick="func"></button> <!-- Vue --> <button v-on:click="func"></button> <!-- работает идеентично --> <button @click="func"></button>
также, легко можно создать кастомный ивент и передать в него значение любого типа
<!-- child-component -->
<button @click="$emit('custom-event',{})"></button>
<!-- parent-compponent -->
<child-component @custom-event="func"/>Однако это работает только от ребенка к родителю
Управление ребенком
Здесь ивенты уже как-бы “не алё”, на помощь приходят либо ссылки либо пропсы
Начнем из менее лаконичного способа – ссылок. Выглядит это так(родитель)
<template>
<div>
<button @click="toggle">Click me!</button>
<child-component ref="child"/>
</div>
</template>
<script>
import ChildComponent from '@/components/ChildComponent'
export default {
name: 'App',
components: {
ChildComponent
},
methods:{
toggle(){
this.$refs.child.collapse = !this.$refs.child.collapse
}
}
}
</script>ребёнок
<template>
<template v-if="collapse">
Collapse
</template>
<template v-else>
Expand
</template>
</template>
<script>
export default {
name: 'ChildComponent',
data:function () {
return {collapse:true}
}
}
</script>
<style scoped>
</style>Здесь атрибут ref создает ссылку на ребёнка, которая предоставляет доступ к свойствам и методам вложенного компонента.
Более правильно(как по мне) будет использование пропсов:
<template>
<div>
<button @click="toggle">Click me!</button>
<child-component :collapse="collapse"/>
</div>
</template>
<script>
import ChildComponent from '@/components/ChildComponent'
export default {
name: 'App',
components: {
ChildComponent
},
data:function () {
return {collapse:true}
},
methods:{
toggle(){
this.collapse = !this.collapse
}
}
}
</script><template>
<template v-if="collapse">
Collapse
</template>
<template v-else>
Expand
</template>
</template>
<script>
export default {
name: 'ChildComponent',
props:{
collapse:Boolean
}
}
</script>
<style scoped>
</style>тут свойство collapse попадает в дату родителя, а в дочернем копроненте переносится в пропс.
Но у меня был третий вариант: тогл происходил внутри дочерних, а в родителе была кнопка “свернуть все”. Вот мое решение
<template>
<div>
<button @click="toggle">Close!</button>
<child-component :close="close" @open="open"/>
</div>
</template>
<script>
import ChildComponent from '@/components/ChildComponent'
export default {
name: 'App',
components: {
ChildComponent
},
data:function () {
return {close:false}
},
methods:{
toggle(){
this.close = true
},
open(){
this.close = false
}
}
}
</script><template>
<div>
<button @click="toggle">Toggle</button>
</div>
<template v-if="collapse">
Collapse
</template>
<template v-else>
Expand
</template>
</template>
<script>
export default {
name: 'ChildComponent',
data:function () {
return {collapse:true}
},
props:{
close:Boolean
},
methods:{
toggle(){
this.collapse = !this.collapse
this.$emit('open')
}
},
watch:{
close:function (old) {
if (old)
this.collapse = true
}
}
}
</script>Суть его в том, чтоб создать пропс и поставить его под наблюдение в watch. В таком варианте, как я навел, проще было б не лезть в дитя, а просто управлять по ссылке; но дело было сложнее и с ссылками не вариант. И еще – при тогле в вложенном компоненте , я емичу событие для обнуления в родитель.
Интересный случай с таблицей. Обработка форм
Задача была построить таблицу с чекбоксами разных настроек. Моя первая итерация данного компонента
<template>
<table>
<thead>
<tr>
<th>Name</th>
<th>Enabled</th>
</tr>
</thead>
<template v-for="(setting,i) in settings" :key="i">
<tr>
<td>{{setting.title}}</td>
<td>
<input type="checkbox" :checked="setting.enabled" @change="change(i)">
</td>
</tr>
</template>
</table>
</template>
<script>
export default {
name: 'App',
data:function () {
return {
settings:[
{title:'setting 1',enabled:true},
{title:'setting 2',enabled:false},
{title:'setting 3',enabled:true},
],
i:0
}
},
methods:{
change(i){
this.settings[i].enabled = !this.settings[i].enabled
}
}
}
</script>и это работает, но у меня возникла мысль – а как получить содержимое если это textarea? Кароч, правильный путь – забыть о change и тп, использовать директиву v-model
<template>
<table>
<thead>
<tr>
<th>Name</th>
<th>Enabled</th>
</tr>
</thead>
<template v-for="(setting,i) in settings" :key="i">
<tr>
<td>{{setting.title}}</td>
<td>
<input type="checkbox" :checked="setting.enabled" v-model="setting.enabled">
</td>
</tr>
</template>
</table>
</template>
<script>
export default {
name: 'App',
data:function () {
return {
settings:[
{title:'setting 1',enabled:true},
{title:'setting 2',enabled:false},
{title:'setting 3',enabled:true},
]
}
},
}
</script>но все же
<template>
<table>
<thead>
<tr>
<th>Name</th>
<th>Enabled</th>
</tr>
</thead>
<template v-for="(setting,i) in settings" :key="i">
<tr>
<td>{{setting.title}}</td>
<td>
<input type="checkbox" :checked="setting.enabled" @change="setting.enabled = $event.target.checked">
</td>
</tr>
</template>
</table>
</template>
<script>
export default {
name: 'App',
data:function () {
return {
settings:[
{title:'setting 1',enabled:true},
{title:'setting 2',enabled:false},
{title:'setting 3',enabled:true},
]
}
},
}
</script>для тех, кому v-model не угодил(в комментах пишите причину – может я чего-то не знаю)…