Get collection length - backbone.js

Using backbone.js and trying to get data from postsList array I got this in chrome console.log
d {length: 0, models: Array[0], _byId: Object, _byCid: Object, constructor: function…}
_byCid: Object
_byId: Object
length: 9
models: Array[9]
__proto__: f
When I'm trying to use console.log(postsList.length) I get 0, but there are 9 models inside. I don't know how to get number of them.

Yes, this is strange behaviour :)
Chrome displays object preview immediately after you have used console.log.
When you entered console.log(collection) it was empty (probably you have fetched model from the server). But at the moment when you expand the object in console Chrome displays actual object params at current moment.
Try this in console:
var object = {key1:{prop:true}};
console.log(object)
object.key2 = true;
console.log(object)
To get collection length use this way:
collection.fetch({context:collection}).done(function() {
console.log(this.length)
});
EDIT
No-no-no :)
Use this.length instead of this.lenght.
collection.fetch({context:collection}).done(function() {
// equals to this.length
console.log(this.size());
// get all models from collection (array of Backbone.Models)
console.log(this.models);
// get all models from collection (like simple array of objects)
console.log(this.toJSON());
// get model with index 1
console.log(this.at(1));
// get model data with index 1
console.log(this.at(1).toJSON());
// get model with id `some-id`
console.log(this.get('some-id'));
// get models data where property `id_str` equals to `292724698935070722`
console.log(this.where({id_str:'292724698935070722'}));
});
For more information look here:
http://backbonejs.org/#Collection

I think Vitaliys answer is a little bit dangerous, because the passed option {context: collection} is:
a) not mentioned in backbones documentation
b) handled deep down in jQuery when fetch triggers some Ajax-call.
Instead the length of the fetched collection can be easily checked in the success- and error- callback of fetch, e.g.:
someCollection.fetch({
success: function(collection) { // the fetched collection!
if (collection.length) {
// not empty
} else {
// empty
}
}
});
See http://backbonejs.org/#Collection-fetch

Related

Backbone collection.reset function returns array

I have the following code in backbone view:
getAccounts: function() {
var filteredCollection = this.view.collection.reset(this.view.collection.where({ AccountStatus: 'Open' }));
return filteredCollection;
}
And I assume that this code returns me collection according to doc link http://backbonejs.org/#Collection-reset
But it returns an array. What is wrong here?
The documentation says
Returns the newly-set models
This means you get an array containing newly set models. It doesn't say it returns the collection itself. There is no reason to return the collection itself because you just performed this action on the collection and you already have access to it.
You can just do return this.view.collection instead.

Backbone model which I see in success callback and error callback is different. #Backbone save

