Backbone Collection Issue - backbone.js

I have a Backbone App with 3 model which have nested collections:
Models Structure:
Layout extends Backbone.Model
-> sections: new Sections extends Backbone.Collection
Section extends Backbone.Model
-> rows: new Rows extends Backbone.Collection
Now if I have two section model in layout and I go and add row model to one of the Section.rows collection, it is adding it to both sections.
BTW, I am adding it from a view on an event.
Thanks in Advance.

got a workaround. I could reproduce your workflow by adding defaults property to my models.
like this:
var Section = Backbone.Model.extend({
defaults: {
rows: new Rows
}
});
var Layout = Backbone.Model.extend({
defaults: {
sections: new Sections
}
});
then really, if i add new row to rows to one of my section its appear to adding to all sections rows collection. So i do this (rude example):
var Row = Backbone.Model.extend({
defaults: {
rowData: 0
}
});
var Rows = Backbone.Collection.extend({
model: Row
});
var Section = Backbone.Model.extend({
//defaults: {
// rows: new Rows
//}
});
var Sections = Backbone.Collection.extend({
model: Section
});
var Layout = Backbone.Model.extend({
//defaults: {
// sections: new Sections
//}
});
var LayoutView = Backbone.View.extend({
});
var lView = new LayoutView({ model: new Layout });
lView.model.set('sections',new Sections());
var sections = lView.model.get('sections');
sections.add({id: 1, name: 's1',rows: new Rows() });
sections.add({id: 2, name: 's2',rows: new Rows() })
var rows = sections.get(1).get('rows');
rows.add({id:'r1',rowsData: 10});
console.log(lView.model.toJSON());

#aleha you are right the issue is of the default attribute settings in Model. As they share the same memoery space(javascript: pass by reference not by value ).
So what I did is in the initialize function
initialize: function() {
this.set( 'rows', new Rows() );
}
So, no need to do it like you are doing above:
sections.add({id: 1, name: 's1',rows: new Rows() });
Hence resolved and automate :)
Thanks for the help though.

Related

Backbone fetch (or sync) doesn't look up right url

I'm trying to get some data from the node server, which works fine, but when I try to GET data via the Backbone fetch (or sync), the request fails. I noticed that, for some reason, actual request is wrong: 'GET http://localhost:3000/socket.io/1/' where it should be 'GET http://localhost:3000/cars', since '/cars' is the value of the URL field that Backbone uses by convention for these operations. These are the relevant modules:
var Backbone = require("backbone");
var Car = require('models/car');
var Cars = Backbone.Collection.extend ({
model: Car,
url: '/cars',
// Unselect all Car Cards
resetSelected: function() {
for (var i=1; i<=this.length; ++i) {
var carcard=this.get(i);
carcard.set({"selected": false});
console.log(carcard.attributes.name + ' unselected');
}
},
// Select a specific model from the collection
selectByID: function(id) {
this.resetSelected();
var carcard = this.get(id);
carcard.set({"selected": true});
console.log(carcard.attributes.name + " selected");
return carcard.attributes.id;
}
});
module.exports = Cars;
And a model:
var Backbone = require("backbone");
var Car = Backbone.Model.extend({
defaults: {
year: 2011,
brand: "Brand",
model: "Model",
name: "Car Name",
pictureFle: "img/car.jpg",
kmTraveled: 0,
litresSpent: 0,
selected: false
},
});
module.exports = Car;
I tried to populate the collection like this:
var cars = new Cars();
cars.fetch();
but, as I explained, failed. Any ideas what the problem could be?

BackboneJS How to merge collections

