Reputation: 10679
Right off the bat: this is my first Backbone app so if there are any design flaws I'm all ears. The purpose of this app is to draw/re-draw a bar graph based upon changing input values.
Input values are stored in the SliderModel
. Each DataPoint
model in the DataSeries
collection is supposed to reprent one column of data that gets drawn in the BarGraph
view.
When the user changes values in the SliderModel
, I've tied and event handler method in the BarGraph
so that I can dynamically re-draw the graph according to the input.
When the page loads, the updateCalculations
method in the BarGraph
view is run. The calculations are processed correctly and the graph is drawn appropriately (all zeros).
However, once the user starts entering input (i.e. one single character) the other
variable below (from updateCalculations
method in the view) is evaluated as undefined
var other = this.collection;
For some reason the collection
in the BarGraph
view is being cleared out when the updateCalculations
method is called via a change to the SliderModel
properties.
Model
Model containing default values and calculation methods
var SliderModel = Backbone.Model.extend({
initialize: function() {
},
defaults: {
purchasePayment: '0',
fixedRate: '0',
returnSpx: '0',
slidervalue: '0'
},
fixedAllocation: function () {
return this.attributes.purchasePayment * (1 - (this.attributes.slidervalue / 100));
},
illustratedEoy: function() {
return this.fixedAllocation() * Math.pow(1 + (this.attributes.fixedRate / 100), 7);
},
eoyContractVal: function (value) {
return this.illustratedEoy() + parseFloat(value);
}
});
Model/Collection
Collection and model type of collection
var DataPoint = Backbone.Model.extend({
initialize: function (lbl, ctrct, rtrn) {
this.set({
label: lbl,
contract: ctrct,
annReturn: rtrn
})
},
});
var DataSeries = Backbone.Collection.extend({
model: DataPoint,
fetch: function () {
this.reset();
this.add([
new DataPoint("1/7yrs", "111830.17", "1.63%"),
new DataPoint("2/7yrs", "115311.17", "2.07%"),
new DataPoint("3/7yrs", "118984.65", "2.52%"),
new DataPoint("4/7yrs", "122859.65", "2.98%"),
new DataPoint("5/7yrs", "126947.77", "3.46%"),
new DataPoint("6/7yrs", "131260.74", "3.94%"),
new DataPoint("7/7yrs", "135810.92", "4.44%")
])
}
});
View
The SliderModel
is the model assigned to this view. The DataSeries
is the collection assigned to the view.
var BarGraph = Backbone.View.extend({
"el": "#graph",
options: {barDemo: ""},
initialize: function (options) {
_.bindAll(this, "render");
this.collection.bind("change", this.updateCollection);
//Bind model change event to view event handler
this.model.bind('change:purchasePayment', this.updateCollection);
this.model.bind('change:slidervalue', this.updateCollection);
this.model.bind('change:purchasePayment', this.updateCollection);
this.model.bind('change:slidervalue', this.updateCollection);
this.model.bind('change:fixedRate', this.updateCollection);
this.model.bind('change:returnSpx', this.updateCollection);
//Run setup methods
this.drawGraph();
this.collection.fetch();
this.updateCollection();
},
drawGraph: function() {
var margin = { top: 20, right: 20, bottom: 20, left: 20 };
this.options.barDemo = d3.selectAll($(this.el)).append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + 20);
},
updateCollection: function () {
var sum = 0.00;
var that = this.model;
//Collection shows as 'undefined' when this method is fired via change event
var other = this.collection;
var indexed = $.makeArray($('#IndexedTable').find('tbody tr'));
var i = 0;
this.collection.each(function (m) {
var value = that.eoyContractVal(that.indexedIllustrated(i));
sum = parseFloat(sum) + parseFloat(value);
other.models[i].attributes.contract = value.toFixed(2);
other.models[i].attributes.annReturn = that.annReturn(value).toFixed(2) + '%';
i++;
});
this.render();
},
render: function () {
},
});
jQuery
How the above code is initialized
var sliderModel = new SliderModel;
var dataSeries = new DataSeries();
new BarGraph({
collection: dataSeries,
model: sliderModel
});
Upvotes: 0
Views: 340
Reputation: 4163
first of all it's a bad bad idea overriding original Collection methods like fetch. You don't need that.
If you want to add some test data without going to the server, use the reset
method or add
like you did, outside of the collection definition.
http://backbonejs.org/#Collection-reset
I would leave the collection with just the model
attribute. In your "main" (your jQuery ready handler), fill the data:
var slider = new SliderModel();
var dataSeries = new DataSeries();
var view = new BarGraph({
model: slider,
collection: dataSeries
});
dataSeries.reset([
new DataPoint("1/7yrs", "111830.17", "1.63%"),
new DataPoint("2/7yrs", "115311.17", "2.07%"),
new DataPoint("3/7yrs", "118984.65", "2.52%"),
new DataPoint("4/7yrs", "122859.65", "2.98%"),
new DataPoint("5/7yrs", "126947.77", "3.46%"),
new DataPoint("6/7yrs", "131260.74", "3.94%"),
new DataPoint("7/7yrs", "135810.92", "4.44%")
]);
Now in your View, you're listening to a change
event in the collection, but that is a Model event.
http://backbonejs.org/#Events-catalog
Normally it's preferred to listen to the reset
event, which you can trigger either resetting the collection by yourself, like we just did, or calling collection.fetch({reset:true})
to get data from the server.
It's recommended practice to use the listenTo
function for event handling, because it automatically binds the function context to the current object.
http://backbonejs.org/#Events-listenTo
So your initialize method becomes:
initialize: function (options) {
_.bindAll(this, "render");
this.listenTo(this.collection, "reset", this.updateCollection);
//Bind model change event to view event handler
//instead of individually listen to every attribute change
//just listen to any change in one line
this.listenTo(this.model, "change", this.updateCollection);
//Run setup methods
this.drawGraph();
//not needed with fake data... or save it to a JSON file and fetch it!
//this.collection.fetch();
this.updateCollection();
}
Upvotes: 1