I have this code which, for reasons I can't understand, produces an empty object when using require()
. My file structure is like this:
src
|__ public
|__ javascript
|__ collections
| categories.js
| listings.js <-- Always an empty object
|__ models
| category.js
| employer.js
| listing.js
| location.js
| routing
| templates
| tests
| ui-components
| views
The problem file is collections/listings.js
, which seems to simply output as an empty object when required like so:
var ListingsCollection = require('../collections/listings')
src/public/javascript/collections/listings.js
looks like this:
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
Listing = require('../models/listing');
Backbone.$ = $;
module.exports = Backbone.Collection.extend({
url: '/listings',
model: Listing,
parse: function (response) {
return response.listings;
}
});
Here is an example of where things go wrong:
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
LocationModel = require('../models/location'),
ListingsCollection = require('../collections/listings');
Backbone.$ = $;
console.log(ListingsCollection); // > Object {}
module.exports = Backbone.Model.extend({
urlRoot: '/employers',
model: {
location: LocationModel,
listings: ListingsCollection
},
parse: function (response) {
var employer = response.employer;
// Create the child listings
employer.listings = new ListingsCollection;
return employer;
},
toJSON : function () {
var json = _.clone(this.attributes);
_.each(_.keys(this.model), function (child) {
if (this.get(child)) {
json[child] = this.get(child).toJSON();
}
}.bind(this));
return json;
}
});
So there it is - That collection never requires into the employer model such that it can be used to create a child collection for the parent model. I've looked at the source and researched this issue but I'm coming up with nothing so far... It's perplexing.
Since my comment seems to have answered this issue, I thought I'd write it up formally.
When using Require.js you have to think about dependencies between modules. In some sense, this is the same issue as if you weren't using require. Let's pretend you have two files, A.js and B.js, which define an "A" function and a "B" function respectively:
You can add those files in any order to your page, and your code will work. But what if your definition of "B" depends on the definition of "A":
Now, suddenly order matters: you have to include your B.js file after your A.js file or else B's code won't work. And if your A.js also depends on your B.js ...
Then your code is fatally flawed, because B depends on A and A depends on B, and one has to be defined first. This is what is known as a "circular dependency".
Require has the same problem, only it's easier to miss because Require abstracts a lot of things from you. At the end of the day though Require (being JavaScript code) has to run sequentially, which means it has to define your modules in some order. If your module A depends on module B, and B depends on A, you'll have the same circular dependency issue.
Similarly if you have A that depends on B, and B that depends on C, and C that depends on A, you'll also have a circular dependency. Or if you have a C that depends on D that depends ... well you get the idea. Ultimately any module that depends on any other module has to ensure that neither the dependent module or any of its dependencies depend on the original module.
So how do you fix your code? The obvious way would be to remove the circular dependencies, which will certainly work, but there's also another option. Let's say your A depends on B, but only at run-time, not at load time. In other words instead of:
you have:
In this case you can use the single argument "synchronous" form of require to avoid having to require "B" at the top of your file:
Because we only use B after we've defined all our modules Require doesn't have to worry about A coming after B; it can define it whenever it wants because, by the time you actually want to use B, it will already be defined. Of course, this assumes that you have some module which actually does include "B" at the top (if you didn't require wouldn't even know that B exists).
Hope that helps.