Reputation: 649
I'm building an application and I'm using Backbone.js. Because I'm quiet new to Backbone, I have read there documentation, looked and tested multiple apps and read the Backbone patterns.
Now I'm starting to understand the whole backbone principal, but I have a little problem with something. I have a global object (example: App
) and I put my views, models,... inside it (example: App.Views.ListView
). Now the problem is that I need to extend these object, because there could be default values inside it.
So when I started, I had something like this
App.Views.ListView = Backbone.View.extend({});
But now I want something like this
_.extend(App.Views.ListView, Backbone.View.extend({});
This doesn't work like I want it to. The App.Views.ListView exists (It's defined in a config file where you see the file object structure and the extending does something). The problem is that I can't make an instance of the view (ex new App.Views.ListView();
)
Examples:
The object that is created in my config
App:{
Views:{
ListView:{
myVar: "hello"
DeeperList: {
}
}
}
}
If you look you see that the object App.Views.ListView contains a variable with the value hello and an object DeeperList. But inside this object I want to add my view without overwriting myVar and DeeperList. When you use App.Views.ListView = Backbone.View.extend({});
you will overwrite it, but is there way to avoid this. Like using extend...
Is the someone who also had this problem of who know how to solve it?
Big Thanks!
Upvotes: 1
Views: 472
Reputation: 1654
So this is the object you want:
App = {
Views: {
ListView: {
myVar: "hello"
DeeperList: {}
}
}
}
Just do a jQuery extend with the first parameter set as true and you'll deep extend/merge this namespace with another object/namespace
(function(App, ListView, undefined){
$.extend(true, ListView, {
'DeeperList': Backbone.View.extend({
})
});
})(window.App, App.Views.ListView);
For more information about javascript namespacing visit following interesting blog post: http://addyosmani.com/blog/essential-js-namespacing/
If you don't use jQuery you can also use the extend function that is mentioned in the blogpost.
function extend(destination, source) {
var toString = Object.prototype.toString,
objTest = toString.call({});
for (var property in source) {
if (source[property] && objTest == toString.call(source[property])) {
destination[property] = destination[property] || {};
extend(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
};
Upvotes: 1
Reputation: 434665
Backbone's extend
isn't the same as Underscore's extend
. Underscore's extend
is little more than a for
loop for (shallow) copying object properties:
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};
Backbone's extend
is a wrapper for its internal inherits
function:
var extend = function(protoProps, classProps) {
var child = inherits(this, protoProps, classProps);
child.extend = this.extend;
return child;
};
//...
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
var inherits = function(parent, protoProps, staticProps) {
var child;
// [Bunch of code for properly setting up the prototype chain and elided]...
return child;
};
Take note of the return child
calls in both extend
and inherits
: they both create and return something new, they don't modify their arguments.
The problem with using _.extend
to mix a view "class" into an existing object:
_.extend(App.Views.ListView, Backbone.View.extend({});
is that _.extend
doesn't do anything with the prototype, _.extend
also doesn't magically convert an object into a function so you can't call new
on what _.extend
gives you. You can mix properties into a view "class" but you have to mix the properties into the prototype
and the assign the view to its final destination so that you get a function in App.Views.V
:
var V = Backbone.View.extend({ ... }); // Create the view.
_.extend(V.prototype, App.Views.V); // Mix App.Views.V into its prototype.
App.Views.V = V; // Replace App.Views.V with our view function.
Demo: http://jsfiddle.net/ambiguous/5KSbw/
All that chicanery is going to confuse the poor soul that has to extend and maintain this code so I'd recommend against it. You'd be better off using a separate object to hold your defaults:
var DEFAULTS = {
Views: { ... }
};
and then reference those defaults explicitly:
App.Views.ListView = Backbone.View.extend({
myVar: DEFAULTS.Views.ListView.myVar,
//...
});
You will have a problem with DeeperList
though, you'd have to explicitly copy its content to avoid alter the DEFAULTS
version if you just copied a reference:
App.Views.ListView = Backbone.View.extend({
DeeperList: _.extend({}, DEFAULTS.Views.ListView.myVar),
//...
});
And if DeeperList
contained objects or arrays, you'd need to use jQuery's $.extend
with true
as the first argument to get a deep copy:
App.Views.ListView = Backbone.View.extend({
DeeperList: $.extend(true, {}, DEFAULTS.Views.ListView.myVar),
//...
});
Upvotes: 0
Reputation: 2702
If I understood you correctly, App.Views.ListView
is kind of mixin. So you could do something like:
var App.Views.ExtendedListView = Backbone.View.extend(_.extend(App.Views.ListView, {
newMethod: function () {
// ...
}
}));
var extListView = new App.Views.ExtendedListView();
It looks a little ugly, but it works. If you could show your App.Views.ListView
, maybe we can to find a more beautiful decision.
Upvotes: 0