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()
Related
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..
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);
});
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);
};
I am having trouble looping through a collection that helps populate a view with data. When I try to loop through the collection and get the following error,
Uncaught TypeError: Object # has no method 'each'
I have absolutley no idea why I am getting this error, other than the object obviosly doesn't have that method, however I only get this error when I run the drop function see code below.
Here is by backbone code,
GroupListView.prototype.keyup = function() {
this.filtered = this.collection.searchName(this.$el.find("#search-query").val());
return this.renderList(this.filtered);
};
GroupListView.prototype.renderList = function(collection) {
var $collection, $this;
console.log(this.$el);
console.log(collection);
if (!collection) {
collection = this.collection;
console.log(collection);
}
this.$el.find(".list-items").empty();
$this = this.$el.find("#people-groups-list");
$collection = collection;
if ($collection.length < 1) {
this.$el.find("#people-groups-list").hide();
return this.$el.find("#people-groups-list").after('<div class="activity-no-show">\
<p>To add a new group, click the + in the top right hand corner to get started.</p>\
</div>');
} else {
this.$el.find("#people-groups-list").show();
collection.each(function(item) {
//console.log(item);
var displayView;
displayView = new app.GroupListDisplayView({
model: item,
collection: $collection
});
return $this.append(displayView.render());
});
return this;
}
};
GroupListDisplayView.prototype.render = function() {
var $body;
//console.log(this.model.toJSON());
this.$el.html(this.template({
m: this.model.toJSON()
}));
$body = this.$el.find(".card-body");
$.each(this.model.get("people"), function(i, person) {
var personTile;
this.person = new app.Person({
id: person.id,
avatar: person.avatar,
first_name: person.first_name,
last_name: person.last_name
});
console.log(this.person);
personTile = new app.PersonTileView({
model: this.person
});
return $body.html(personTile.render());
});
return this.$el.attr("id", "group-card-" + this.model.id);
};
GroupListDisplayView.prototype.drop = function(e) {
var $collection, $model, person_id, request;
e.preventDefault();
$collection = this.collection;
person_id = e.originalEvent.dataTransfer.getData('Text');
request = new app.PersonGroupAdd;
$model = this.model;
return request.save({
async: true,
wait: true,
person_id: person_id,
group_id: this.model.get("id")
}, {
success: function(d) {
return $model.fetch({
async: true,
wait: true
});
}
});
};
GroupListView.prototype.initialize = function() {
this.collection.on("change", this.renderList, this);
this.collection.on("reset", this.render, this);
return this.renderList();
};
Try this instead, place this function with in your collection
parse: function (data) {
data.forEach(function (item) {
displayView = new app.GroupListDisplayView({
model: item,
collection: $collection
});
});
}
Hope this helps.
I'm not sure what the drop function has to do with it, but I see renderList being passed the results of searchName during keyup. What's likely happening is that searchName is returning a regular array, instead of a wrapped object that would have the each method (i.e. a Backbone.Collection, jQuery collection or Underscore wrapped object).
Instead of calling collection.each, use jQuery or Underscore:
$.each(collection, function(i, item) { ... });
or
_.each(collection, function(item, i, list) { ... });
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.