I have a backbone model which has Backbone Collections in it. When I save the model and if it is success then my model object is properly structured as it was. But when error occurs (say validation error), in error callback the model object is modified (Collections inside model object are converted into Array). As a result all my functions defined for that Collections are now "undefined" and gives me error.
save : function() {
this.model.save(_.extend(originalModel.toJSON() || {}, this.model
.toJSON()), {
success : this.onSaveSuccess,
error: this.onSaveError,
include : []
});
},
onSaveSuccess : function(model) {
//Here the model is properly structured
},
onSaveError : function(model, response) {
// Here the model is modified, all collections are now array
//I have to explicitly call my parse method to re structure it.
model = model.parse(model.attributes);
}
I would like to know why is this happening. Am I doing something wrong here ?
For the sake of this example, let's assume the attribute of the model that holds the collection is called "people". It isn't clearly documented, but model.save(attributes) actually behaves like:
model.set(attributes);
model.save();
Here's the relevant annotated source of save(...). What your code is doing is first setting the "people" attribute to the array of people, then attempting to save it. When the save fails, your model has the array, not the collection, as the value of "people".
I suspect your end point is returning the full representation of the model on success, and your model is correctly parsing that representation & re-building the Collection at that point. But your error handler won't do that automatically.
As an aside, in my experience Models that contain Collections are hard to manage & reason about. I've had better luck having a Model that contains an array of data, and then having a method on that Model to build a Collection on the fly. Something like:
var MyModel = Backbone.Model.extend({
// ...
getPeople: function() {
// See if we've previously built this collection
if (!this._peopleCollection) {
var people = this.get('people');
this._peopleCollection = new Backbone.Collection(people);
}
return this._peopleCollection;
}
});
This removes the Collection concept from the server communication (where it's pretty unnecessary), while also providing a smarter data layer of your application (smart Models are a good thing).
The solution for this is passing wait:true in options. This will not modify until and unless server returns a valid response.
save : function() {
this.model.save(_.extend(originalModel.toJSON() || {}, this.model
.toJSON()), {
success : this.onSaveSuccess,
error: this.onSaveError,
**wait:true**
include : []
});
},

Backbone Collection get property

Got a server returning a JSON object like so:
{
'key1':'value'
'key2':{
'key2_0':'value'
}
}
And a collection:
var Collection = Backbone.Collection.extend({
url:api.url//which returns the object above
});
var collection = new Collection();
collection.fetch({
success:function(data){
//do something
}
});
Now i need to use certain properties of the collection throughout my application, but say i need key1, i always have to do collection.at(0).get('key1');//returns 'value', because the data returned is stored within the collection, in a new Array at key 0.
Question:
How to directly... collection.get('key1')//now returns undefined... because it is.
I know i could expose an object to the global scope in the collection success function some_other_var = data.toJSON()[0] and access the some_other_var properties directly, but that's not what i'm looking for;
In order to use the get() function from a Backbone.Collection you need to know the model id or cid wanted.
For instance, lets say your data coming from the server is like follow:
[{
id: '123',
name: 'Alex'
}, {
id: '456',
name: 'Jhon'
}]
In that case you can do this:
this.collection.get('123').get('name') // Return "Alex"
Keep in mind that collection is just a set of model, so behind the scenes by doing collection.get() you are getting a model
Tip: If you don't have any kind of id in your server data, there is always the option of using underscore methods:
find
filter
some
contains
etc
It seems like you're trying to ascribe attributes to a collection, but a collection is merely a set of models. Having additional data that is constant throughout the collection suggests that it should be wrapped inside another Model, which is demonstrated here: Persisting & loading metadata in a backbone.js collection

Backbone model structure gets changed when returning them from web worker

I am trying to reset a backbone collection with an array of models. It gets reset but the model structure is changed (nested one level).
Here is a detailed explanation:
Model
var SeatModel = Backbone.Model.extend({
defaults:{
},
initialize:function () {
console.log('Model initialized');
}
});
Collection
var myCollection = Backbone.Collection.extend({
url:"",
parse:function (data) {
},
initialize:function () {
console.log('Collection initialized');
}
});
Now, I am executing some logic in a web worker, which generates an array of models. The size of the array varies depending on the url I hit.
When the array is ready, I reset the data in the collection using something like:
(Before this, I have instantiated the collection and set it in an service object)
worker.onmessage = function(e) {
newDataForCollection = e.data;
//update the collection
service.get("myCollection").reset(newDataForCollection);
};
After getting reset, the structure of the collection gets changed to something like:
models: Array[3154]
[0...99]
0:g.Model
attributes:
attributes:
price: "12"
Whereas it should be like:
models: Array[3154]
[0...99]
0:g.Model
attributes:
price: "12"
Also the number of models in the array gets reduced. (Should have been around 6100 in this case).
I am unable to figure out, what causes the internal structure to get nested by one level on invoking reset on the collection.
Updated Post
Figured it out. We cannot send objects with functions in post message, so the models in the array just have the attributes and no functions. Related Passing objects to a web worker
Figured it out. We cannot send objects with functions in post message, so the models in the array just have the attributes and no functions. This was related to issue Passing objects to a web worker

Underscore collection pluck returning undefineds [duplicate]

Hi im trying to output every "lat" field from my collection. However anything i do returns the right number of results but they all say undefined, the data is definitely there and the names are definitely right. I've tried using pluck and _.each with a get inside the function and all it ever says is undefined.
This is the current method im trying
var ccLocal = window.router.carsCollection;
_.each(ccLocal.models, function(model) {
console.log(model.lat);
})
logging ccLocal returns the entire collection with all its data so its definitely there. What am i doing wrong?
Using model.get("lat") also fails.
Using console.log(ccLocal.at(0).attributes); returns this
Object {unitID: "03_Cow_30", positionHistory: Array[1]}
positionHistory: Array[1]
0: Object
estimatedSpeed: "39"
isToday: false
lastSoundFileName: "F11"
lastSoundRange: "11"
lastSoundTime: "2008-10-29 20:38:25"
lat: "51.466227"
long: "-0.491647"
minutesAgo: 1016726
status: "1"
time: "2011-07-13 16:03:37"
__proto__: Object
length: 1
__proto__: Array[0]
unitID: "03_Cow_30"
__proto__: Object
Ah, so your model attributes data structure is not what everyone thought. Based on your attributes structure, you need something like this. It's a bit fragile due to assuming positionHistory is an array with at least one element, but that's where your data is.
var ccLocal = window.router.carsCollection;
_.each(ccLocal.models, function(model) {
console.log(model.get('positionHistory')[0].lat);
})

Resources