Backbone Collection View each - javascript

I'm new to Backbone.js and getting some trouble with collection view.
Here's what I'm trying to do:
var customersCollection = new _App.Collections.Customers();
var customersView = new _App.Views.Customers({collection: customersCollection});
customersView.render();
And here's a view - I can't understand why I cannot iterate over collection:
_App.Views.Customers = Backbone.View.extend({
render: function() {
console.log('Here is my collection');
console.log(this.collection);
console.log('Now lets iterate over it...');
_.each(this.collection, function(item) {
console.log(item);
}, this);
console.log('...done');
return this;
}
});
What I see in chrome console:
Here is my collection
child {length: 0, models: Array[0], _byId: Object, constructor: function, url: "/admin/customers/latest.json"…}
_byId: Object
length: 5
models: Array[5]
__proto__: Surrogate
Now lets iterate over it...
...done
So I can't figure out why I can see a collection but can't each over it.
Thanks
// SOLVED
I have found why this was going to happen.
Completely missed that .fetch() is asynchronous, so when render() was called, data were still not present in collection.
This code works for me now, so I can go on with templates, etc
_App.Views.Customers = Backbone.View.extend({
initialize: function() {
this.collection = new _App.Collections.Customers();
this.collection.on('sync', this.render, this);
this.collection.fetch();
},
render: function() {
this.collection.each(function(item) {
console.log(item);
});
return this;
}
});
new _App.Views.Customers();
Regards, Nikolay

You're not using _.each appropriately.
Should be:
_.each(this.collection.models, function(item) {
console.log(item);
},this);
or better yet:
this.collection.each(function(item) {
console.log(item);
});

Related

Filtering the collection doesn't work

I have a collection of models with boolean value interviewed and I want to filter them so that my view would display either models with this property set to true or models with this property set to false. In my collection I have the following methods:
var ResumeCollection = Backbone.Collection.extend({
filterActive: function () {
var active = this.where({interviewed: false});
return active;
},
filterInterviewed: function () {
var interviewed = this.where({interviewed: true});
return interviewed;
}
});
and in my view I have the following:
var ResumeList = Backbone.View.extend({
events: {
'click #active': 'showActive',
'click #interviewed': 'showInterviewed'
},
initialize: function () {
this.collection = new ResumeCollection();
},
render: function () {
var self = this;
this.$el.html( $('#filter') );
_.each(this.collection.toArray(), function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});
},
showActive: function () {
this.collection.filterActive();
this.render();
},
showInterviewed: function () {
this.collection.filterInterviewed();
this.render();
}
});
But any time I click #active or #interviewed buttons, it happens nothing and the models with required properties aren't rendered. I've already tried to manage that with reset method or returning a new collection instance with required models, but that's not a solution, because when I succesfully filter the initial collection, it returns me a new collection with the models I need (e.g. models where interviewed: true), and I can't filter it more -- it returns just an empty collection.
I just can't get how can I filter this one collection in the way I need.
You're returning a successfully filtered collection, and then not doing anything with them.
showActive: function () {
this.collection.filterActive();//returns a value you're not using
this.render();
},
showInterviewed: function () {
this.collection.filterInterviewed();//returns a value you're not using
this.render();
}
I suggest adding an optional parameter to your render method that represents the filtered collection. If the parameter is defined, use it. If not, use the unfiltered collection.
Borrowing some of #Simo's code to return a new collection.
filterActive: function () {
var active = this.where({interviewed: false});
return new ResumeCollection(active);
},
filterInterviewed: function () {
var interviewed = this.where({interviewed: true});
return new ResumeCollection(interviewed);
},
render: function (filtered) {
var self = this;
var data = filtered ? filtered.toArray() : this.collection.toArray();
this.$el.html( $('#filter') );
_.each(data , function (cv) {
self.$el.append((new ResumeView({model: cv})).render().$el);
});
},
showActive: function () {
var filtered = this.collection.filterActive();
this.render(filtered);
},
showInterviewed: function () {
var filtered = this.collection.filterInterviewed();
this.render(filtered);
}
Your issue is that you are not returning the filtered collection.
This should work:
filterActive: function () {
var active = this.filter(function(item) {
return item.get('interviewed') === false;
});
return new ResumeCollection(active);
},
filterInterviewed: function () {
var interviewed = this.filter(function(item) {
return item.get('interviewed') === true;
});
return new ResumeCollection(interviewed);
},
i would suggest you to modify you render function to accept a argument which will be array of models.
Now when rendering full collection you can call render as
render(this.collection.models) // reference to list of models
also if you filter out the collection then the filter function most probably be returning the subset of models from collection. Which you can again pass to render function
this.render(this.showActive()) // showActive returns subset of models from collection
This way your render function becomes modular.. which accepts array and render then on page..
Now for Filtering out Collection you can use filter , where methods exposed by underscore .. Remember to capture the return and pass it along to render Function..

