Backbone.JS – A year later



Backbone.JS – A year later

0 3


slides-fossil

Introduces Fossil

On Github themouette / slides-fossil

Backbone.JS

A year later

By Muetton Julien / @themouette

APIHour @clermontech 2013 july, 3rd

Agenda

Best practices A typical Backbone application Stop repeating yourself

Best practices

Be ready for next level

Isolate your UI components

A component's responsability is to provide meaningful user experience.

  • Use sandboxed context
  • Communicate through events

Cover your tracks

  • Keep an eye on memory
  • Setup only what is needed
  • Cleanup when leaving

UI is fragile

Ensure you have tests!

Industrialize your process

  • Task manager: Grunt
  • Frontend dependency management: bower
  • Command line dependency management: npm

Typical Backbone Application

Backbone.JS is great

  • Low level
  • Very flexible
  • Clean

But it requires a lot of boilerplate.

Kernel

The application main entry point

Load dependencies Get remote data Register routes Setup UI Show loading Wait for data Start router

Controller

Show loading Get remote data Wait for data Create and display view(s)

We Could Go On

  • Layout
  • Contextual layout
  • View manager
  • Error management
  • ...

One day, I was tired of it

Introducing Fossil

Stop repeating yourself

Fossil.Application

Responsible for boot sequence and modules orchestration

Example

var myApp = new Fossil.Application({
    routes: {
        '': function () {
            this.routing.navigate('search', {replace: false});
    },
    fragments: {
        'topmenu': TopmenuFragment
    }
});

myApp // declare services
    .use('routing', Fossil.Services.Routing)
    .use('session', Fossil.Services.Session);
myApp // connect modules
    .connect('search', SearchModule)
    .connect('configure/:model', ConfigureModule)
myApp.start();

And also

  • Event dispatcher
  • Promise API
  • Main layout
  • ...

Module

Consistent package of UI, business and data.

Example module

var SearchModule = Fossil.Module.extend({
    routing: {
        ':query': 'onquery'
    },
    fragments: {
        'sidebar': SidebarFragment,
        'content': ContentFragment
    },
    onqueryRoute: function (query) {
        this
            .abort() // abort previous calls
            .waitFor(this.ensureCollection().fetch({query: query}))
            .thenWith(this, this.renderResults, this.displayError);
    },
    renderResults: function () {
        this.trigger('search:query:render'); }
});

And also

  • Event dispatcher
  • Bridge to application
  • Promise API
  • Contextualized layout
  • Start/Standby/Stop
  • ...

Fossil.Fragment

Standalone set of views, with its own logic.

Can be included in Application, Modules and Fragments.

Never manage UI in Modules or Application, send events to Fragments.

Example fragment

var SidebarFragment = Fossil.Fragment.extend({
    ancestorEvents: {
        'search:query:render': function (results) {
            this.setView(new Fossil.View.Collection({
                ItemView: ResultRowView,
                collection: results
            }));
        }
    }
});

And also

  • Event dispatcher
  • Bridge to ancestor
  • View manager
  • Follow ancestor lifecycle
  • Start/Standby/Stop
  • ...

Fossil.Service

  • Available application wide
  • Provide interceptors in every component lifecycle
  • Provide commands
  • Be imaginative !

Coming soon

Enhanced view manager Scaffolding Gruntfile automation More examples

Early alpha available on GitHub

Thank You

Questions?

Carving up your UI

A car retailer intranet

Workflow

Search a car Select a car Configure options Save invoice

Search

Configure

Remain testable

Application

var myApp = new Fossil.Application({
    routes: {
        '': function () {
            this.routing.navigate('search', {replace: false});
    },
    fragments: {
        'topmenu': TopmenuFragment
    }
});
myApp // declare services
    .use('routing', Fossil.Services.Routing)
myApp // connect modules
    .connect('search', SearchModule)
    .connect('configure/:model', ConfigureModule)
myApp.start();

No unit tests needed, it is just configuration

Module

var SearchModule = Fossil.Module.extend({
    routing: {
        ':query': 'onquery' },
    onqueryRoute: function (query) {
        this
            .abort() // abort previous calls
            .waitFor(this.ensureCollection().fetch({query: query}))
            .thenWith(this, this.renderResults, this.displayError);
    },
    renderResults: function () {
        this.trigger('search:query:render');
    }
});
  • renderResults should trigger 'search:query:render'
  • onqueryRoute should call renderResults on success
  • onqueryRoute should call displayError on error

Fragment

var SidebarFragment = Fossil.Fragment.extend({
    ancestorEvents: {
        'search:query:render': function (results) {
            this.setView(new Fossil.View.Collection({
                ItemView: ResultRowView,
                collection: results
            }));
        }
    }
});
  • should call setView on 'search:query:render'