Fabio B.
Fabio B.

Reputation: 9400

Browserify+Backbone.js "ApplicationState" shared module

I don't understand how to share some sort of "singleton" application-state holding object model, between different views, using browserify. Books and tutorials often use a global namespace such as:

var app = app || {};

I have a simple example app which consists of:

app.js

var $ = require('jquery');
var Backbone = require('backbone');
Backbone.$ = $;

var MenuView = require('./views/MenuView');
var ContainerView = require('./views/ContainerView');

new MenuView();
new ContainerView();

MenuView.js

var Backbone = require('backbone');
var ApplicationState = require('../models/ApplicationState');

module.exports = Backbone.View.extend({
    el: '#menuView', 
    events: {
        'click .menuLink': 'changePage'
    },
    changePage: function(event) {
        event.preventDefault();
        var viewName = $(event.target).attr('data-view');
        ApplicationState.set('currentView',viewName);
    }
});

ContainerView.js

var Backbone = require('backbone');
var ApplicationState = require('../models/ApplicationState');

module.exports = Backbone.View.extend({
    el: '#containerView', 
    initialize: function() {
        this.listenTo( ApplicationState, 'change', this.render );   
        this.render();
    },
    render: function() {
        this.$el.html( ApplicationState.get('currentView') );
    },
    close: function() {
        this.stopListening();
    }
});

This seems working using this approach:

ApplicationState.js var Backbone = require('backbone');

var ApplicationState = Backbone.Model.extend({
    defaults: {
        currentView: 'TransactionListView'
    }
});

module.exports = new ApplicationState();

Is the ApplicationState module really created only once (caching) ? Or is there the risk of recreating / resetting the module?

What is the best practice for my use case? Thank you a lot.

Upvotes: 3

Views: 634

Answers (1)

Marshall
Marshall

Reputation: 197

Yes, there will only be one ApplicationState in the example you gave. Browserify executes anything following module.exports = as soon as the js file is run and then anything that requires that file is passed a reference to the result.

However it’s generally better practice to avoid sharing state this way between views and instead use a parent view that delegates to subviews. There are a number of ways to set this up. For ideas on best practices for organizing a backbone app, check out this talk: https://www.youtube.com/watch?v=Lm05e5sJaE8

For the example you gave I would highly consider using Backbone's Router. In your example you have a nav that changes the "main" view. Backbone.Router intercepts navigation and checks it against your specified routes calling your view method. For instance:

router.js

module.exports = Backbone.Router.extend({
    initialize: function(options){
        this.ContainerView = new ContainerView();
    },
    routes: {
        'transactions': 'showTransactionListView',
        'transaction/:id': 'showTransactionDetailView'
    },
    showTransactionListView: function() {
        this.ContainerView.render('TransactionListView');
    },
    showTransactionDetailView: function(id) {
        this.ContainerView.render('TransactionDetailView', id);
    }
});

Then any link to #transations (or just transactions if you're using Backbone History) will call your ContainerView.render('TransactionListView'). And, as a bonus, if you reload the page you'll still be looking at TransactionListView.

Other notes:

  • You'll want to make sure you discard old views when you replace them (by calling .remove() on them) so as to avoid memory leaks. Further Reading
  • You can add some flexibility to your router and use a controller pattern to render subviews with this nifty plugin

Upvotes: 5

Related Questions