Thinking in Backbone

Backbone implementation of "Thinking in React" example, using ES6.

by infous

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
<div id="app"></div>

<script id="search-bar-template" type="text/x-handlebars-template">
  <input type="text" placeholder="Search...">
  <p>
    <input type="checkbox">
    Only show products in stock
  </p>
</script>

<script id="product-table-template" type="text/x-handlebars-template">
  <thead>
    <tr>
      <th>Name</th>
      <th>Price</th>
    </tr>
  </thead>
  <tbody></tbody>
</script>

<script id="product-row-template" type="text/x-handlebars-template">
  <td>{{#if stocked}}{{name}}{{else}}<span style="color: red">{{name}}</span>{{/if}}</td>
  <td>{{ price }}</td>
</script>

<script id="product-category-row-template" type="text/x-handlebars-template">
  <th colspan="2">{{ category }}</th>
</script>

<a href="https://github.com/starikovs/thinking-in-backbone/" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png"></a>

CSS

body { padding: 30px; }

Babel + JSX

class Product extends Backbone.Model {
    get defaults () {
        return {
            category: '',
            price: '',
            stocked: false,
            name: ''
        };
    }
}

class State extends Backbone.Model {
    get defaults () {
        return {
            filterText: '',
            inStockOnly: false
        };
    }
}

class Products extends Backbone.Collection {
    get model () {
        return Product;
    }
}

class ProductRow extends Backbone.View {  
    initialize () {
        this.render();
    }

    tagName () {
        return 'tr';
    }

    get template () {
        return Handlebars.compile($('#product-row-template').html());
    }

    render () {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
}

class ProductCategoryRow extends Backbone.View {   
    initialize () {
        this.render();
    }

    tagName () {
        return 'tr';
    }

    get template () {
        return Handlebars.compile($('#product-category-row-template').html());
    }

    render () {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
}

class ProductTable extends Backbone.View {    
    initialize () {
        this.listenTo(this.model, 'change', this.render);
        this.render();
    }

    tagName () {
        return 'table';
    }

    get template () {
        return Handlebars.compile($('#product-table-template').html());
    }

    render () {
        this.unregisterSubviews();
        this.$el.html(this.template({ }));

        let filterText = this.model.get('filterText');
        let isStockOnly = this.model.get('isStockOnly');
        let rows = this.$('tbody');
        let lastCategory = null;

        this.collection.each(model => {
            if (model.get('name').indexOf(filterText) === -1 ||
                (isStockOnly && !model.get('stocked'))) {
                return;
            }

            if (model.get('category') !== lastCategory) {
       ...