How do you emit event inside recursive child components vuejs
Taking the tree example from vue site https://v2.vuejs.org/v2/examples/tree-view.html
How would you transmit on click to the parent each clicked elements id?
How do you emit event inside recursive child components vuejs
Taking the tree example from vue site https://v2.vuejs.org/v2/examples/tree-view.html
How would you transmit on click to the parent each clicked elements id?
In the case of recursive elements, you can create an event bus in the parent, pass it to the children as a prop, and have each prop pass it to any children they generate.
Each child emits events on the bus, and the parent handles them. I copied the tree-view exercise you linked and added the bus functionality.
// demo data
var data = {
name: 'My Tree',
children: [{
name: 'hello'
},
{
name: 'wat'
},
{
name: 'child folder',
children: [{
name: 'child folder',
children: [{
name: 'hello'
},
{
name: 'wat'
}
]
},
{
name: 'hello'
},
{
name: 'wat'
},
{
name: 'child folder',
children: [{
name: 'hello'
},
{
name: 'wat'
}
]
}
]
}
]
};
var itemId = 0;
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object,
bus: Object
},
data: function() {
return {
open: false,
id: ++itemId
}
},
computed: {
isFolder: function() {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function() {
if (this.isFolder) {
this.open = !this.open;
this.bus.$emit('toggled', this.id);
}
},
changeType: function() {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function() {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
el: '#demo',
data: {
treeData: data,
bus: new Vue()
},
created() {
this.bus.$on('toggled', (who) => {
console.log("Toggled", who);
});
}
})
body {
font-family: Menlo, Consolas, monospace;
color: #444;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: dot;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<div :class="{bold: isFolder}" @click="toggle" @dblclick="changeType">
{{model.name}}
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item" :bus="bus" v-for="model in model.children" :model="model">
</item>
<li class="add" @click="addChild">+</li>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<item class="item" :bus="bus" :model="treeData">
</item>
</ul>
Here's another solution if you don't want to create multiple Vue instances. I use this in my single file recursive components.
It uses the v-on
directive (I'm using the @
shorthand).
In your recursive component <template>
:
<YourComponent @bus="bus"></YourComponent>
In the recursive component methods
:
methods: {
bus: function (data) {
this.$emit('bus', data)
}
}
To kick it off, you emit an event in a child:
this.$emit('bus', {data1: 'somedata', data2: 'somedata'})
That data will be transmitted all the way up the chain, and then you receive that event in the page that called your recursive component:
methods: {
bus (data) {
// do something with the data
}
}
Here's a fiddle showing it in action on the Vue.JS tree example. Right-click on an element, and it will output that model in the console:
In the case of recursive elements, you can create an event bus in the parent, pass it to the children as a prop, and have each prop pass it to any children they generate.
Each child emits events on the bus, and the parent handles them. I copied the tree-view exercise you linked and added the bus functionality.
// demo data
var data = {
name: 'My Tree',
children: [{
name: 'hello'
},
{
name: 'wat'
},
{
name: 'child folder',
children: [{
name: 'child folder',
children: [{
name: 'hello'
},
{
name: 'wat'
}
]
},
{
name: 'hello'
},
{
name: 'wat'
},
{
name: 'child folder',
children: [{
name: 'hello'
},
{
name: 'wat'
}
]
}
]
}
]
};
var itemId = 0;
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object,
bus: Object
},
data: function() {
return {
open: false,
id: ++itemId
}
},
computed: {
isFolder: function() {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function() {
if (this.isFolder) {
this.open = !this.open;
this.bus.$emit('toggled', this.id);
}
},
changeType: function() {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function() {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
el: '#demo',
data: {
treeData: data,
bus: new Vue()
},
created() {
this.bus.$on('toggled', (who) => {
console.log("Toggled", who);
});
}
})
body {
font-family: Menlo, Consolas, monospace;
color: #444;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: dot;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<div :class="{bold: isFolder}" @click="toggle" @dblclick="changeType">
{{model.name}}
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item" :bus="bus" v-for="model in model.children" :model="model">
</item>
<li class="add" @click="addChild">+</li>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<item class="item" :bus="bus" :model="treeData">
</item>
</ul>
Use v-on="$listeners"
I'll let you into a little secret. The Vue $listeners
property (which is documented as being there to pass events down to children), also passes child events to parents!
https://v2.vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components
Here is a pseudo-code example (shorthand for illustrative purposes):
<ancestor-component @messageForAncestor="displayMessage">
...
<parent-component v-on="$listeners">
...
<child-component @click="$emit('messageForAncestor')">
In the display above the child component would pass up an event. The parent would typically be able to listen for the messageForAncestor
event and that's where it would need to stop, but. Putting v-on="$listeners"
on the parent actually says please pass it on.
Warn: That was probably a bad idea
This is probably a very bad idea though. A better idea would be to simply ask the middle component (the parent) to pass it on...
<!-- better idea (listen for message and pass on message) -->
<parent-component @message="$emit('message', $event)">
I think the best and clear way is here:
https://www.digitalocean.com/community/tutorials/vuejs-communicating-recursive-components
So, You need pass function as parameter. It`s help to awoid limitation of Vue emits, when you are using one with recursive components
© 2022 - 2024 — McMap. All rights reserved.