Large-scale

Backbone apps


Bo Gotthardt
mail@gotthardt.bo

Backbone

Model
Collection
Events
Router
View

Not enough!

Model

var DocumentModel = Backbone.Model.extend({});
var document = new DocumentModel();

document.on("change:title", function (doc, newTitle) {
    console.log("Title changed to", newTitle);
});
document.set("title", "Hello World");

Collection

var DocumentCollection = Backbone.Collection.extend({
    model: DocumentModel,
    url: "api/documents"
});
var documents = new DocumentCollection();

documents.on("add", function (doc) {
    console.log("Got document from server", doc);
});
documents.fetch();

View

var DocumentView = Backbone.View.extend({
    className: "document",
    events: {
        "click .save": "saveDocument"
    },
    render: function () {
        // "The default implementation of render is a no-op."
    },
    saveDocument: function () {
        // ...
    }
});

var view = new DocumentView({
    model: document
});
// Somehow render and attach to the right place in the DOM.

What's missing?

  • Composable views
  • App and view lifecycle
  • Two-way binding
  • Rich models

Can't do a modern large-scale app without those

How to get there?

Backbone plugin ecosystem
Assemble a set of plugins that does all of this

Marionette

Boilerplate-free ItemView
CollectionView
Pluggable views with Regions
Top-level Application

Views and regions

var DocumentView = Marionette.Layout.extend({
    template: Handlebars.compile(template),
    regions: {
        imageBrowser: "#image-browser"
    },
    onRender: function () {
        this.imageBrowser.show(new ImageBrowserView({}));
    },
    //...
});

region.show(new DocumentView({
    model: document
}));

CollectionView

var TableView = Marionette.CollectionView.extend({
    itemView: DocumentThumbnailView
});

var view = new TableView({
    collection: documents
});

Stickit

Strightforward two-way binding
Binding setup in code enables clever tricks

Bindings setup

var DocumentView = Marionette.Layout.extend({
    bindings: {
        ".title": "title",
        ".content": "content",
        ".alert.short-title": {
            observe: "title",
            visible: function (title) {
                return title.length < 5;
            }
        }
    },
    // ...
});
And nothing binding-related in the HTML

Associations

Models with other models as properties
Parsing/serialization handled automatically

Relations

var DocumentModel = Associations.AssociatedModel.extend({
    relations: [{
        type: Associations.One,
        key: "author",
        relatedModel: AuthorModel
    }, {
        type: Associations.Many,
        key: "comments",
        collectionType: CommentCollection
    }]
});

document.get("author").set("name", "John Smith");
document.set("author.name", "John Smith");

Other good ideas

  • RequireJS for modularization
  • RequireJS plugins for loading templates and CSS
  • Would likely work well with TypeScript
  • Organize classes by feature, not type!

Advantages

  • More object-oriented
  • Easier to get started with
  • Easier to customize than monolithic frameworks
  • Easier to debug than dirty-checking loops

Customization ideas

Region that slides in the new view on top of the old
Region shows a Promise for a view
Form validation visualized via bindings elements

Disadvantages

  • Not an "out of the box" solution
  • Not all plugins play well together

Questions?

Large-scale Backbone apps

By Bo Gotthardt

Large-scale Backbone apps

  • 1,262