init a project

yo backbone # generates your application base and build workflow
yo backbone:model blog
yo backbone:collection blog
yo backbone:router blog
yo backbone:view blog

Controllers are an intermediary between models and views which are classically responsible for two tasks:

  • update the view when the model changes
  • update the model when the user manipulates the view handling validation and errors
  • responding to DOM events, rendering templates

In Backbone, controller logic is shared between Backbone.View and Backbone.Router

underscore templates

_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };

easyer server side interaction

Backbone.emulateHTTP = true;
Backbone.emulateJSON = true;

app structure

basic html

<!DOCTYPE html>
<html>
    <head>
        <script src="http://code.jquery.com/jquery-1.7.2.min.js"
        type="text/javascript" language="JavaScript" ></script>
        <script src="http://underscorejs.org/underscore-min.js"
        type="text/javascript" language="JavaScript" ></script>
        <script src="http://backbonejs.org/backbone-min.js"
        type="text/javascript" language="JavaScript" ></script>
        <script src="$app.js" type="text/javascript"
        language="JavaScript" ></script>
    </head>
<body>
 
 
  <!-- templates -->
  <script type="text/template" id="stats-template">
      <% if (done) { %>
        <a id="clear-completed" class="btn  ">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
      <% } %>
      <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
  </script>
</body>
</html>
window.__app__ = {
    Models: {},
    Collections: {},
    Views: {},
    Routers: {},
    init: function () {
        // app init
    }
};
// app init event
$(document).ready(function () {
    __app__.init();
});
//-- router ----------------------------------------
__app__.Routers = __app__.Routers || {};
(function () {
    __app__.Routers.R = Backbone.Router.extend({
      routes: {
          "": "homeAction"
      },
      initialize: function(){},
      homeAction: function(){
        // init views
        app.homeView = new app.views.HomeView({
          el: $('#homeView')
        });
        //app.homeView.render();
        //app.homeView.delegateEvents();
      }
    });
})();
 
//-- model ----------------------------------------
__app__.Models = __app__.Models || {};
(function () {
    __app__.Models.Cart = Backbone.Model.extend({
        url: '',
        //urlRoot: '/cart',//url a cui inviare/legegre i dati
        initialize: function() {
        },
        defaults: {
        },
        validate: function(attrs, options) {
        },
        parse: function(response, options)  {
            return response;
        }
        // BB assumes you are interacting with a RESTful API but allows you to override one method,
        // Backbone.sync(), if not. You tell your model where the resource is on the server (the URL)
        // and then you can just call save().
    });
})();
// uso save:
//user.save(userDetails, {
//  success: function (user) {
//    console.log(user.toJSON());
//  }
//});
 
 
//-- collection ----------------------------------------
__app__.Collections = __app__.Collections || {};
(function () {
    __app__.Collections.CartItems = Backbone.Collection.extend({
        model: __app__.Models.CartItems
    });
})();

uso dei Model/Collection

var Item = Backbone.Model.extend({
    defaults: {
        'field': "Not specified",
    },
    initialize: function(){ }
});
 
var ItemCollection = Backbone.Collection.extend({
    model: Song
});
 
var item_list = new ItemCollection([
    new Item({ field: "aa" }),
    new Item({ field: "aa" }),
]);
 
//-- view -----------------------------------------------
// dispatching events that originate from the UI and propagate Model events
__app__.Views = __app__.Views || {};
(function () {
    __app__.Views.Cart = Backbone.View.extend({
        tagName: 'div',// usati da BB per puntare a this.$el
        id: 'cartView',
        initialize: function () {
          // collegamento con altri model e sub-view
          // binding sulle modifiche al model
          this.listenTo(this.model, 'change', this.render);
          // prima di initialize viene fatta _ensureElement()
          // this.setElement( this.el );
          // appena dopo initilize scatta la delegateEvents(),
          // quindi il dom e this.$el deve essere presente
          this.render();
        },
        // mappa evento UI => sub selector => this.method
        // delega su elementi contenuti nel id corrente
        events: {
            "click .toggle":  "onToggleClick",
            //"dblclick .view": "onViewDblclick",
            //"keypress .edit": "onEditEnter",
            //"blur .edit":     "onEditBlur"
        },
        //NOTA: fondamentale ridefinirla
        render: function () {
            var data = this.model.attributes;
            var html = template_func(data);
            this.$('#CartView').html(html);
            //this.delegateEvents();// a seconda del contesto può essere necessario
 
            // subviews: preferibile configurarle per renderle manualmente jit
            //this.collection.each(function(m){
            //    var tmpView = new subView({ model: m });
            //    this.$el.append(tmpView.render().el);
            //}, this);
        },
        //-- altri gestori di eventi --------------------------
        onEditEnter: function(){
            // aggiorna il model in risposta a input utente
            this.model.set({name:$("#name").val()});
        }
    });
})();
// uso:
//   var person = new Backbone.Model({name:''});
//   messageView = new MessageView({el: $('#message-container'), model: person });

Batch Render Views

// inefficient rendering
var el_list = $('#list');
var dataset = []; // array of 1000 objects
for (var i = 0; i < dataset.length; i++) {
  var view = new ItemView({model: dataset[i]});
  el_list.append(view.render().el);
}

Instead of rendering each individual module on the page, you can decide to load all the HTML up into some type of string, array or detached DOM node and then insert them in your views into the page all at once. This leads to touching the DOM less and, in turn, the less times the browser has to repaint itself. Implementation: Render all of your HTML into a string, array, or detached DOM node that will be inserted into the page at a later time.

// batch rendering views
var el_items = $('#items');
var el_wrapper = el_items.parent();
el_items.detach(); // this detaches the container element from the DOM
var dataset = data; // array of 1000 objects
for (var i = 0; i < dataset.length; i++) {
  var myView = new ItemView({model: dataset[i]});
  el_list.append( myView.render().el );
}
// put container element into the DOM, which dumps all of the views into the page at once.
el_wrapper.append(el_list);

Typescript

TS backbone https://github.com/tastejs/todomvc/tree/gh-pages/examples/typescript-backbone

module Views {
    export class HomeView extends Backbone.View {
        constructor( opt: any ) {
            // TS constructor, init ts properties
            super(opt);// BB chiamerà this.initialize(opt);
        }
        initialize(opt:any) {
            // BB contructior, listen to models...
        }
        render() {
            var html = '<div class="content">test render</div> ';
            this.$el.html(html);
            return this;
        }
        events() {
            return {
                'click .content': 'onOpen',
            };
        }
        onOpen(event:any) {
            console.log('onOpen');
        }
    }
}

router:

module Mobile {
    export class Router extends Backbone.Router {
        public routes:Hash = {
            '': 'homeAction',
            //'*actions': 'homeAction', //catch all route
            'home': 'homeAction',
        };
        constructor(options?: Backbone.RouterOptions) {
            super(options);
            // routes va inizializzato prima di richiamre super, che inizierà la gestione della url
            // vedi come TS compila il costruttore
            // call _bindRoutes() here function to bind your routes
            (<any>this)._bindRoutes();
        }
 
        public initialize():void {
        }
 
        protected homeAction():void {
            var el:JQuery = Views.Utils.ensurePage('homeView');
            var V = Views.Utils.traceView(new Views.HomeView({
            el: el[0] });
            V.render().delegateEvents();
        }
    }
}

resources: