Reputation: 855
I'm trying to create a list with expandable items using vue js 2. The idea is that when I click on an item I want it to expand(show it's content) if it's not already expanded and close all the other expanded elements. I have created two components expandable-panel
and expandable-item
. I can have multiple types of contents inside so I want to use slots.
The code I have:
ExpandablePanel
component:
<template>
<div class="expandable-panel" @expanded="doStuff">
<slot></slot>
</div>
</template>
<script type="text/babel">
export default {
methods: {
doStuff: function() {
alert('expanded');
}
}
}
</script>
The ExpandableItem
component:
<template>
<div class="expandable-item" @click="toggleExpand">
<div class="expandable-item-header">
{{ title }}
</div>
<div class="expandable-item-body" v-if="expanded">
<slot></slot>
</div>
</div>
</template>
<script type="text/babel">
export default {
props: {
title: String
// expanded: {
// type: Boolean,
// default: true
// }
},
data: function() {
return {
expanded: true
}
},
methods: {
toggleExpand: function() {
this.expanded = !this.expanded;
this.$emit('expand');
}
}
}
</script>
And my HTML file
<expandable-panel>
<expandable-item title='title 1'>Lorem ipsum...</expandable-item>
<expandable-item title='title 2'>Lorem ipsum...</expandable-item>
<expandable-item title='title 3'>Lorem ipsum...</expandable-item>
</expandable-panel>
My app and components are registered like this:
Vue.component('expandable-panel', require('./components/global/ExpandablePanel.vue'));
Vue.component('expandable-item', require('./components/global/ExpandableItem.vue'));
const app = new Vue({
el: '#app'
});
Problems:
The $emit
part doesn't seem to reach to the @expanded="doStuff"
inside the parent component.
Even if it reaches there how do I properly toggle the expanded
values? Can I use props for this?
Right now, it toggles each elements but I can't update the others. Thank you
Upvotes: 1
Views: 1792
Reputation: 1438
You have here the strict relationship between parent and child, so you can emit event via $parent object.
First, on parent object creation, listen for your event with this.$on
:
Vue.component('parent', {
template: `<div><slot></slot></div>`,
created: function() {
this.$on('event', function(message) {
alert(message);
})
}
});
Then inside your child component emit this event with $parent.$emit
Vue.component('child', {
template: `<button @click="$parent.$emit('event', 'From child')">I'm slot</button>`
});
Here is working demo: https://jsfiddle.net/rdjjpc7a/1754/
Upvotes: 1
Reputation: 73609
In the ExpandablePanel
component: you should only have @expand
as that is what you are emitting from the child:
<template>
<div class="expandable-panel" @expand="doStuff">
<slot></slot>
</div>
</template>
Upvotes: 0