Ember Tree View

by fangyang

HTML

<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://raw.github.com/wycats/handlebars.js/1.0.0-rc.3/dist/handlebars.js"></script>
<script type="text/javascript">
	window.ENV = window.ENV || {};
	ENV.EXPERIMENTAL_CONTROL_HELPER = true;
</script>

<script src="//cdnjs.cloudflare.com/ajax/libs/ember.js/1.0.0-rc.1/ember.js"></script>

<script data-template-name="application" type="text/x-handlebars">
    <h1>Ember.js Tree Example</h1>
    {{control "treeBranch" App.treeRoot}}
    <p><a href="http://billysbilling.com/blog/How-to-implement-a-tree-in-Ember-js">Read the blog post</a></p>
</script>
  
<script data-template-name="tree" type="text/x-handlebars">
  	{{control "treeBranch" content}}
</script>

<script data-template-name="tree-branch" type="text/x-handlebars">
  	{{each children itemController="treeNode" itemViewClass="App.TreeNodeView"}}
</script>
  
<script data-template-name="tree-node" type="text/x-handlebars">
    <span {{bindAttr class=":toggle-icon children.length::leaf"}} {{action toggle}}>
        {{#if isExpanded}}
            &#x25BC;
        {{else}}
            &#x25B6;
        {{/if}}
    </span>
    <a {{action click}} class="text">{{text}}</a>
    {{#if isExpanded}}
	    {{control "treeBranch" content}}
    {{/if}}
</script>

CSS

body {
    color: #444;
    font-family: Helvetica, Arial, sans-serif;
    font-size: 14px;
    padding: 30px;
    margin: 0px;
}

h1 {
    font-size: 24px;
    line-height: 1.2em;
    margin: 0px 0px 10px 0px;
}

/* Branch */
.tree-branch {
    padding: 0px;
    margin: 0px;
}
.tree-node .tree-branch {
    padding-left: 10px; /* Nested tree branches should be indented */
}

/* Node */
.tree-node {
    list-style: none;
    line-height: 20px;
}

/* Arrow */
.tree-node .toggle-icon {
    cursor: pointer;
    display: inline-block;
    width: 10px;
    margin-right: 0px;
    font-size: 10px;
}
.tree-node .toggle-icon:hover {
    opacity: 0.8;
}
.tree-node .toggle-icon.leaf {
    visibility: hidden;
}

/* Node text */
.tree-node .text {
    cursor: pointer;
    text-decoration: underline;
}
.tree-node .text:hover {
    text-decoration: none;
}

JavaScript

App = Ember.Application.create();

App.TreeBranchController = Ember.ObjectController.extend({
});
App.register('controller:treeBranch', App.TreeBranchController, {singleton: false});

App.TreeBranchView = Ember.View.extend({
  tagName: 'ul',
  templateName: 'tree-branch',
  classNames: ['tree-branch']
});

App.TreeNodeController = Ember.ObjectController.extend({
    isExpanded: false,
    toggle: function() {
        this.set('isExpanded', !this.get('isExpanded'));
    },
    click: function() {
        console.log('Clicked: '+this.get('text'));
    }
});
App.register('controller:treeNode', App.TreeNodeController, {singleton: false});

App.TreeNodeView = Ember.View.extend({
  tagName: 'li',
  templateName: 'tree-node',
  classNames: ['tree-node']
});


App.set('treeRoot', {
    text: 'Root',
    children: [
        {
            text: 'People',
            children: [
                {
                    text: 'Basketball players',
                    children: [
                        {
                            text: 'Lebron James',
                            children: []
                        },
                        {
                            text: 'Kobe Bryant',
                            children: []
                        }
                    ]
                },
                {
                    text: 'Astronauts',
                    children: [
                        {
                            text: 'Neil Armstrong',
                            children: []
                        },
                        {
                            text: 'Yuri Gagarin',
                            children: []
                        }
                    ]
                }
            ]
        },
        {
            text: 'Fruits',
            children: [
                {
                    text: 'Banana',
                    children: []
                },
                {
                    text: 'Pineapple',
                   ...