Backbone - Possible to get the collection from a model - javascript

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.

Related

Backbone collection inside model

I'm new to backbone and I wonder if there is a way to save previous models in a collection as an attribute of the model itself. For example,
var history = Backbone.Collection.extend({});
var myModel = Backbone.Model.extend({
defaults: {
id: '',
name: '',
history: history //history is a collection of myModel
},
//override setter so when set method is called, it will save the previous model inside history collection.
})
This would be ephemeral history
var myModel = Backbone.Model.extend({
defaults:{
id:''
},
constructor: function(){
this.history = new Backbone.Collection();
},
set: function(){
var args = Array.prototype.slice.call(arguments);
this.history.add(this.toJSON());
return Backbone.Model.prototype.set.apply(this, args);
}
});

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 View each

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);
});

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);
};

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