Backbone-nested-views

by abenrob

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.3/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js"></script>
<button id="addA">Add to A</button><button id="addB">Add to B</button><br>
<div id="target"></div>

CSS

button {
    margin: 10px 0 0 10px;
}
table {
    width: 200px;
    margin: 10px;
}
tr {
    border:1px solid #000;
}
tr{
    display: inline-block;
    width: 100%;
}
td {
    padding: 5px;
}
.name {
    float: left;
}
.nums {
    float: right;
    width: 100px;
    text-align: right;
}
.hideme {
    display: none;
}
.list-expand {
    cursor: pointer;
}

JavaScript

var sampleData = [
    {_id:"a", name: "a",nums:[{num:1},{num:2},{num:3},{num:4}]},
    //{name: "b",nums:[{num:5},{num:6},{num:7},{num:8}]},
    {_id:"b", name: "b",nums:[]},
];

/** Collection of models to draw */
var rowModel = Backbone.Model.extend({
    idAttribute:"_id"
});
var rowsCollExtend = Backbone.Collection.extend({
    model:rowModel
});
var rowsCollection = new rowsCollExtend([]);

/** View representing a table */
var TableView = Backbone.View.extend({
    tagName: 'table',
    initialize : function() {
        _.bindAll(this,'render','renderOne');
        if(this.model) {
            this.model.on('change',this.render,this);
        };
        this.collection.on('add', this.render,this);
    },
    render: function() {
        this.$el.empty();
        this.collection.each(this.renderOne);
        return this;
    },
    renderOne : function(model) {
        var row=new RowView({model:model});
        this.$el.append(row.render().$el);
        return this;
    }
});

/** View representing a row of that table */
var RowView = Backbone.View.extend({
    initialize: function() {
        this.model.on('change',this.render,this);
    },
    render: function() {
        var template = _.template("<tr><td class='name'><%= name %></td><td class='nums'></td></tr>");
        this.$el.html(template(this.model.toJSON()));
        var listCollection = new Backbone.Collection(this.model.toJSON().nums);
        var aList = new ListExpandView({collection:listCollection});
        aList.render();
        this.$el.find('.nums').append(aList.$el);
        return this;
    },
});

/** View representing list within row **/
var ListExpandView = Backbone.View.extend({
    initialize: function() {
        this.collection.on('change',this.render,this);
    },
    events: {
        "click .list-expand": "listExpander",
    },
    listExpander: function(e) {
        if ($(e.target).hasClass('hiding')) {
            $(e.target).siblings('.item').removeClass('hideme')
       ...