I asked this before BackboneJS Display multiple collections in one <ul>-Element but I can't get it to work and I'm starting getting really desperate so
how can I merge these 3 collections and display them in the same <ul>?
define(['app','backbone','modules/artistInstagram', 'modules/artistTwitter',
'modules/artistFacebook'
], function (App, Backbone, ArtistInstagram, ArtistTwitter, ArtistFacebook) {
var ArtistSocialMedia = App.module();
ArtistSocialMedia.View = Backbone.View.extend({
tagName: 'ul',
id: 'suptiles',
beforeRender: function(){
var artistinstagramCollection = new ArtistInstagram.ArtistInstagramCollection();
artistinstagramCollection.artist_id = this.artist_id;
this.insertView('.socialMedia', new ArtistInstagram.View({collection: artistinstagramCollection}));
artistinstagramCollection.fetch();
var artisttwitterCollection = new ArtistTwitter.ArtistTwitterCollection();
artisttwitterCollection.artist_id = this.artist_id;
this.insertView('.socialMedia', new ArtistTwitter.View({collection: artisttwitterCollection}));
artisttwitterCollection.fetch();
var artistfacebookCollection = new ArtistFacebook.ArtistFacebookCollection();
artistfacebookCollection.artist_id = this.artist_id;
this.insertView('.socialMedia', new ArtistFacebook.View({collection: artistfacebookCollection}));
artistfacebookCollection.fetch();
}
});
return ArtistSocialMedia;
});
Right now, it clearly creates 3 views but I want to merge them into one collection. Please help!
Thanks in advance...
Don't overthink it - since you're defining an element with dynamic content, it should be its own View. It's an unordered list, so the tag name must be <ul>. All you're doing is filling in the <li>'s, so the template isn't very complicated.
var collection1 = new WhateverCollection();
var collection2 = new WhateverCollection();
var collection3 = new WhateverCollection();
var ListView = Backbone.View.extend({
tagName: 'ul',
render: function(){
// define template
var templateStr = '<% _.each(collection,function(model){ %>\
<li><%- model.name %></li>\
<% }); %>';
// convert to function
var template = _.template(templateStr);
// for learning purposes, render each one individually
var htmlFromFirst = template({ collection: collection1.toJSON() });
var htmlFromSecond = template({ collection: collection2.toJSON() });
var htmlFromThird = template({ collection: collection3.toJSON() });
// set the html
this.$el.html( htmlFromFirst + htmlFromSecond + htmlFromThird );
return this;
}
});

Backbone inherit model2 attribute from model1 attribute

