Backbone model: initialize vs. constructor - javascript

Reading through the docs, I see that you can replace the constructor for Backbone's extend on a model class. But what's the difference between doing this vs. doing it on the initialize method? Aren't both called when you use new?
var Library = Backbone.Model.extend({
constructor: function() {
this.books = new Books();
Backbone.Model.apply(this, arguments);
},
parse: function(data, options) {
this.books.reset(data.books);
return data.library;
}
});
vs.
var Library = Backbone.Model.extend({
initialize: function() {
this.books = new Books();
Backbone.Model.apply(this, arguments);
},
parse: function(data, options) {
this.books.reset(data.books);
return data.library;
}
});

"constructor" runs before Backbone sets up the structure.
"initialize" is called inside the structure's constructor function.
In other words if you need to add anything to the object before Backbone sets up the structure you might want to use "constructor". If you need to add something to your object after that Backbone sets up the structure you can use "initialize".
From: https://github.com/jashkenas/backbone/issues/720

Related

Inheriting 'Class' properties in Backbone

I am trying to determine how it may be possible to inherit "class" properties of Backbone objects. Class properties are explained here:
https://taurenmills.wordpress.com/2011/10/08/backbone-js-with-class-properties/
We can do inheritance with Backbone, like so:
var BaseModel = Backbone.Model.extend({
someFunc1: function(){
},
someFunc2: function(){
}
},
{ //class properties below
newInstance: function(attrs,opts_{
var model = new BaseModel(attrs);
model.randomProp = opts.randomProp;
return model;
}
});
var SubModel = BaseModel.extend({
someFunc2: function(){ //overrides someFunc2 in BaseModel
someFunc1(); calls someFunc1 in BaseModel
}
},
{ //class properties below
newInstance: function(attrs,opts){
var model = new SubModel (attrs);
model.randomProp = opts.randomProp;
return model;
}
}
);
my question is: how can we inherit the "class" functions from BaseModel?
I would like for my subclasses of BaseModel to inherit the class function newInstance.
But I don't think that's possible. Coming from Java, it's not straightforward as how to inherit static methods which refer to the subclass itself in the inherited static method, not the superclass.
ignoring what I just said, in other words, I would like to do something like:
newInstance: function(attrs,opts){
var Constr = this.constructor; //*but* the 'this' keyword will not be available in the newInstance function, which is like a static method in Java
var model = new Constr(attrs);
model.randomProp = opts.randomProp;
return model;
}
the reason I want to implement the class function is so that I can set a specific property on the new model instance everytime that function is invoked.
You can access method from your base class by using your object's prototype.
For example if you wanted to call your base classes initialize method you would do the following
//base class
initialize: function (attributes, options) {
this.someProperty = options.somePropery
},
//sub class
initialize: function (attributes, options) {
BaseModel.prototype.initialize.call(this,attributes, options);
},
That said, inheritance really works a bit differently in JavaScript (this isn't really unique to backbone) to how it works in Java and you probably should read up a bit on the object prototype.

Backbone Collection inheritance, two constructors

I am looking for a way to have a BaseCollection for all my collections for shared behavior.
I was able to successfully do it with shared functions, but I want some initialization logic to occur in the BaseCollection so that all my collections trigger an event 'model-change'. Is there a way for my BaseCollection to have a constructor - is the code below the best way to do it? I assume if I put two initialize functions or two constructor override functions then only one will get called. So it seems I can only use one of them each.
var BaseCollection = Backbone.Collection.extend({
constructor: function () {
var self = this;
this.on('change',function(model,property){
self.trigger('model-change',model,property);
});
Backbone.Collection.apply(this, arguments);
},
parse: function (resp) {
if (resp.success) {
return resp.success;
}
else if (resp.error) {
return this.models;
}
else {
return resp;
}
},
});
var UsersCollection = BaseCollection.extend({
constructor: function () { //this will override the constructor in BaseCollection, but I want both...
this.givenName = '#UsersCollection';
Backbone.Collection.apply(this, arguments);
},
initialize: function (models, opts) {
//do something here
}
});
Is there a better way to do it? One problem that I have is that I can't override constructor twice, I can only override it in the parent (BaseCollection) class. So is there a way to chain constructors? Or do I pretty much only have two options?
You can probably just extend and apply from the BaseCollection instead of the Backbone.Collection to get the results you want from your example:
var BaseCollection = Backbone.Collection.extend({
constructor: function () {
// your base collection constructor
// run the Backbone.Collection constructor on this
Backbone.Collection.apply(this, arguments);
}
});
// extends methods on BaseCollection
var UserCollection = BaseCollection.extend({
constructor: function () {
// your user collection constructor
// run the BaseCollection constructor on this
BaseCollection.apply(this, arguments);
}
});

Extending dynamic and mapped data in Knockout

Using Knockout.JS, I'm trying to determine how to best extend objects in the view model when they will be both loaded via the mapping plugin and dynamically added. In this example, I'm adding a method addChild to the Person object.
Extending during mapping:
var myPersonModel = function (data) {
ko.mapping.fromJS(data, {}, this);
this.addChild = function () {
this.children.push(new Child());
} .bind(this);
}
var mapping = {
'people': {
create: function (options) {
return new myPersonModel(options.data);
},
}
}
var viewModel = ko.mapping.fromJS(data, mapping);
Extending during dynamic creation:
function Person(id, name, children) {
this.id = ko.observable(id);
this.name = ko.observable(name);
this.children = ko.observable(children);
this.addChild = function () {
this.Forms.push(new Child());
} .bind(this);
}
But it seems to me there must be an easier way to do so such that I don't need to repeat myself, and both mapped and new objects get the addChild method.
Take a look at this answer, especially the live example on JSFiddle.
It looks like your code is close to what it needs to be.
I think you just need to fold mapping and ko.mapping.fromJS into your Person constructor.

Adding more functions to Backbone Models

I am attempting to add some functions to backbone so that I can communicate with mongodb. Now I know this won't work client side; however, I do like backbone's functionality for server side model logic as well. I noticed that I would be doing a bunch of repeat work if I kept adding the same functionality for each model so decided to create a "app_model" file to extend backbone when I'm server side. I also don't want to override the standard Backbone functions because they will be useful client side.
So let's take this user class for instance:
var Backbone = require('./app_model');
var User = Backbone.Model.extend({
name : "users",
defaults: function() {
return {
username: "default",
role: 2,
created: new Date(),
updated: new Date(),
logged: new Date()
};
},
idAttribute: "username",
/**
* A predefined listing of user roles
*/
userRoles: [
"admin", //0
"author", //1
"user" //2
],
initialize: function() {
if(!!app) {
this.svrInit();
}
}
});
module.exports = User;
And I want to append functions onto backbone by using my "app_model.js" file, which looks something like this currently:
var Backbone = require('backbone'),
Deferred = require('Deferred'),
when = Deferred.when;
Backbone.Model.prototype.svrInit = function() {
//TODO: perhaps the code below should be made static some how so we don't have a bunch of instances of collection
var model = this;
if(!!app.db){
app.db.collection(this.name,function(err,collection){
model.collection = collection;
});
}
};
Backbone.Model.prototype.svrSave = function() {
var model = this.toJSON();
var dfd = new Deferred();
this.collection.insert(model, {safe:true}, function(err, result){
dfd.resolve();
});
return dfd;
};
Backbone.Model.prototype.svrFind = function(options) {
var model = this.toJSON();
var dfd = new Deferred();
this.collection.find(options, {safe:true}, function(err, result){
dfd.resolve();
});
return dfd;
};
module.exports = Backbone;
I ran my tests when I abstracted this out and it seemed to work alright. Is there a better way to do any of this? Any pit falls? I am using the global "app" variable, is that bad? If so what are some ways around it? I do find it ugly that I had to put this.svrInit() inside the init function at the model level is there anyway to automatically make that happen after creation?
So I've been thinking about this question for a couple days and I the cleanest thing I've come up with is something like this:
var MyModel = function( attributes, options ) {
Backbone.Model.apply( this, arguments );
this.specialInitializer();
};
MyModel.extend = Backbone.Model.extend;
_.extend( MyModel.prototype, Backbone.Model.prototype, {
specialInitializer: function() {
// called after the users 'initialize'
console.log("MyModel initialized.", this);
},
otherNewMethod: function() {
// this is just like any other instance method,
// just as if Backbone.Model implemented it
}
} );
So what this does is basically make an entirely new 'kind' of Backbone.Model. One which also calls specialInitializer. If you look at the backbone source just after the constructor definition for Backbone.Model you'll see this is a similar strategy.
Construct the instance.
Call an initializer the implementor is supposed to define.
Extend the prototype with functionality (in their case Backbone.Events, in ours, Backbone.Model).
Your new initializer can of course call whatever else it needs, etc.
As for your other questions about the static collection stuff and global app variable, I'm afraid I don't follow exactly what is going on there since I don't see a definition for app and don't know what you're using the collection for.
Here's a fiddle that demonstrates this with some extra logging and such.
I'm working on a fairly large code-base with 4-5 levels of inheritance in the views. This is the pattern I'm using:
var BaseView = Backbone.Model.extend({
somefunc: function() {
//contents
},
otherfunc: function(a,b,c) {
//contents
},
//...
});
var User = BaseView.extend({
// things in user view can now access somefunc and otherfunc
});
Here's a quick example in a jsfiddle (note the doSearch function being inherited)

Backbone.js view instance variables?

I'm learning Backbone.js and am trying to figure out whether it's possible to have instance variables in Backbone views.
My goal is to load a view's templates from an external file when a view is being instantiated. Currently I'm storing them in a global variable in the Backbone app's global namespace, but it would be cleaner to store the templates in a view's instance variables. Currently I have it set up like this:
var templates = {};
MessageView = Backbone.View.extend({
initialize: function() {
$.get('js/Test2Templates.tpl', function(doc) {
var tmpls = $(doc).filter('template');
templates['MessageView'] = [];
tmpls.each(function() {
templates.MessageView[this.id] = $.jqotec($.unescapeHTML(this.innerHTML));
});
});
},
render: function() {
var tpldata = {name: 'Ville', thing: 'Finland'};
$('#display').jqoteapp(templates.MessageView.greeting_template, tpldata);
},
events: {
"click input[type=button]": "additionalTransactions"
},
additionalTransactions: function() {
this.render();
}
});
But instead of using "templates" being defined as a global var, I'd like to create 'templates' in a view's initialize function, along these lines (but this doesn't work):
MessageView = Backbone.View.extend({
view_templates: {},
initialize: function() {
$.get('js/Test2Templates.tpl', function(doc) {
var tmpls = $(doc).filter('template');
tmpls.each(function() {
this.view_templates[this.id] = $.jqotec($.unescapeHTML(this.innerHTML));
});
});
},
render: function() {
var tpldata = {name: 'Ville', thing: 'Suomi'};
$('#display').jqoteapp(this.view_templates.greeting_template, tpldata);
},
events: {
"click input[type=button]": "additionalTransactions"
},
additionalTransactions: function() {
this.render();
}
});
This is probably (?) pretty straightforward and/or obvious, but me being somewhere on the Backbone.js learning curve, I'd much appreciate any help with this!! Thanks!
Your view_templates instance variable is fine (and a good idea as well). You just have to be sure that you're using the right this inside your $.get() callback and inside your tmpls.each() call. I think you want your initialize to look more like this:
initialize: function() {
this.view_templates = { };
var _this = this;
$.get('js/Test2Templates.tpl', function(doc) {
var tmpls = $(doc).filter('template');
tmpls.each(function() {
_this.view_templates[this.id] = $.jqotec($.unescapeHTML(this.innerHTML));
});
});
},
I'm not sure which this.id you want inside the tmpls.each() but I'm guessing that you want the DOM id attribute from the current template so I left it as this.id.
The this.view_templates assignment in your constructor (initialize) is needed because you presumably want each instance of the view to have its own copy of the array. Creating a new view instance doesn't do a deep copy of the the view so if you just have:
MessageView = Backbone.View.extend({
view_templates: {},
// ...
then all the instances will end up sharing the same view_templates object and view_templates will behave more like a class variable than an instance variable.
You can specify your instance variables in the view definition (i.e. the Backbone.View.extend() call) as a form of documentation but you will want to initialize any of them that should behave as an instance variable in your initialize method; read-only or "class variables" like events can be left as part of the view's definition.

Categories

Resources