backbonejs filter data when passing parameter

I am trying this
try to filter a specific data.
In menu I have a lista I would like when I click in one i get only information about it.
HourView= Backbone.View.extend({
initialize: function(){
this.template = _.template( $("#HourView").html() );
},
render: function () {
var col = new HourCollection();
$.ajax({ //zeptojs
async: true
});
col.fetch({ success: function() {
}});
//col.fetch({reset: true});
$.ajax({ //zeptojs
async: false
});
col.where({"name": "Gamarra"});
this.$el.html(this.template({ horarios: col.toJSON() }));
return this;
}
});
[
{
"name": "Gamarra",
"VES": [
"00:00"
],
"GRAU": [
"01:00"
]
},
{
"name": "Grau",
"VES": [
"08:00"
],
"GRAU": [
"07:00"
]
}
]
I am trying this
initialize: function(){
this.collection = new HourCollection();
this.collection.fetch({reset: true});
this.collection.fetch({success: function(collection) {
collection = collection.where({"name": "Gamarra"});
console.log(JSON.stringify(collection))
}});
this.collection.on("sync", this.render, this);
this.template = _.template( $("#HourView").html() );
},
render: function () {
this.$el.html(this.template({ hs: this.collection.toJSON() }));
return this;
}
The method where returns an array (not collection) with the models, so you can use underscore to invoke toJSON method on each model so it will look like a toJSON but filtered.
this.$el.html(this.template({ hs: _.invoke(this.collection.where({"name": "Gamarra"}), 'toJSON') }));
The second way is just to use filter on JSON
this.$el.html(this.template({ hs: _.where(this.collection.toJSON(), {name: "Gamarra"}) }));
Third way is to use chain method on collection (btw it doesn't work on your data :( and I don't know why it returns an empty array)
this.collection.chain().where({ name: "Gamarra" }).value()

Backbone collection sortBy

