How to call model's method from collection in backbone.js? - javascript

I've been trying to learn backbone.js these days. What I've is a model and a collection. The model has some default properties defined in it which I want to fetch within a collection. This is my code:
var model = Backbone.Model.extend({
defaults : {
done : true
}
});
var collection = Backbone.Collection.extend({
model : model,
pickMe : function () {
log(this.model.get('done')); //return undefined
}
});
var col = new collection();
col.pickMe();
How do I call methods defined in my model from collection? Am I doing wrong here?

The basic setup of Backbone is this :
You have models which are part of collection(s). So here in your setup you have a model constructor model and collection constructor collection, but you have no models in your collection and if you have any models they will be an array, so your code should be something like this
var model = Backbone.Model.extend({
defaults : {
done : true
}
});
var collection = Backbone.Collection.extend({
model : model,
pickMe : function () {
for ( i = 0; i < this.models.length; i++ ) {
log(this.models[i].get('done')); // this prints 'true'
}
}
});
// Here we are actually creating a new model for the collection
var col = new collection([{ name : 'jack' }]);
col.pickMe();
You can check the working jsFiddle here : http://jsfiddle.net/S8tHk/1/

#erturne is correct, you're trying to call a method on your model constructor, not a model instance. That doesn't make sense.
If you really want to define methods on the collection, then #drinchev provides an example of how to iterate through the models in the collection and invoke their methods. Although, the example is rather clunky -- using the built-in iterator methods would be more elegant.
Depending what you're trying to accomplish, you may want to just use the built-in collection iterator methods to invoke methods on each model instead of defining methods on the collection. E.g.:
var Model = Backbone.Model.extend({
defaults : {
done : true
}
});
var Collection = Backbone.Collection.extend({
model : Model
});
var col = new Collection([{}, {done : false}, {}]);
col.each(function (model) {
log(model.get('done'));
});

I think you'd better attach your model methods to the model itself, not in the collection. So you should have somethings like this:
var model = Backbone.Model.extend({
defaults : {
done : true
},
pickMe : function () {
log(this.get('done'));
}
});
var collection = Backbone.Collection.extend({
model : model,
});
var model = collection.get(id)
model.pickMe()

Related

How do you prevent Knockback.js creating view models for null relations?

If my backbone models have relationships (for example, created by backbone-relational), those relationships might be nullable, leading the foreign key fields to sometimes be null.
If I have several knockback view models, and I've specified factories so that when following relations I get the view models with the desired functionality for the model, when it encounters an attribute that is null, it goes ahead and creates a view model passing null as the model, which likely breaks most of the view model's functionality.
Example:
var ChildViewModel = kb.ViewModel.extend({
constructor: function (model, options) {
// this is the problem I'm trying to avoid - creating a view model with
// no model
if (!model) {
// just report the error somehow - the jsfiddle has the
// relevant HTML element
document.getElementById("error").innerHTML = "ChildModelView initialised without a model!";
}
kb.ViewModel.prototype.constructor.apply(this, arguments);
}
});
var ParentViewModel = kb.ViewModel.extend({
constructor: function (model, options) {
// specify factories here, because this way you can easily deal with
// reverse relationships, or complicated relationship trees when you
// have a large number of different types of view model.
kb.ViewModel.prototype.constructor.call(
this,
model,
{
factories: {relation1: ChildViewModel,
relation2: ChildViewModel},
options: options
}
);
}
});
// if we assume that relation2 is a nullable relationship, backbone-relational,
// for example, would give us a model that looks like this:
var model = new Backbone.Model({
id: 1,
relation1: new Backbone.Model({id: 2}), // this works fine
relation2: null // this causes a problem
});
var view_model = new ParentViewModel(model);
And the fiddle:
https://jsfiddle.net/vbw44vac/1/
I've just discovered what I think might be a reasonable solution.
Your factories don't have to be ViewModel "classes", but can be factory functions. So:
var nullable = function (view_model_class) {
var factory = function (object, options) {
if (object === null) return object;
return new view_model_class(object, options);
};
return factory;
};
And then when you're defining your factories:
kb.ViewModel.prototype.constructor.call(
this,
model,
{
factories: {relation1: nullable(ChildViewModel),
relation2: nullable(ChildViewModel)},
options: options
}
);

Backbone: Id not being set to model

