I have this collection:
App.Collection.Pieces = Backbone.Collection.extend({
model: App.Model.Piece,
initialize: function() {
this.add(
// The `King` model extends the `Piece` model
new App.Model.King({
cid: 'wk',
color: 'white'
})
);
}
});
Then I have this code in a view:
this.white = new App.Collection.Pieces();
console.log(this.white.get('wk'));
I have two questions:
Can I add a model B to a collection of A models, if B extends A?
The console.log statement returns undefined. Any reason why the cid property is not being set?
Thanks.
You can add any object or model to a collection. If its a plain JS object, the model defined for the collection will be instantiated. Annotated source
According to Backbone's documentation the cid is automatically assigned to all models created. Looking at the annotated source it seems the cid is automatically generated - you cannot assign a custom cid to a model.
Related
Since Collections is actually a model, it has attributes and so on, like in this example
var Images = Backbone.Collection.extend({
url: 'https://api.myjson.com/bins/4v2d8'
});
var View = Backbone.View.extend({
el: $('.images'),
initialize: function(){
this.listenTo(this.collection, 'sync', this.render, this); // what is this keyword as the last param here?
},
render: function(){
this.collection.each(function(model){
this.$el.append($('<p>'+model.get('name')+'</>' ));
}, this);
}
});
$(function(){
var images = new View({ collection: new Images() });
images.collection.fetch();
});
http://jsbin.com/gohedeguto/edit?html,js,output
But why this one doesn't work?
http://jsbin.com/seyoyeqeku/edit?html,js,output
I replaced Collection with Model and passed to the View. I got this.model.each of undefined.
While T-J is right, he doesn't elaborate on why.
A Backbone model has lots of functions to manage an attributes hash, and is often referenced by an id.
A Backbone collection is an array of Backbone model instances. It has some similar and lots of different functions to manage its models array property which is where the model instances are stored.
Some of these functions are proxies to Underscore.js functions.
Model's Underscore methods
keys
values
pairs
invert
pick
omit
chain
isEmpty
Collection's Underscore methods
46 in total at the moment, see the list.
collection.each(iteratee, [context])
is a proxy to _.each(collection.models, iteratee, [context]).
Backbone's Model do not have a each function because it doesn't really make sense and there are better ways to loop through the attributes of a model.
Since Collections is actually a model
No. This is wrong assumption. Collection is not a model which is why your code doesn't work.
Model does not have an each method and your code throws following error:
Uncaught TypeError: this.model.each is not a function(…)
and don't follow the other answer that iterates over model properties and prints it's name, it doesn't make sense. Because model is supposed to represent an entity, like a person. It'll only have one property containing name, there's no point in iterating over model properties to access one of them, you can directly access it's attributes with get() method without having to iterate.
So I'm looking into backboneJS and I'm trying to figure out when to use the collection attribute and when to use this.collection inside the initialize function? Is there a difference? Here is an example.
Backbone.View.extend({
collection: myCollection,
initialize: function(collectionData) {
this.collection = new app.Library(collectionData);
}
});
When you use "this" in a method of a object, it is set to the object the method called on. So this.collection will always equal to collection property (initialize method will be invoked by a view instance right?).
In my opinion, I always assign the default collection in view option declaration and access the collection reference by this.collection in the method of view object.
//set the default collection to a view object
var CustomView = Backbone.View.extend({
collection: myCollection
});
//or set collection when you instantitate a view
var myView = new CustomView({collection: myCollection});
//and get the reference of view's collection for event registering
initialize:function(){
//refresh view when new data is added to collection
this.listenTo(this.collection,"add",this.render);
}
Here I am passing a model to a Backbone view.
view = new View ({model:{item:4,name:"Ipad"}});
When I console.log that model from within the View. I get:
Object {item: 4, title: "Ipad"}
This is not a backbone model therefore I don't have methods
like toJSON. I realize that if I define a Backbone model and
passed it in everything works fine.
view = new GenreView ({model:new Model({title: 4, title: "Ipad"})});
This logs
r {cid: "c2", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
Why is it that first approach doesn't work and how can I fix it?
Its simply that the special 'model' option expects a Backbone.Model not a javascript object.
So you are correct when you create a new Backbone.Model to pass into the view.
There is nothing to fix as far as I can tell.
You need to use a Backbone.Model instead of a regular JavaScript object {}
var Item = Backbone.Model.extend({
// ...
});
Instantiate the Item model
var myItem = new Item();
Now use your item in the view
var myView = new View({model: myItem});
This answer assumes that View is setup as something like
var View = Backbone.View.extends({
// ...
});
You could cast the object to a Backbone Model in your view's initialize method:
var View = Backbone.View.extend({
initialize: function(options){
if (_.isPlainObject(this.model)) {
this.model = new Backbone.Model(this.model);
}
}
});
This way the view will be able to operate on it's model regardless of whether you passed it an instance of a Backbone.Model or a plain object. You can see an example of it working here: http://jsbin.com/igecEgE/1/edit?js,console
In the Backbone model below I have a nested Backbone collection.
var Student = Backbone.Model.extend({
firstName: null,
lastName: null,
initialize: function() {
this.programCollection = new ProgramCollection({});
}
});
var ProgramCollection = Backbone.Collection.extend({
model: Program
});
However when trying to add Program objects to collection like so...
var testStudent = new Student();
testStudent.get("programCollection").add(new Program());
I get the following error:
Unable to get value of the property 'add': object is null or undefined
Obviously I'm doing something the wrong way since the programCollection is undefined.
Properties of the model instance directly are not the same as attributes properties. If you want a model instance to have a collection, which is not data to be stored directly on that student record, set it as a model instance property (as you are doing) but then just access it directly without calling get.
var testStudent = new Student();
testStudent.programCollection.add(new Program());
I have a Marionette.CompositeView which needs to render a collection.
I would like to filter this collection on fetch and add action.
I tried with the following code (1) but I get the following error (2).
Any ideas, thanks.
(1)
var myCompositeView = Marionette.CompositeView.extend({
initialize: function () {
this.collection = app.taskCollection.where({type: 'todo'});
}
});
(2)
// Uncaught TypeError: Object has no method 'on'
Marionette's CompositeView and CollectionView both expect the collection setting to be a valid Backbone.Collection. The where method on Backbone's collection does not return a Backbone.Collection, it return an array. So you have to wrap a collection around the results:
initialize: function(){
var filtered = app.taskCollection.where({type: 'todo'});
this.collection = new Backbone.Collection(filtered);
}
Of course you can use any type that extends from Backbone.Collection. I just wanted to illustrate the point of it being a collection with this example.