I have a models and collection
Model1 = Backbone.Model.extend();
Model2 = Backbone.Model.extend({
defaults: {
code:'',
m1_id: ?????, // this part should get the Model1 "id" attribute
id: '' // e.g. the value of m1.get('id');
}
});
C = Backbone.Collection.extend({
model: Model2
});
and make an instance of each
var m1 = new Model1();
var m2 = new Model2();
var c = new C();
and set the values
m1.set({'code':'0001', 'type': 'O', id: 1});
c.add({'code':'sample1', 'id':1}); // note: i did not set the m1_id
c.add({'code':'sample2', 'id':2});
but the model inside collection get the Model1 id attrtibute, something like
the Collection must have this
c.at(0).toJSON();
-> {'code':'sample1', 'm1_id': 1, id: 1} // note: the "m1_id" value is
c.at(1).toJSON(); // from Model1 "id" attribute
-> {'code':'sample2', 'm1_id': 1, id: 2}
How i can automatically set the Model2 attribute inside the Collection from the Model1 attribute.. Thanks!
Firstly, you have some problem with your code:
var m1, m2, and c should be called with the keyword: new to instantiate your models and collections
e.g. var m1 = new Model1()
your code to add to your collection (c.add) is also missing the closing curly brace
c.add({'code':'sample1'); // should be c.add({code:"sample1"});
Your code and question isn't completely clear to me, but I suspect you may be trying to add a model to your collection with the same id perhaps. Multiple models of the same id won't get added to your collection, as per the backbone documentation:
Note that adding the same model (a model with the same id) to a
collection more than once is a no-op.
If you need to to pass the id from another model, you'd need to set another attribute like you have "parent_id" on passing it to your collection.
e.g
var temp_id = m1.get('id');
c.add({code:"sample3", id:temp_id});
Model1 = Backbone.Model.extend();
var m1 = new Model1();
Model2 = Backbone.Model.extend({
defaults: {
code:'',
m1_id: '',
id: ''
}
});
var m2 = new Model2();
C = Backbone.Collection.extend({
model: Model2,
initialize: function(){
this.on('add', this.onAddModel, this);
},
onAddModel: function(model){
model.set({'m1_id': m1.get('id')});
}
});
var c = new C();
m1.set({'code':'0001', 'type': 'O', id: 1});
c.add({'code':'sample1', 'id':1}); // trigger the c.onAddModel function
c.at(0).toJSON();
-> {'code':'sample1', 'm1_id': 1, id: 1}

How to make selected item to change class(How to make property observable in backbone)

i want to build simple screen, where list of items in one side and selected item details -on another. when user click on one of the items - its details displayed in 'details' section.
also the selected item in the 'list' section must be decorated with 'active' class.
here is my router code:
var AppRouter = Backbone.Router.extend({
routes:{
"":"list",
"users/:id":"userDetails"
},
list:function () {
this.usersList = new UsersCollection(usersList);/* new UsersCollection()*/
var self = this;
//this.userList.fetch({
// success: function () {
this.UsersListView = new UsersListView({ model: this.usersList });
$('#sidebar').html(this.UsersListView.render().el);
// }
//})//end of fetch
},
userDetails:function (id) {
if(this.usersList){
//unselect prevously selected
if(this.user )this.user.set({'selected':false});
this.user = this.usersList.get(id);
//select current
this.user.set({'selected':true});
//empty refill the items section
this.UsersListView = new UsersListView({ model: this.usersList });
$('#sidebar').empty().html(this.UsersListView.render().el);
if (this.UserDetailsView) this.UserDetailsView.close();
this.UserDetailsView = new UserDetailsView({model:this.user});
$('#content').html(this.UserDetailsView.render().el);
}
else{
}
}
});
So far i managed to set the 'active' item class by emptying and refill the items section html.
Is there any way to observe (like in knockoutjs) the 'selected' property, so once it changes -the change will be visible in html?
code of view:
window.UserListItemView = Backbone.View.extend({
tagName:"li",
template:_.template($('#tpl-user-list-item').html()),
render:function (eventName) {
if(this.model.get('selected')){$(this.el).addClass('active');}
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Thanks forwards
This is what you're looking for (especially the Events#listenTo method).
So; in your view:
initialize: function() {
// listen to your model
this.listenTo(this.model, 'change:selected', this.updateClass);
},
updateClass: function() {
// something like...
this.$el.toggleClass('active');
}

Filter backbone collection by attribute value

I have a defined model and a collection:
var Box = Backbone.Model.extend({
defaults: {
x: 0,
y: 0,
w: 1,
h: 1,
color: "black"
}
});
var Boxes = Backbone.Collection.extend({
model: Box
});
When the collection is populated with the models, I need a new Boxes collection made out of Box models that have a specific color attribute contained in the complete collection, I do it this way:
var sorted = boxes.groupBy(function(box) {
return box.get("color");
});
var red_boxes = _.first(_.values(_.pick(sorted, "red")));
var red_collection = new Boxes;
red_boxes.each(function(box){
red_collection.add(box);
});
console.log(red_collection);
This works, but I find it a bit complicated and unefficient. Is there a way of doing this same thing in a more simple way?
Here is the code I described: http://jsfiddle.net/HB88W/1/
I like returning a new instance of the collection. This makes these filtering methods chainable (boxes.byColor("red").bySize("L"), for example).
var Boxes = Backbone.Collection.extend({
model: Box,
byColor: function (color) {
filtered = this.filter(function (box) {
return box.get("color") === color;
});
return new Boxes(filtered);
}
});
var red_boxes = boxes.byColor("red")
See http://backbonejs.org/#Collection-where
var red_boxes = boxes.where({color: "red"});
var red_collection = new Boxes(red_boxes);

Resources