I have tried the following to set an id to my model:
var globalCounter = 1;
var Model = Backbone.Model.extend({
initialize: function () {
this.id = globalCounter;
globalCounter += 1;
}
});
myModel = new Model();
console.log(myMode.get('id')); // prints undefined
How can I set an id to my models?
You need to use the set() function instead (http://jsbin.com/agosub/1/);
var globalCounter = 1;
var Model = Backbone.Model.extend({
initialize: function () {
this.set('id', globalCounter);
globalCounter += 1;
}
});
myModel = new Model();
console.log(myModel.get('id')); // prints 1
You must use :
this.set('id', globalCounter);
instead of this.id = globalCounter;
You are adding the id value to the Model object, but you want to add it to Model.attributes object. And that what is doing Model.set() method.
model.set("key", value) will put the value in model.attributes.key;
model.get("key") will return the value inside model.attributes.key
This is a little weird for new comers to Backbone, but it's a major (and easy) point to get. It's designed so that using model.set(...) will fire change events you can easily catch to update your views.
Backbone and ES6 Update :
The Backbone attribute object is outdates by ES6 getters and setters. Theses functions can overwrite the standard access.
Warning : this is pseudo-code that may be one day used with ES6 !
class MyModel extends Backbone.Model{
get id(){ return this.attributes.id; }
set id(id){ this.attributes.id = id; }
}
This would allow to write :
let myModel = new Model();
myModel.id = 13; // will use myModel.id(13)
console.log (myModel.id); // will show myModel.id()
As of today, this is only a dream of a Backbone 2. After basic searches, I've seen nothing about that coming.

Backbone render return this

I'm trying to figure out some of the 'patterns' to set up a Backbone-project. In the examples below, in the 'render'-function, the author returns an instance of 'this'.
Why is this? Is it specific for the example, or something common for Backbone? I don't see why one should return 'this' in the 'render'-function.
The examples
http://backbonefu.com/2011/08/filtering-a-collection-in-backbone-js/
Calling a jQuery plugin in a Backbone render method
This is just a common practice so you can call render() and to chain another method call.
It is a common pattern that the Views don't insert its HTML content in the page, and this job is done by the instance that instantiate the View in a first place.
Then what you have to write in the code that instantiate the View is something like this:
var myView = new MyView({ model: myModel });
myView.render();
$(myDOMElement).html( myView.el );
But if render() returns the View itself you can write the above code like this:
var myView = new MyView({ model: myModel });
$(myDOMElement).html( myView.render().el );
The meaning of returning this, is for providing chaining possibilities.
For example, lets assume:
var obj = {
prop1 : 0,
method1 : function(){
},
method2 : function(){
}
};
//Then you could do something like:
obj.method1();
obj.method2();
obj.prop1 = 1;
All actions on obj you need to do separately.
Now consider:
var obj = {
prop1 : 0,
method1 : function(){
return this;
},
method2 : function(){
return this;
}
};
//Now you could do these
obj.method1().prop1 = 1;
obj.method1().method2().method1();

Add values to a view with JSON and Backbone.js

I have a View (created using Backbone.View.extend) and a JSON object, I actually get the object from a couchdb database. How do I give the values to the view?
For example, I have these two objects:
var personJSON = {"name":"Buddy","email":"trout#fish.net"}
var personDetailsView; //Backbone.View
Is there a way to pass the values into the view without explicitly mapping them to the model?
I've found examples where you can add objects to a Backbone collections but not a view.
If you need to have access to the JSON object within the PersonDetailsView (Backbone.View) object, one way of doing this is to pass the JSON parameter as an option in the View's constructor:
//view definition
var PersonDetailsView = Backbone.View.extend({
initialize: function(){
this.person = this.options.person; //do something with the parameter
}
});
var personJSON = {"name":"Buddy","email":"trout#fish.net"};
var personDetailsView = new PersonDetailsView({ person : personJSON });
From Backbone's official documentation:
When creating a new View, the options you pass are attached to the view as this.options, for future reference.
Usually, you retrieve model via model collection and pass it to view:
var person=personCollection.find(1);
var view=new PersonView({model: person});
var personView = Backbone.View.extend({
template: JST['personTemplate'],
render: function(){
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
$('#container').html(personView.render().el);

Is backbone.js model's view instance render function static?

This is a general question about backbone.js and javascript, I'm intermediate in javascript:
If you have a collection of models, each connected to an instance of a view, is each model instance's view instance containing a full instance of the view's render method? If so, what is recommended way to ensure that the render method is 'static', so that memory is not wasted if each model instance requires the exact same render function?
In the example below, does each hat instance also contain an instance of the render function, or is it pointing to a 'static' render function?
var Hat = Backbone.Model.extend({});
var HatCollection = Backbone.Collection.extend({
model : Hat,
});
var HatView = Backbone.View.extend({
render : function() {
// output the hat's html
}
});
var hats = new HatCollection([ new Hat(), new Hat(), new Hat()])
hats.each(function(hat) {
hat.view = new HatView({ model : hat });
});
There are no real "static" or "class" methods in Javascript. What you have is a method defined on the class's prototype.
When you use Backbone.View.extend(), everything you pass is added to the prototype, so they are indeed what you would call "static" methods.
Just check if the render method is a member of the instance or the prototype:
(function () {
var HatView = Backbone.View.extend({
render : function() {
console.log("rendering a hat...");
}
});
var hview = new HatView();
console.log(hview.hasOwnProperty("render")); // false
console.log(hview.render === HatView.prototype.render); // true
}());

Categories

Resources