Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for shared objects in Backbone/Require Application

I've been developing Backbone applications for a little while now, and am just starting to learn to use Backbone with Require.js.

In my backbone app that I am refactoring, I defined a namespace like this: App.model.repo. This model is used over and over again in different views. I do the same thing with a few collections, for example, App.collection.files. These models and collections are bootstrapped in with the initial index file request.

I did find this example, which looks like a great way to get that bootstrapped data in. However, I am struggling with the best way to reuse/share these models and collection between views.

I can think of three possible solutions. Which is best and why? Or is there another solution I am missing entirely?

Solution 1

Define these common modules and collections in the index (when they are bootstrapped in), and then pass them along to each Backbone view as an option (of initialize).

define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html'], 
    function($, _, Backbone, Handlebars, template){     
        return Backbone.View.extend({
            template: Handlebars.compile(template),
            initialize: function(options){
                this.repoModel = options.repoModel; // common model passed in
            }
        });
    }
);

These seems clean as far as separation, but could get funky quick, with tons of things being passed all over the place.

Solution 2

Define a globals module, and add commonly used models and collections to it.

// models/Repo.js
define(['backbone'],
    function(Backbone){
        return Backbone.Model.extend({
            idAttribute: 'repo_id'
        });
    }
);

// globals.js (within index.php, for bootstrapping data)
define(['underscore', 'models/Repo'], 
    function(_, RepoModel){     
        var globals = {};
        
        globals.repoModel = new Repo(<?php echo json_encode($repo); ?>);
        
        return globals
    }
);

define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html', 'globals'], 
    function($, _, Backbone, Handlebars, template, globals){
        var repoModel = globals.repoModel; // repoModel from globals
        
        return Backbone.View.extend({
            template: Handlebars.compile(template),
            initialize: function(options){

            }
        });
    }
);

Does this solution defeat the whole point of AMD?

Solution 3

Make some models and collections return an instance, instead of a constructor (effectively making them Singletons).

// models/repo.js
define(['backbone'],
    function(Backbone){
        // return instance
        return new Backbone.Model.extend({
            idAttribute: 'repo_id'
        });
    }
);

// Included in index.php for bootstrapping data
require(['jquery', 'backbone', 'models/repo', 'routers/Application'],
    function($, Backbone, repoModel, ApplicationRouter){
        repoModel.set(<?php echo json_encode($repo); ?>);

        new ApplicationRouter({el: $('.site-container')});
        Backbone.history.start();
    }
);

define(['jquery', 'underscore', 'backbone', 'handlebars', 'text!templates/NavBar.html', 'models/repo'], 
    function($, _, Backbone, Handlebars, template, repoModel){
        // repoModel has values set by index.php
        
        return Backbone.View.extend({
            template: Handlebars.compile(template),
            initialize: function(options){

            }
        });
    }
);

This I worry could get real confusing about what is a constructor and what is an instance.

End

If you read this far, you are awesome! Thanks for taking the time.

like image 691
Bart Avatar asked Feb 14 '13 19:02

Bart


1 Answers

In my case, I prefer option 3. Although, to prevent confusion, I put every singleton instance in their own folder named instances. Also, I tend to separate the model/collection from the instance module.

Then, I just call them in:

define([
  "instance/photos"
], function( photos ) { /* do stuff */ });

I prefer this option as every module is forced to define its dependencies (which is not the case via namespace for example). The solution 2 could do the job, but if I'm using AMD, I want my module as small as possible - plus keeping them small make it easier to unit test.

And lastly, about unit test, I can just re-define the instance inside my unit test to use mock data. So, definitely, option 3.

You can see an example of this pattern on an Open source app I'm working on ATM: https://github.com/iplanwebsites/newtab-bookmarks/tree/master/app

like image 150
Simon Boudrias Avatar answered Dec 10 '22 20:12

Simon Boudrias