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());
Related
I see a difference using create() and push() on collections using Backfire and wonder if this is a misunderstanding on my part, or a bug.
I have an Animal model and Animals collection as below. Normally, the collection is created with an options object containing a zoo_id which is then used to populate the zoo_id in new models. It's a fixed value for this example.
var Animal = Backbone.Model.extend({
initialize: function(attributes, options) {
console.log("model initializing", attributes, options)
}
}),
Animals = Backbone.Firebase.Collection.extend({
firebase: myFirebaseUrl + "/animal",
initialize: function(models, options) {
this.model = function(attrs, opts) {
return new Animal(_.extend(attrs, {zoo_id: 4}))
};
this.on("add", function(model, collection, options) {
console.log("adding", model, collection, options, model.attributes)
})
}
})
var a= new Animals()
If there's data in Firebase, all of the retrieved animal models in a[] have zoo_id = 4, as expected.
When I push a new model
a.push({name: "racoon"})
all of the attribute objects logged to the console have zoo_id = 4. However, the returned object does not have a zoo_id, nor is zoo_id present for the new entry in the Forge.
When I create a new model
a.create({name: "ape"})
all of the attribute objects logged to the console have zoo_id = 4, the returned object has zoo_id = 4, and the new entry has zoo_id = 4 is in the Forge.
If I remove the Firebase extensions and just use a regular Backbone model and collection in the same manner, push returns an object with a zoo_id, and create fails as there's no url set up (as expected).
thanks in advance for clarification!
Push is not part of the functionality overridden by the Backfire API. It pretty much sticks to the same contract as Backbone.Collection. Thus, push simply appends a record to the end of the array without syncing it to any back end.
You could probably create the desired behavior by calling sync after push, as would normally be done with a Backbone collection. I'm not sure how the id would work here, you might need to add one onto the object before it can be synchronized.
However, it's probably simplest to use create/add instead, which are part of BackFire's API and handle server synchronization.
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);
}
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.
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
Every time I call "this.model = new Unidade();", this.model keeps some values of the last value stored in there.
this.model.id => turns to null, but others properties keep the values of the last model.
Model Source
window.Unidade = Backbone.Model.extend({
urlRoot : $urlPadrao + "cliente/externo/unidade",
defaults: { // setting defaults to null or ""
},
initialize: function () {
... // Just initialize the validators methods
},
getDefaults: function() {
// returns the same values of defaults
// since I can't get a "new" model
}
});
In my view:
this.model = new Unidade();
// this.model may be already populated... I'm trying to create a new one
// that's the problem, it keeps some values of the last one
// Shouldn't give me a new model populate with the defaults?
For the record I'm not messing with the prototypes.
I manage to get the source of this sorcery, somehow, backbone was getting these properties and setting then on the prototype of the model, thus making them static.
Mighty strange.
By any chance are these properties arrays? I found that mine were essentially being treated as pass-by-reference due to what I found in this article :
Arrays in a Backbone.js Model are essentially static?