I am new to Backbone, and am using it with Backgrid to display a large amount of data. The data represents two lists of Ingredients: one with existing and one with updated values. There are no primary keys for this data in the DB so the goal is to be able to match the old and new Ingredients manually by name and then generate a DB update from the matched data. To do this I have three collections: ingredientsOld
(database), ingredientsNew
(update.xml), and ingredients
. The ingredientsOld
and ingredientsNew
collections are just collections of the basic Ingredient
model. The ingredients
collection, however, is a collection of IngredientComp
models which contain an integer status, an 'old' Ingredient
, and a 'new' Ingredient
.
var Ingredient = Backbone.Model.extend({});
var Ingredients = Backbone.Collection.extend({ model: Ingredient });
var IngredientComp = Backbone.Model.extend({
constructor: function(attributes, options) {
Backbone.Model.apply( this, arguments );
if (attributes.o instanceof Ingredient) {
this.o = attributes.o;
console.log("Adding existing ingredient: "+this.o.cid);
} else {
this.o = new Ingredient(attributes.o);
console.log("Adding new ingredient: "+this.o.get("name"));
}
if (attributes.n instanceof Ingredient) {
this.n = attributes.n;
} else {
this.n = new Ingredient(attributes.n);
}
}
});
var IngredientComps = Backbone.Collection.extend({
model: IngredientComp,
comparator: function(comp){
return -comp.get("status");
}
});
var ingredientsOld = new Ingredients();
var ingredientsNew = new Ingredients();
var ingredients = new IngredientComps();
The data is being generated by PHP and outputted to JSON like so:
ingredientsOld.add([
{"name":"Milk, whole, 3.25%","guid":"3BDA78C1-69C1-4582-83F8-5A9D00E58B45","item_id":16554,"age":"old","cals":"37","fat_cals":"18","protein":"2","carbs":"3","fiber":"0","sugar":"3","fat":"2","sat_fat":"1","trans_fat":"0","chol":"6","sod":"24","weight":"2.00","quantity":"1 each","parents":{"CC09EB05-4827-416E-995A-EBD62F0D0B4A":"Baileys Irish Cream Shake"}}, ...
ingredients.add([
{"status":3,"o":{"name":"Sliced Frozen Strawberries","guid":"A063D161-A876-4036-ADB0-C5C35BD9E5D5","item_id":16538,"age":"old","cals":"77","fat_cals":"0","protein":"1","carbs":"19","fiber":"1","sugar":"19","fat":"0","sat_fat":"0","trans_fat":"0","chol":"0","sod":"0","weight":"69.60","quantity":"1 each","parents":{"BC262BEE-CED5-4AB3-A207-D1A04E5BF5C7":"Lemonade"}},"n":{"name":"Frozen Strawberries","guid":"5090A352-74B4-42DB-8206-3FD7A7CF9D56","item_id":"","age":"new","cals":"77","fat_cals":"0","protein":"1","carbs":"19","fiber":"1","sugar":"19","fat":"0","sat_fat":"0","trans_fat":"0","chol":"0","sod":"0","weight":"","quantity":"69.60 Gram","parents":{"237D1B3D-7871-4C05-A788-38C0AAC04A71":"Malt, Strawberry"}}}, ...
When I display values from the IngredientComp model (from the render function of a custom Backgrid Cell), I initially have to output them like this:
render: function() {
col = this.column.get("name");
var v1 = this.model.get("o")[col];
var v2 = this.model.get("n")[col];
this.$el.html( v1 + "\n<br />\n<b>" + v2 + "</b>" );
return this;
}
It is only after moving the IngredientComp models from one collection to another that the this.model.get("o").get(col);
function works. Here is the function that moves the Ingredients from one collection to another:
function matchItems(oldId, newId) {
var oldItem = ingredientsOld.remove(oldId);
var newItem = ingredientsNew.remove(newId);
ingredients.add({'status': 1, 'o': oldItem, 'n': newItem});
}
I have updated the render function to try both methods of retrieving the value, but it is a bit slower and certainly not the proper way of handling the problem:
render: function() {
col = this.column.get("name");
// Investigate why the Ingredient model's get() method isn't available initially
var v1 = this.model.get("o")[col];
// The above line returns 'undefined' if the Ingredient model has moved from one
// collection to another, so we have to do this:
if (typeof v1 === "undefined"){ v1 = this.model.get("o").get(col)};
var v2 = this.model.get("n")[col];
if (typeof v2 === "undefined"){ v2 = this.model.get("n").get(col)};
this.$el.html( v1 + "\n<br />\n<b>" + v2 + "</b>" );
return this;
}
Can anyone shed some light on what might be causing this problem? I have done a bit of research on Backbone-relational.js, but it seems like a lot of overkill for what I am trying to accomplish.
I would first recommend using initialize
instead of constructor
, because the constructor function overrides and delays the creation of the model.
The main issue thought is that model.get('o') returns something different in this if statement. by doing this.o
it is not setting the attribute on the model, but instead setting it on the model object. Therefore when the model is actually created model.get('o')
is a regular object and not a backbone model.
if (attributes.o instanceof Ingredient) {
this.o = attributes.o;
console.log("Adding existing ingredient: "+this.o.cid);
} else {
this.o = new Ingredient(attributes.o);
console.log("Adding new ingredient: "+this.o.get("name"));
}
Changing the if statement to the following should solve the issue.
if (attributes.o instanceof Ingredient) {
this.o = attributes.o;
console.log("Adding existing ingredient: "+this.o.cid);
} else {
this.set('0', new Ingredient(attributes.o));
console.log("Adding new ingredient: "+this.o.get("name"));
}