I'm using Backbone with RequireJS, and my View needs to switch between like, say, 20 collections corresponding to respective RESTful APIs.
The "normal" way handles things fine, except that for each API a new Collection needs to be defined, resulting in a hugely bloated codebase:
Collection (x 20 times)
define(function(require) {
var Backbone = require('backbone');
var c = Backbone.Collection.extend({
url: '/path/to/api-1'
});
return new c();
});
View
define(function(require) {
var Backbone = require('backbone'),
c1 = require('./collections/collection-1'),
...
c20 = require('./collections/collection-20');
var v = Backbone.View.extend({
/* Some methods to toggle between Collections here */
});
return v;
});
By only doing return c;
inside the Collection, and calling new c({url: /path/to/api-1});
inside the View, I was able to cut down the highly duplicated Collection defines; but now on each new c();
calls, the APIs will always be poked to retrieve a new copy of data, which is a waste to resources and something I do not want.
Is there any way to make Collections "persistent"? Which better meets all of the criteria below:
- For each API's corresponding Collection, only one
new
is performed;
- Each Collection can be shared/accessed across different Views defined using RequireJS;
- The definition of the Collection can be reused by all APIs;
- The global namespace is not polluted at all.
In a similar vein to the other answer, you could have a collections array defined as an AMD module:
Collections
define(['backbone', 'underscore'], function(Backbone, _) {
var collections = [];
var urls = ['/path/to/api-1', '/path/to/api-2', '/path/to/api-3'];
_(urls).each(function(url) {
collections.push(Backbone.Collection.extend({
url: url
}));
});
return collections;
});
And your views could use it like this:
View
define(['Backbone', 'collections'], function(Backbone, collections) {
var v = Backbone.View.extend({
/* Some methods to toggle between Collections here */
});
return v;
});
Found a rather hackish solution:
Wrap your Collection with another Model:
define(function(require) {
var Backbone = require('backbone'),
m = require('../models/model');
var wrapper = Backbone.Model.extend({
initialize: function() {
var $_path = this.get('path');
var c = Backbone.Collection.extend({
model: m,
url: $_path,
initialize: function() {
this.deferred = this.fetch();
}
});
this.set({
collection: new c()
});
}
});
return wrapper;
});
Then, define a Cache container:
define(function(require) {
return cache = {};
});
Require both the Cache container and the wrapped Collection in your View module:
define(function(require) {
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
cache = require(cache),
wrapper = require('../collections/collection');
var v = Backbone.View.extend({
render: function($_path) {
var c = cache[$_path] || cache[$_path] = new wrapper({
path: $_path
}).get('collection'));
}
....
});
return v;
});
This way you get both the benefits of instantiate new Collections with dynamically-generated $_path
values, and being able to cache data fetched from your APIs so long the page is not refreshed - implement localStorage
if you'd like the data to persist through page refreshes.
You don't need to make a new file / collection.
You could just do something like this in your view:
var collections = [];
collections.push( Backbone.Collection.extend({url: '/path/to/api-1'});
collections.push( Backbone.Collection.extend({url: '/path/to/api-2'});
collections.push( Backbone.Collection.extend({url: '/path/to/api-3'});
...
collections.push( Backbone.Collection.extend({url: '/path/to/api-20'});
var view1 = Backbone.View.extend( {data:collections} );
var view2 = Backbone.View.extend( {data:collections} );
You will then have an array loaded with all your collections