I've been trying to follow the example here: Creating nested models with backboneJS + backbone-relational + requireJS
And ended up having something different (since I need a Backbone.Collection, not a Backbone.RelationalModel):
define(['exports', 'backbone', 'backbone.relational'], function (Exports, Backbone) {
var ModuleModel = Backbone.RelationalModel.extend({
relations : [{
type : Backbone.HasMany,
key : "children",
relatedModel : 'ModuleModel',
collectionType : 'ModuleCollection'
}]
});
Exports.ModuleModel = ModuleModel;
return ModuleModel;
});
define(['exports', 'backbone'], function(Exports, Backbone) {
var ModuleCollection = Backbone.Collection.extend({
model : Exports.ModuleModel,
});
Exports.ModuleCollection = ModuleCollection;
return ModuleCollection;
});
But, when I do:
var modules = new ModuleCollection();
modules.fetch();
I get:
TypeError: this.model is undefined
And it's kind of obvious since Exports.ModuleModel is created after ModuleCollection has been initialized.
Any hints on how to achieve this self-referential model?
Thanks in advance!
One of the advantages of AMD is that it eliminates the need for global variables, thereby reducing the time needed to lookup object references. So yea, putting objects in the global namespace most definitely is not AMD :p
The chief problem in your original post appears to stem from a misunderstanding of RequireJS. I see three (maybe four) problems:
- Neither module declares a reference to the other (they only reference Backbone and Backbone-relational).
exports
appears to be used as a substitute for the global namespace - it isn't. You use exports
to create an empty object for the module that is available immediately for reference by other modules - but you have to reference it!
relatedModel
and collectionType
accept either object references or the names of objects accessible through global namespace. Since you're using AMD you're not defining globals, therefore you need to provide valid object references.
collectionType
will simply point to the imported Collection
object
relatedModel
is a bit trickier. Since it's a self reference, the object it's referencing won't actually exist until the extend()
operation completes, so you have to delay it's assignment until later.
- RequireJS requires that each file define exactly one module. I assume that the code samples above represent two separate files, but in case they don't, they should.
This should work:
ModuleModel.js:
define(['exports', 'ModuleCollection'], function (exports, Module) {
'use strict';
var Model = Backbone.RelationalModel.extend({
relations : [{
type : Backbone.HasMany,
key : 'children',
// `Model` undefined at this point in time, so this line is useless:
relatedModel : Model,
// `Collection` is attached to the imported `Module` object:
collectionType : Module.Collection
}]
});
// Now that `Model` is defined, we can supply a valid object reference:
Model.prototype.relations[0].relatedModel = Model;
// Attach `Model` to `exports` so an obj ref can be obtained elsewhere
exports.Model = Model;
});
ModuleCollection.js
define(['exports', 'ModuleModel'], function(exports, Module) {
'use strict';
var Collection = Backbone.Collection.extend({
// `Model` is attached to the imported `Module` object
model : Module.Model,
url: 'data.php' // <-- or wherever
});
// Attach `Collection` to `exports` so an obj ref can be obtained elsewhere
exports.Collection = Collection;
});
Main.js
define(['ModuleCollection'], function(Module) {
'use strict';
var modules = new Module.Collection();
modules.fetch();
});
Hope this helps...
Ok, I solved it by exposing the namespaces in the global scope (just removed var
).
I don't know if this is AMD complaint, but at least it doesn't look that bad:
define(['backbone', 'backbone.relational'], function (Backbone) {
ModuleModel = Backbone.RelationalModel.extend({
relations : [{
type : Backbone.HasMany,
key : "children",
relatedModel : 'ModuleModel',
collectionType : 'ModuleCollection'
}]
});
return ModuleModel;
});
define(['backbone', 'models/module'], function(Backbone, ModuleModel) {
ModuleCollection = Backbone.Collection.extend({
model : ModuleModel,
});
return ModuleCollection;
});
If someone has a better solution, feel free to post it!