jeroenjoosen
jeroenjoosen

Reputation: 649

Put a Backbone.js View,Model,Router... inside an object with _.extend()

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

Answers (3)

Geert Wille
Geert Wille

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

mu is too short
mu is too short

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

theotheo
theotheo

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

Related Questions