Backbone: Id not being set to model - javascript

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.

Related

Pushing to properties in Backbone [duplicate]

This question already has an answer here:
Backbone View extends is polluted
(1 answer)
Closed 8 years ago.
I spent a lot of time trying to catch a bug in my app. Eventually I set apart this piece of code which behavior seems very strange to me.
var Model = Backbone.Model.extend({
myProperty: []
});
var one = new Model();
var two = new Model();
one.myProperty.push(1);
console.log(two.myProperty); //1!!
What's the reason behind it? Why it acts so? How to avoid this type of bugs in code?
Inheritance in JavaScript is prototypical - objects can refer directly to properties higher up in the prototype chain.
In your example, one and two both share a common prototype, and do not provide their own values for myProperty so they both refer directly to Model.protoype.myProperty.
You should create new myProperty array for each model you instantiate. Model.initialize is the idiomatic place for this kind of initialisation - overriding constructor is unnecessarily complex.
var Model = Backbone.Model.extend({
initialize: function() {
this.myProperty = [];
}
});
Alternatively you could make myProperty as an attribute of the model:
var Model = Backbone.Model.extend({
defaults: function() {
return {
myProperty: []
}
}
});
It is important to note that defaults is a function - if you were to use a simple object you would encounter the same shared reference issue.
Actually its because myProperty is an array, and as you know arrays will be stored by reference. Just to test consider the following code:
var Model = Backbone.Model.extend({
myProperty: [],
messege: ''
});
var one = new Model();
var two = new Model();
one.messege = 'One!';
two.messege = 'Two!';
console.log(one.messege ); // 'One!'
console.log(two.messege ); // 'Two!'
An alternative around this could be:
var Model = Backbone.Model.extend({
constructor: function() {
this.myProperty = [];
Backbone.Model.apply(this);
}
});
var one = new Model();
one.myProperty.push(1);
var two = new Model();
console.log(two.myProperty); // []
The documentation says:
constructor / initialize new Model([attributes], [options])
When creating an instance of a model, you can pass in the initial values of the attributes, which will be set on the model. If you define an initialize function, it will be invoked when the model is created.
In rare cases, if you're looking to get fancy, you may want to override constructor, which allows you to replace the actual constructor function for your model.
So, following the documentation, you'd want to do something like this to get your case running:
var Model = Backbone.Model.extend({
initialize: function() {
this.myProperty = [];
}
});
source: http://backbonejs.org/#Model-extend

Collection/Instance objects style vie javascript without proto

I'm trying to make classical Collection/Instance model via javascript. So Collection object has some method for working with full collection and ((new Collection()) instanceof Instance) has methods to work with the instance. My code is rather simple.
var Collection = function Collection() {
this.message = "collection";
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
};
Collection.prototype = {
collectionMethod: function () {
console.log(this.message);
}
};
var Instance = function Instance(collection) {
this.collection = collection;
this.message = "instance";
};
Instance.prototype = {
instanceMethod: function () {
console.log(this.message);
}
};
// Test exec (values are like expected);
var C = new Collection();
var i = new C();
C.collectionMethod(); // collection
i.instanceMethod(); // instance
i.collection.collectionMethod(); // collection
C.newMethod(); // TypeError
i.newMethod(); // TypeError
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // newMethod: collection
i.newMethod(); // newMethod: instance
But i don't want to use proto because it's not a part of standart and doesn't work in IE at all. Is there any way around in this case?
Some explanations about what's all about. For example you have a collection of users. And you want to be able find the user and create new one.
So you create new collection like
var User = new Collection();
Then you create new instance like.
var me = new User({name: "alex"});
And now you find this instance like
User.find_by_name("alex"); // === me
Also (in fact this is the main reason i'm doing this way instead of just creating something like User.new function to use it like var me = User.new({name: "alex"});) you can know who I am doing something like (if you for example have also var Dog = new Collection())
me instanceof Dog // false
me instanceof User // true
This code:
var I = Instance.bind(null, this);
I.__proto__ = this;
return I;
really doesn't make much sense. Function.bind creates a new function, so anyone calling your Collection function, in any way, will get back a function, not an object whose prototype is set to the function's prototype.
In general, if you want to create an object whose prototype is set to a specific object, you don't set __proto__ since that's not standard, as you stated. The best way is to just use Object.create (which is shimable if you want to support IE8).
var I = Object.create(this);
Also, the reason you're getting errors on newMethod is because you're trying to call them before you add them to the prototype:
Collection.prototype.newMethod = Instance.prototype.newMethod = function () {
console.log("newMethod: " + this.message);
}
C.newMethod(); // should work now
i.newMethod(); // should work now
So seems like it's impossible for now. More information can be found here.
How do I inherit javascript functions ?

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();

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

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()

Set backbone model attribute value to another model

Can I set another backbone model as its attribute ?
I have a model that represents a basic entity, I 'd like to reuse it as part of something else.
But, it looks like Backbone's model attribute value can only be String.
Sort answer: yes you can:
myHouse.set({ door: new Door() })
But, in my opinion, is not a good idea to do so, because I don't think Backbone is expecting to found Objects in the Model.attributes. I didn't try but I don't think methods like Model.toJSON are gonna have a correct behavior if someone of the attributes is an Object.
But said so, I don't see any problem to declare real attributes in the Model that make reference to objects like:
myHouse.door = new Door();
If I understood correctly you need to do something like that:
var ModelOne = Backbone.Model.extend({
method : function() {
console.log('ModelOne > method');
},
sayHello : function() {
console.log('ModelOne > hello');
}
});
var ModelTwo = ModelOne.extend({
method : function() {
ModelOne.prototype.method.apply(this);
console.log('ModelTwo > method');
}
});
var methodTwo = new ModelTwo();
methodTwo.method();
methodTwo.sayHello();
Output:
ModelOne > method
ModelTwo > method
ModelOne > hello
One more variation of setting Backbone.Model to the attribute of another model is to set it as default value:
var UserModel = Backbone.Model.extend({
defaults: {
id: null,
name: null,
address: new AddressModel()
}
});
var someUser = new UserModel();
someUser.address.set('country', 'ZA');
When you are doing someUser.save() data enclosed in someUser.attributes.address will be a usual data object.
! One thing not yet tested by me is population of AddressModel upon someUser.fetch()

Categories

Resources