Ember.js Recursive component and action bubble up
Ember recursive component is very interesting. I had to create a side tree view component for a project I was working on and I created it using a recursive component in Ember.
The cool thing with recursive components in Ember is that it helps you create tree structures in a very elegant and short way in terms of code.
For example, let’s say you created a tree data structure, something that looks like this:
let tree = [
{
id: 1,
children: [
{
id: 2,
children: []
}
]
},
{
id: 3,
children: []
}
]
And then you want to display this tree like a side menu. So we will have something like this:
// tree.hbs
<ul>
{{tree-recursive-component tree=tree}}
</ul>// tree-recursive-component.hbs
<li>
// display tree id
{{tree.id}}
{{#each tree.children as |child|}}
<ul>
{{tree-recursive-component tree=child}}
</ul>
{{/each}}
</li>
Imagine writing that in a loop … ( you would have to do many many many nested loops which is nasty and looks very bad, definitely not good practice ).
So what happens now? You have create a tree like structure, you can style it so that for each <ul>
element or child, you can indent it to make it look nicer. But to make it more interesting, lets pass an action to the component because we want to collapse and expand the lists items right?
For simplicity’s sake, lets say you pass an action that console.log
the current item’s id. What you will soon find out is that whenever you click on an item in the list, depending on how deep you are in the recursive call ( or the levels at which you are in the tree / recursive call ), the number of console log calls will equal the current recursive call.
Why is this?
This is because actions inside recursive components bubbles up, meaning that if I am the fifth child in the recursive component, I will execute my action, but I have to travel up the stack and call each of my parent’s actions ( which have the same action as I am because we all share the same component ).
How do we remedy this? We need set the bubble
attribute in the Ember’s action helper to false so that it wont call the actions on every parent component but its own, like so:
<ul {{action “printToConsole” bubbles=false}}>
This was a fun and challenging task that I had the pleasure of solving. I am glad that I stumbled across this problem because solving this helped me understand Ember better and become a better programmer overall.