Reputation: 1541
I'm trying to use jqplot with Durandal and knockout. I've found a post by Rob on the Durandal group (https://groups.google.com/forum/#!topic/durandaljs/WXBiSK3WmIs) that addresses this but it uses a construction for the viewmodel that completely confuses me as it adds a "prototype.activate" method to a constructor and that's completely new to me (and doesn't work when I try and use it).
Can anyone please try and tell me how I could go about using Rob's example with my revealing module pattern as below?
My viewmodel:
define(['globalVar', 'services/datacontext', 'services/calccontext"], function (globalVar, datacontext, calcContext) {
var activate = function () {
return datacontext.newEntity(articleResults, "articleVersionResults");
};
var calcAll = function () {
//can be called externally and recalcs all
return individualImpactCalc('byWeightAndFactor', 'CO2e', articleResults().material_CO2e);
//will be more calls in here soon
};
var individualImpactCalc = function (calcName, fac, calcObservable) {
return calcContext.indCalc(calcName, fac, calcObservable, globalVar.components()); //puts result straight into observable
};
var vm = {
activate: activate,
calcAll: calcAll
};
return vm;
});
Rob's code sample:
define(["jquery", "knockout"], function($, ko){
// constructor
var ctor = function () {
var self = this;
// properties
self.chartConfig = {};
};
ctor.prototype.activate = function(id, page) {
var self = this;
// get data from database
self.chartConfig.data([
[300, 295, 290],
[320, 320, 320],
[260, 265, 260]
]);
self.chartConfig.title("Forecast net operating costs");
self.chartConfig.seriesColors(["#0026FF", "#FF6A00", "#606060"]);
self.chartConfig.series([
{ label: "Foo" },
{ label: "Bar" },
{ label: "Baz" }
]);
self.chartConfig.ticks(["Apr 13", "May 13", "Jun 13"]);
};
ctor.prototype.compositionComplete = function (view, parent) {
var self = this;
var c = self.chartConfig;
self.jqChart = $.jqplot("chart", c.data(), ...
// example to change the data
setTimeout(function () {
self.chartConfig.data([
[280, 280, 280],
[280, 280, 280],
[280, 280, 280]
]);
}, 4000);
};
return ctor;
});
Edit 1: The problem is that compositioncomplete is not firing. This viewmodel is a subview within the main page. No idea if that's affecting things?
My viewmodel is now:
define(['globalVar', 'services/datacontext', 'services/calccontext', "jquery", "knockout"], function (globalVar, datacontext, calcContext, $, ko) {
// constructor
var ctor = function () {
var self = this;
// properties
self.chartConfig = {
data: ko.observableArray([]),
title: ko.observable(),
seriesColors: ko.observableArray([]),
series: ko.observableArray([]),
ticks: ko.observableArray([])
};
// subscriptions
self.chartConfig.data.subscribe(function (newValue) {
var opts = {
data: newValue
};
if (self.jqChart) {
self.jqChart.replot(opts);
console.log("chart replotted", opts);
}
});
};
function setChartVars() {
var self = this;
// get data from database
self.chartConfig.data([
[300, 295, 290],
[320, 320, 320],
[260, 265, 260]
]);
self.chartConfig.title("Forecast net operating costs");
self.chartConfig.seriesColors(["#0026FF", "#FF6A00", "#606060"]);
self.chartConfig.series([
{ label: "Foo" },
{ label: "Bar" },
{ label: "Baz" }
]);
self.chartConfig.ticks(["Apr 13", "May 13", "Jun 13"]);
};
var activate = function () {
ctor();
setChartVars();
return datacontext.newEntity(articleResults, "articleVersionResults");
};
var compositionComplete = function () {
var self = this;
var c = self.chartConfig;
self.jqChart = $.jqplot("chart", c.data());
// example to change the data
setTimeout(function () {
self.chartConfig.data([
[280, 280, 280],
[280, 280, 280],
[280, 280, 280]
]);
}, 4000);
};
var calcAll = function () {
//can be called externally and recalcs all
//return res_mat_gwp();
return individualImpactCalc('byWeightAndFactor', 'CO2e', articleResults().material_CO2e);
};
var individualImpactCalc = function (calcName, fac, calcObservable) {
return calcContext.indCalc(calcName, fac, calcObservable, globalVar.components()); //puts result straight into observable
};
var vm = {
activate: activate,
compositionComplete: compositionComplete,
calcAll: calcAll,
editArticleVersion: globalVar.editArticleVersion,
articleResults: articleResults,
ctor: ctor
};
return vm;
});
Upvotes: 3
Views: 3096
Reputation: 4269
You have to understand the fundamental difference between returning a singleton viewmodel and a function viewmodel. You also need to understand how inheritance works in JavaScript to grasp the purpose behind the prototype
property and why it's used.
You already know singleton viewmodels - this is how you have been constructing your viewmodels all this time, so, it is most familiar to you. The other way, which you don't understand very well, is a way to return an "interface" to a viewmodel, with the purpose of instantiating one or more of them. If you return a viewmodel as a function, you must use the new
keyword to instantiate it. Let me illustrate this using an example.
This example viewmodel returns a function, not a singleton:
define([], function () {
var example = function (title) {
this.title = title;
};
example.prototype.activate = function (params) {
// do something with the params
};
return example;
});
This viewmodel returns a singleton and requires the previous one as a dependency:
define(['viewmodels/example'], function (Example) {
return {
exampleOne: new Example('This is a test!'),
exampleTwo: new Example('This is another test!')
};
});
Since 'viewmodels/example' returns a function, we must use the new
keyword to instantiate it (NOTE: Durandal will do this for you if you are using composition binding in your view).
Both exampleOne
and exampleTwo
have unique titles; however, they share the same activate()
method. That's the advantage of the prototype
property. This strategy can be applied to views you want duplicated, but share the same activation code, like modal dialogs or widgets.
Upvotes: 5
Reputation: 1541
Well, in the absence of more learned solutions, I have at least managed to get it working. I had to remove the ctor function and make it a plain page variable. Had to remove all reference to "self" as in the last durandal event "this" was something other than the window object.
As "compositionComplete isn't firing in my viewmodel I used "attached" instead.
Working code:
define(['globalVar', 'services/datacontext', 'services/calccontext', "jquery", "knockout"], function (globalVar, datacontext, calcContext, $, ko) {
var chartConfig = {
data: ko.observableArray([]),
title: ko.observable(),
seriesColors: ko.observableArray([]),
series: ko.observableArray([]),
ticks: ko.observableArray([])
};
// subscriptions
chartConfig.data.subscribe(function (newValue) {
var opts = {
data: newValue
};
if (self.jqChart) {
self.jqChart.replot(opts);
console.log("chart replotted", opts);
}
});
function setChartVars() {
//var self = this;
// get data from database
chartConfig.data([
[300, 295, 290],
[320, 320, 320],
[260, 265, 260]
]);
chartConfig.title("Forecast net operating costs");
chartConfig.seriesColors(["#0026FF", "#FF6A00", "#606060"]);
chartConfig.series([
{ label: "Foo" },
{ label: "Bar" },
{ label: "Baz" }
]);
chartConfig.ticks(["Apr 13", "May 13", "Jun 13"]);
};
var activate = function () {
setChartVars();
return datacontext.newEntity(articleResults, "articleVersionResults");
};
var compositionComplete = function () {
//var self = this;
var c = chartConfig;
jqChart = $.jqplot("chart", c.data());
// example to change the data
setTimeout(function () {
chartConfig.data([
[280, 280, 280],
[280, 280, 280],
[280, 280, 280]
]);
}, 4000);
};
var calcAll = function () {
//can be called externally and recalcs all
//return res_mat_gwp();
return individualImpactCalc('byWeightAndFactor', 'CO2e', articleResults().material_CO2e);
};
var individualImpactCalc = function (calcName, fac, calcObservable) {
return calcContext.indCalc(calcName, fac, calcObservable, globalVar.components()); //puts result straight into observable
};
var vm = {
activate: activate,
attached: compositionComplete,
calcAll: calcAll,
editArticleVersion: globalVar.editArticleVersion,
articleResults: articleResults
};
return vm;
});
Upvotes: 0