I make my first backbone app and get some problems with collection sorting.
After using this
var SortedFriends = MyFriends.sortBy(function(friend) {
return friend.get("uid");
});
console.log(SortedFriends) show that SortedFriends contains sorted models, but when i try to use collection functions like 'SortedFriends.each' or 'SortedFriends.at' it make error:
TypeError: SortedFriends.each is not a function.
Code:
var Friend = Backbone.Model.extend({});
var Friends = Backbone.Collection.extend({
model: Friend,
});
var MyFriends = new Friends();
MyFriends.reset(<?=$friends?>);
var FriendView = Backbone.View.extend({
initialize: function(){
model:Friend
},
tagName: "tr",
template: _.template($('#item-template').html()),
className: "document-row",
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
var SortedFriends = MyFriends.sortBy(function(friend) {
return friend.get("uid");
});
var addOne = function(element){
var view = new FriendView({model: element});
$("#friends").append(view.render().el);
}
console.log(JSON.stringify(SortedFriends));
SortedFriends.each(function(friend){
var view = new FriendView({model: friend});
$("#friends").append(view.render().el);
});
If youre using backbone collections then youre probably better off using the comparator rather than collection methods
http://backbonejs.org/#Collection-comparator
When youre ready to sort your collection:
MyFriends.comparator = function(friend){
return friend.get("uid");
});
MyFriends.sort();
OR if you want to keep the order of the unsorted collection then you will need to clone it first
http://backbonejs.org/#Collection-clone
var SortedFriends = MyFriends.clone();
SortedFriends.comparator = function(friend){
return friend.get("uid");
});
SortedFriends.sort();
I'm not sure if it's a bug or a feature of Backbone's adaptation of sortBy, but apparently it returns an array, not an Underscore collection.
One workaround is to wrap the whole thing in _( ... ), which tells Underscore to wrap the array back into a collection:
var SortedFriends = _(MyFriends.sortBy(function(friend) {
return friend.get("uid");
}));
Edit
Most of the Underscore methods in Backbone seem to be chainable (replace sortBy with reject, for example, and it runs). Looking at the Backbone source where they wire up the Underscore proxies, it seems that sortBy is treated differently. I can't understand why they do it this way ...
var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
'isEmpty', 'chain'];
_.each(methods, function(method) {
Collection.prototype[method] = function() {
var args = slice.call(arguments);
args.unshift(this.models);
return _[method].apply(_, args);
};
});
var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
_.each(attributeMethods, function(method) {
Collection.prototype[method] = function(value, context) {
var iterator = _.isFunction(value) ? value : function(model) {
return model.get(value);
};
return _[method](this.models, iterator, context);
};

Backbone - Possible to get the collection from a model

I'm wondering if there's a way to get a reference to a collection from one of its models. For instance, if any of the people in the collection below are somehow aware of belonging to a collection, or multiple collections. Fiddle
(function() {
window.App = {
Models: {},
Views: {},
Collections: {}
};
App.Models.Person = Backbone.Model.extend({
defaults: {
name: 'John',
phone: '555-555-5555'
}
});
App.Views.Person = Backbone.View.extend({
tagName: 'li',
template: _.template("<%= name %> -- <%= phone %>"),
render: function(){
var template = this.template( this.model.toJSON() );
this.$el.html( template );
return this;
}
});
App.Collections.People = Backbone.Collection.extend({
model: App.Models.Person
});
App.Views.People = Backbone.View.extend({
tagName: 'ul',
add: function(person){
var personView = new App.Views.Person({ model: person });
this.$el.append( personView.render().el );
return this;
},
render: function() {
this.collection.each(this.add, this);
return this;
}
});
})();
var peeps = [ { name: 'Mary' }, { name: 'David' }, { name: 'Tiffany' } ];
var people = new App.Collections.People(peeps);
var peopleView = new App.Views.People({ collection: people });
peopleView.render().$el.appendTo('body');
Each model has a property called collection. In your fiddle, adding console.log(people.models[0].collection) will print out the collection.
Looking through the source code, it looks like this is what's used to do things like remove a model from a collection when the model's destroy() method is called.
Update: see this updated fiddle which creates three person models and two collections. It prints them to the console. It looks like model.collection only refers to the first collection the person was added to, not the second.

Why are my Backbone Models nested strangely within a Collection, requiring drilling down to access methods/properties?

I've got a Collection and a Model, both using attributes/options to augment them with additional capabilities. Here's the Model (LoadRouteGroup):
return Backbone.Model.extend({
initialize: function () {
console.log(this);
},
fetchf: function () {
console.log("FETCH");
}
});
And the Collection (LoadRouteGroups):
return Backbone.Collection.extend({
constructUrl: function(options) {
if (options.groupingType === "facility") {
// TODO: new endpoint: /api/v1/loadroutes?grouping=facility
this.url = clawConfig.endpoints.webApiRootUrl + "/api/loads/facilities";
}
else {
this.url = clawConfig.endpoints.webApiRootUrl + "/api/v1/loadroutes";
}
},
initialize: function (models, options) {
options || (options = {});
this.constructUrl(options);
console.log(this);
}
});
They're instantiated as such:
var loadRouteGroup = new LoadRouteGroup({
entityType: "facility"
});
// WORKS
loadRouteGroup.fetchf();
// assign groupingType option to collection to denote which URL to use
var loadRouteGroups = new LoadRouteGroups({
model: loadRouteGroup
}, {
groupingType: "facility"
});
var firstGroup = loadRouteGroups.at(0);
// DOESN'T WORK
firstGroup.fetchf();
// WORKS
firstGroup.attributes.model.fetchf();
I would expect that call to firstGroup.fetchf() to work... but it doesn't. Instead, I have to weirdly drill down and use firstGroup.attributes.model.fetchf() in order to access the method.
What's going on here? This would seem straightforward to me, but I can't for the life of me figure out what's wrong with the relationship between my Collection and Model.
The collection definition should include the model type:
return Backbone.Collection.extend({
// ....
model: LoadRouteGroup
});
When initializing the collection, pass in an array of models:
var loadRouteGroup = new LoadRouteGroup({
entityType: "facility"
});
var loadRouteGroups = new LoadRouteGroups([loadRouteGroup], {
groupingType: "facility"
});
Specify the model when you extend the collection instead of when you instantiate.

Categories

Resources