Modifying backbone collection - backbone.js

I have a collection which has collection of models. I have to get each model and set some more properties to that before submitting to the server.
how to do this using backbone.?
UPDATE :
this is what i tried to print the model if it prints wanted to try with set property of model but it was giving me uncaught typeerror cannot call method 'each' of undefined:
covertInvestmentJournal:function(){
this.investmentTransactionsCollection.each(function(model){
console.log(model);
});
}

This is how i resolved my problem :
for (var i = 0, l = investmentTransactionsCollection.length; i < l; i++) {
investmentTransactionsCollection.models[i].set({securityName:$('#security-name').val()});
investmentTransactionsCollection.models[i].set({valueDate:DateUtils.formatAsYMD($('#value-date'))});
investmentTransactionsCollection.models[i].set({adjustmentType:$('#adjustment-type').val()});
investmentTransactionsCollection.models[i].set({assetClass:$('#assetclass').val()});
investmentTransactionsCollection.models[i].set({currency:$('#currency').val()});
}

Related

Cannot read property 'length' of undefined error when passing collection of models to a CollectionView in Marionette

I am working in Backbone/Marionette and have a Collection, where I am parsing data, and creating a collection of models, which I then pass to a CollectionView, and a childView is set for that CollectionView.
The collection successfully makes it into the CollectionView, but it seems when my app attempts to display the childView, I get an Uncaught TypeError: Cannot read property 'length' of undefined
The error gets thrown in marionette here:
_filteredSortedModels: function(addedAt) {
var viewComparator = this.getViewComparator();
var models = this.collection.models;
addedAt = Math.min(Math.max(addedAt, 0), models.length - 1);
it appears as though "models" is undefined here.
My app is set up this way:
DrawerCollection:
App.module('Home.Drawer.Collections', function (Collections, App, Backbone, Marionette) {
model: App.Home.Drawer.Models.DrawerModel,
...make a request, parse some data...
responseData.push( new App.Home.Drawer.Models.DrawerModel({
...model properties...
});
var DrawerLayoutCollection = new App.Home.DrawerCollectionView({collection: responseData});
App.mainContainer.show(shelfLayoutCollection);
DrawerView:
App.module('Home', function(Home, App, Backbone, Marionette) {
Home.DrawerComponent = Backbone.Marionette.LayoutView.extend({
...my template, events, ui, etc....
});
});
Collection View (which has the DrawerView set to be its childView):
Home.DrawerCollectionView = Backbone.Marionette.CollectionView.extend({
childView: Home.DrawerComponent
});
Any input would be much appreciated!!!
Few things seem wrong with this line:
var DrawerLayoutCollection = new App.Home.DrawerCollectionView({collection: responseData});
First, DrawerLayoutCollection is not a constructor, but an instance.
Second, it's called a collection but it's actually a view instance.
You are passing responseData as a collection to the collection view instead of an actual backbone collection.
It should be something like
var drawerLayoutCollectionView = new App.Home.DrawerCollectionView({collection: new Backbone.Collection(responseData)});

backbone.js set in model initialize not effecting models in collection

While performing a fetch() on my backbone collection, and instantiating models as children of that collection, I want to add one more piece of information to each model.
I thought that I could do this using set in the model initialize. (My assumption is that fetch() is instantiating a new model for each object passed into it. And therefore as each initialize occurs the extra piece of data would be set.
To illustrate my problem I've pasted in four snippets, first from my collection class. Second the initialize function in my model class. Third, two functions that I use in the initialize function to get the needed information from the flickr api. Fourth, and finally, the app.js which performs the fetch().
First the collection class:
var ArmorApp = ArmorApp || {};
ArmorApp.ArmorCollection = Backbone.Collection.extend({
model: ArmorApp.singleArmor,
url: "https://spreadsheets.google.com/feeds/list/1SjHIBLTFb1XrlrpHxZ4SLE9lEJf4NyDVnKnbVejlL4w/1/public/values?alt=json",
//comparator: "Century",
parse: function(data){
var armorarray = [];
var entryarray = data.feed.entry;
for (var x in entryarray){
armorarray.push({"id": entryarray[x].gsx$id.$t,
"Filename": entryarray[x].gsx$filename.$t,
"Century": entryarray[x].gsx$century.$t,
"Date": entryarray[x].gsx$date.$t,
"Country": entryarray[x].gsx$country.$t,
"City": entryarray[x].gsx$city.$t,
"Type": entryarray[x].gsx$type.$t,
"Maker": entryarray[x].gsx$maker.$t,
"Recepient": entryarray[x].gsx$recipient.$t,
"Flickrid": entryarray[x].gsx$flickrid.$t,
"FlickrUrl": "", //entryarray[x].gsx$flickrurl.$t,
"FlickrUrlBig": ""//entryarray[x].gsx$flickrurlbig.$t,
});
}
return armorarray;
}
});
Second, the initialization in my model.
initialize: function(){
//console.log("A model instance named " + this.get("Filename"));
item = this;
var flickrapi = "https://api.flickr.com/services/rest/?&method=flickr.photos.getSizes&api_key=<my_apikey>&photo_id=" + this.get("Flickrid") + "&format=json&jsoncallback=?";
sources = getFlickrSources(flickrapi);
sources.then(function(data){
sourceArray = parseFlickrResponse(data);
FlickrSmall = sourceArray[0].FlickrSmall;
console.log (FlickrSmall);
item.set("FlickrUrl", FlickrSmall);
console.log(item);
});
Notice here how I'm getting the "Flickrid" and using to get one more piece of information and then trying to add it back into the model with item.set("FlickrUrl", FlickerSmall);
console.log confirms that the property "FlickrUrl" has been set to the desired value.
Third, these are the functions my model uses to get the information it needs for the flicker api.
var getFlickrSources = function(flickrapi){
flickrResponse = $.ajax({
url: flickrapi,
// The name of the callback parameter, as specified by the YQL service
jsonp: "callback",
// Tell jQuery we're expecting JSONP
dataType: "jsonp"})
return flickrResponse;
}
var parseFlickrResponse = function(data){
flickrSourceArray = []
if (data.stat == "ok"){
sizeArray = data.sizes.size;
for (var y in sizeArray){
if (sizeArray[y].label == "Small"){
flickrSourceArray.push({"FlickrSmall": sizeArray[y].source});
}
else if (sizeArray[y].label == "Large"){
flickrSourceArray.push({"FlickrLarge": sizeArray[y].source});
}
}
}
return flickrSourceArray
}
But, fourth, when I try to perform the fetch and render the collection, I only get objects in my collection without the FlickrUrl property set.
//create an array of models and then pass them in collection creation method
var armorGroup = new ArmorApp.ArmorCollection();
armorGroup.fetch().then(function(){
console.log(armorGroup.toJSON());
var armorGroupView = new ArmorApp.allArmorView({collection: armorGroup});
$("#allArmor").html(armorGroupView.render().el);
});
var armorRouter = new ArmorApp.Router();
Backbone.history.start();
The console.log in this last snippet prints out all the objects/models supposedly instantiated through the fetch. But none of them include the extra property that should have been set during the initialization.
Any ideas what is happening?
What is this function ? getFlickrSources(flickrapi)
Why are you using this.get in the initialize function. Honestly it looks over-complicated for what you are trying to do.
If you want to set some parameter when you instantiate your model then do this var model = new Model({ param:"someparam", url:"someurl",wtv:"somewtv"});
If the point is to update your model just write an update function in your model something like update: function (newparam) { this.set;... etc and call it when you need it.
If I read you well you just want to set some params when your model is instantiated, so just use what I specified above. Here is some more doc : http://backbonejs.org/#Model-constructor
I hope it helps.
edit:
Put your call outside your model, you shouldn't (imo) make call inside your model this way it seems kinda dirty.
Sources.then(function(flickrdata) {
var mymodel = new Model({flicker:flickrdata.wtv});
});
It would be cleaner in my opinion.

Nested backbone model results in infinite recursion when saving

This problem just seemed to appear while I updated to Backbone 1.1. I have a nested Backbone model:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""},
parse: function (response) {
response.name = response.set_id;
response.problems = new ProblemList(response.problems);
return response;
}
});
var ProblemList = Backbone.Collection.extend({
model: Problem
});
I initially load in a ProblemSetList, which is a collection of ProblemSet models in my page. Any changes to the open_date or due_date fields of any ProblemSet, first go to the server and update that property, then returns. This fires another change event on the ProblemSet.
It appears that all subsequent returns from the server fires another change event and the changed attribute is the "problems" attribute. This results in infinite recursive calls.
The problem appears to come from the part of set method of Backbone.Model (code listed here from line 339)
// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
if (!_.isEqual(current[attr], val)) changes.push(attr);
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
unset ? delete current[attr] : current[attr] = val;
}
// Trigger all relevant attribute changes.
if (!silent) {
if (changes.length) this._pending = true;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
The comparison on the problems attribute returns false from _.isEqual() and therefore fires a change event.
My question is: is this the right way to do a nested Backbone model? I had something similar working in Backbone 1.1. Other thoughts about how to proceed to avoid this issue?
You reinstantiate your problems attribute each time your model.fetch completes, the objects are different and thus trigger a new cycle.
What I usually do to handle nested models:
use a model property outside of the attributes handled by Backbone,
instantiate it in the initialize function,
set or reset this object in the parent parse function and return a response omitting the set data
Something like this:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""
},
initialize: function (opts) {
var pbs = (opts && opts.problems) ? opts.problems : [];
this.problems = new ProblemList(pbs);
},
parse: function (response) {
response.name = response.set_id;
if (response.problems)
this.problems.set(response.problems);
return _.omit(response, 'problems');
}
});
parse gets called on fetch and save (according to backbone documentation), this might cause your infinite loop. I don't think that the parse function is the right place to create the new ProblemsList sub-collection, do it in the initialize function of your model instead.

_.bindAll(this) and Uncaught TypeError: Cannot read property 'idAttribute' of undefined in backbone-relation.js

I have two models (User and Task) which are instances of Backbone.RelationalModel.
The relation about these two models is the following:
// Task model
var Task = Backbone.RelationalModel.extend({
relations: [
{
type: 'HasOne',
key: 'user',
relatedModel: User
}
],
urlRoot: 'someUrl'
});
Then I have one collection which code looks like this:
var FollowerCollection = Backbone.Collection.extend({
initialize: function () {
_.bindAll(this);
}
model: User
});
var User = Backbone.RelationalModel.extend({
});
When I make a fetch on FollowerCollection I get the following error:
Uncaught TypeError: Cannot read property 'idAttribute' of undefined
on the line 1565 of backbone-relation.js of backbone-relation version 0.5.0
Here a piece of code of backbone-relation.js
if ( !( model instanceof Backbone.Model ) ) {
// Try to find 'model' in Backbone.store. If it already exists, set the new properties on it.
var existingModel = Backbone.Relational.store.find( this.model, model[ this.model.prototype.idAttribute ] );
The problem is related to _.bindAll(this) because if I comment it, it works properly.
Why? Any ideas?
Removing the _.bindAll does work.
It's a shame, because it's a really handy function. It must interact with some part of Backbone badly. I'm on v9.10
I use this method all the time, and issues only come up sometimes (like when you want to do a bulk add to a collection).
For me, The problem was in this Backbone.js method:
// Get a model from the set by id.
get: function(obj) {
if (obj == null) return void 0;
this._idAttr || (this._idAttr = this.model.prototype.idAttribute);
return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj];
},
The code fails at this.model.prototype because prototype is undefined. What? Ya. For reals.
The problem is that when _.bindAll is called, it binds all properties of the collection, as #jakee says. This seems to include Collection.model, which is a bug I think.
The solution is to bind individual methods until this is fixed.
There's an existing, but closed issue on github: https://github.com/documentcloud/backbone/issues/2080
Seems like the current maintainers don't like the method, but I don't understand why.
Like my project is really big I had to create my custom bindAll. Here you have the code, it works with the lastest versions.
I bind all the properties of the instance "this" except the ones that has prototype with properties, like this.model in a Collection
https://gist.github.com/patrixd/8025952
//bindAll from underscore that allows 1 argument to bind all the functions from the prototype,
//or if there are more arguments they will be the only binded
_.originalBindAll = _.bindAll;
_.bindAll = function (that) {
var funcs = Array.prototype.slice.call(arguments, 1),
validKeys = [], fn;
if (funcs.length == 0) {
for (var i in that) {
fn = that[i];
if (fn && typeof fn == "function" && (!fn.prototype ||
_.keys(fn.prototype).length == 0))
validKeys.push(i);
}
_.originalBindAll.apply(_, [that].concat(validKeys));
}
else
_.originalBindAll.apply(_, arguments);
};

Backbone collection remove, ReferenceError: el is not defined

I have a view for a collection, and when I invoke its remove method I call its collection remove method as well, and I'm getting a 'ReferenceError: el is not defined' which doesn't make any sense to me, why would a collection need an el.
Invoking code:
try {
myAppModel=backboneApp.views.privateViews.myAppsTabView.myAppsView.views.myAppsPrivateView.collection.get(appId);
backboneApp.views.privateViews.myAppsTabView.myAppsView.views.myAppsPrivateView.remove(myAppModel);
} catch(e) {
console.log("delFromMyAppsCollection: Failed to delete app from collection e= " + e);
}
Remove method within View:
remove : function(modelToRemove) {
alert('Killing!');
console.log("MyAppsPrivateView.remove called with model: ", modelToRemove );
this.collection.remove(modelToRemove);
console.log("MyAppsPrivateView.remove collection: ", this.collection );
this._rendered = false;
}
I guess it may be a better way to delete an element from a collection/view, but still it seems odd that the collection is complaining about not having an el, any ideas?
Thanks in advance.
Just in case,
view definition:
var MyAppsPrivateView = Backbone.View.extend( {
// Reference to this collection's model.
model: PapsCatalog , // don't should be PapModel instead of a collection?
templateId: Epc2G.myAppsTemplateId,
template: jQuery('#' + this.templateId).html(),
view instantiation:
var options = {
className : "MyAppsContainer",
uid : "myAppsPrivateView",
collection : papsCollection,
el : "#myAppsView"
};
var oMyAppsPrivateView = new MyAppsPrivateView(_.clone(options));
Might it relate to Backbone.View already having a remove method, and you’re overriding it?
This sounds like a composite view situation, have you considered having a view for every model in the collection?

Resources