Florin Ionce
Florin Ionce

Reputation: 855

Child component emit to parent and parent update all children components

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

Answers (2)

Jędrzej Chałubek
Jędrzej Chałubek

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

Saurabh
Saurabh

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

Related Questions