---
title: Using Recursive Components in Ember.js
teaser: Build and render a tree structure with Ember.js.
tags: web,ember,javascript
author: Omar Ismail
published_on: 2015-04-13
---

We recently built an analytics engine for [MIT Media
Lab](http://www.media.mit.edu/) using Ember.js, Rails, and D3.js. The project
involved querying the MIT’s servers for data and using D3 to visualize the
streams of data that returned from the query. Each of those streams could be
operated on (e.g. finding the average), which would render a new stream showing
the altered data.

## How We Built The Tree Structure

We built a tree structure where the root node was the initial stream from the
query, and child nodes were new streams which were generated as a result of a
specific operation applied to a parent node. Sibling nodes — nodes that have the
same parent — could be created as a result of applying multiple operations on
the same parent node.

To accomplish this, we created a node model, which has many of itself
(children), and belongs to itself (parent). In modeling terms, this is called a
[reflexive
association](http://guides.emberjs.com/v1.10.0/models/defining-models/#toc_reflexive-relation).

```js
export default DS.Model.extend({
  // other domain logic

  children: DS.hasMany("nodes", { inverse: "parent" }),
  parent: DS.belongsTo("node", { inverse: "children" })
});
```

## Recursive Calling of a Component

![Tree](http://images.thoughtbot.com/recursive-ember-components/tributary-tree.png)

Rendering a tree structure of unknown size can be done by using recursion. The
best way to handle this in Ember is to create a component — representing a node
on a tree — that recursively calls itself. In the template that retrieved the
initial data stream, we called the component that began the root of the tree.

```handlebars
{{stream-node node=model.rootNode}}
```

At the bottom of the `{{stream-node}}` component's template, we iterated through
the children on the model — the node — and rendered a new `{{stream-node}}` component
for each.

```handlebars
{{#each displayedChildren as |displayedChild|}}
  {{stream-node node=displayedChild stackedSiblings=stackedChildren}}
{{/each}}
```

And in our `{{stream-node}}` component:

```js
export default Ember.Component.extend({
  // other domain logic

  displayedChildren: function() {
    return this.get("node.children").filter(function(node) {
      return node.get("isTopChild");
    });
  }.property("node.{children,topChild}"),
});
```

A few things are happening here. Every time we call `{{stream-node}}`, we pass
in the new stream data as `node=…`. We can now use the same functionality on all
the nodes while only defining that functionality once in the `{{stream-node}}`
component.

The tree was represented vertically down the page. Each child node was placed
below the parent node. Whenever a node had siblings, they were displayed as a
stack, and the `topChild()` was the node that appeared at the top of the stack.
We had a separate page for comparing the sibling nodes, and the compare view
could alter what the top child was.

![Compare](http://images.thoughtbot.com/recursive-ember-components/tributary-compare.png)

`topChild` is an attribute on the Node model rather than the component because
it is part of the domain logic, in which the server uses to figure out what the
active leaf nodes are for a given tree. The `topChild` value is also used in
many places within the app, and therefore cannot be encapsulated in a single
component.

Because of the association on the parent node to the child node, the parent node
can validate that only one of its children has the `topChild` attribute set to
true. This also gave us the benefit that when the model changes (adding children
or altering the `topChild`), the computed properties will handle the update and
display the proper information.

Because we have the `property("node.children")` on `displayedChildren()`
function, any time a new child was added to a node, Ember automatically created
a new `{{stream-node}}` component and appended the appropriate node at the
bottom of the page.

When creating a new child node, all we needed to do to ensure that Ember would
create the new component automatically was push the new node on the children
array of the parent node:

```js
newNode.save().then((node) => {
  parentNode.get("children").addObject(node);
});
```

## Bubbling and Deleting Nodes

Bubbling up events/actions in components is different than bubbling up via
controller/routes. Controller/route bubbling happens automatically. Components
are isolated, which means that when bubbling up actions is done manually. The
name of the action in the controller/route that the event ultimately bubbles up
to needs to be assigned as an attribute on the component so that Ember knows
which function in the actions hash should handle this event.

The delete action is triggered within the stream-node template.

```handlebars
<a href="#" class="delete button button-tile button-default" {{action
"deleteNode" node}}>
</a>
```

There are two ways to assign this attribute. One is on the component call in the
template `{{stream-node deleteNode="deleteNode" node=model.rootNode}}`, and the
other is to assign the attribute within the component itself:

```js
export default Ember.Component.extend({
  deleteNode: "deleteNode",

  // other domain logic

  actions: {
    deleteNode: function(node) {
      this.sendAction("deleteNode", node);
    }
  }
});
```

This would keep bubbling up through the component tree
until it hit the action in the route:

```js
export default Ember.Route.extend({
  // other domain logic

  actions: {
    deleteNode: function(node) {
      node.destroyRecord();
    }
  }
});
```

At this point, if you don't delete the child nodes, then the orphaned nodes will
remain in the store, but won't be displayed. Once a child is deleted, the model
of the parent node will update due to computed properties, and the components
will re-render.

All in all, this project displayed the power of Ember in maintaining consistency
in the data and updating computed properties whenever data changed. When we were
tasked to implement multiple trees — a task we thought would take us a long time
— it took 40 minutes to build, thanks to the heavy lifting of Ember.
