Durandal knockout jchartfx

835 views Asked by At

I am trying to integrate this component jchartfx with Durandel. I have a standalone prototype using knockout without Durandel and it works perfectly, so I am convinced the issue is Durandel related.

Here is my viewmodel code:

define(function (require) {
    var http = require('durandal/http');
    var serverUrl = '/api/burndown';
    var chart1;

    function burnDownModel() {
        var self = this;
        self.initilize = true;
        self.displayName = 'Burndown Chart';
        self.description = 'Burndown Chart';
        self.chartData = ko.observableArray([]);
        self.viewAttached = function() {

        };

        // testing with fake data
        self.activate = function () {
            return http.get(serverUrl).then(function (response) {

                for (var i = 0; i < 10; i++) {
                    var data = {
                        "Date": '2013-02-18T00:00:00',
                        "DevCurrentEstimates": 6.5,
                        "TestCurrentEstimates": 7.5,
                        "DevOriginalEstimates": 8.5,
                        "TestOriginalEstimates": 9.5
                    };


                    self.chartData.push(data);
                }

                ko.bindingHandlers.jchartFx = {
                    init: function (element, valueAccessor) {
                        chart1 = new cfx.Chart();
                        chart1.getData().setSeries(4);
                        chart1.getAxisX().getLabelsFormat().setFormat(cfx.AxisFormat.Date);
                        chart1.getAxisX().getLabelsFormat().setCustomFormat("MMM-dd");
                        debugger;

                        var value = ko.utils.unwrapObservable(valueAccessor());
                        chart1.setDataSource(value);
                        chart1.create(element);
                    }
                };
            });
        };
    };

    return burnDownModel;
});

and my html binding

<div id="ChartDiv1" class="chartdiv"  data-bind="jchartFx:chartData"style="width:550px;height:400px;display:inline-block"></div>

As I said I have this working without Durandel.

Here is the script for that solution:

<script type="text/javascript">

    $(function () {
        var viewModel = {
            chartDatas: ko.observableArray([])
        };
        LoadChartData();

        function LoadChartData() {
            for (var i = 0; i < 10; i++) {
                var chartData = {
                    "Date": '2013-02-18T00:00:00',
                    "DevCurrentEstimates": 6.5,
                    "TestCurrentEstimates": 7.5,
                    "DevOriginalEstimates": 8.5,
                    "TestOriginalEstimates": 9.5
                };
                viewModel.chartDatas.push(chartData);
            }
        }

        ko.applyBindings(viewModel);
    });

</script>

The only difference I can see is that this script runs when the page loads and also I have to apply the ko bindings manually as opposed to Durandel doing it for me. I don't get any errors, it's just that no chart data is displayed.

2

There are 2 answers

0
mikekidder On BEST ANSWER

First thing on your code, change the chartData to an Observable. With jchartfx it is expecting a single array of objects (json), not an array of objects. I'm passing in a single array to the chartData observable.

Add an "update" to your Knockout bindingHandler and do the chart create method there. This will get fired when you fetch your data from either viewAttached method or afterBind in your viewModel. Since viewAttached is after Durandal binds to the DOM, your charts will show up as expected.

The viewModel file

define(function (require) {

    var system = require('durandal/system');
    var http = require('durandal/http');

    // KO observables & bindings
    var errorMessage = "";
    var pageTitle = "jChartFX Demo";
    var chartData = ko.observable();

    function LoadServerData() {

        // Your server request can go here;
        var items = [{
            "Date": '2013-02-18T00:00:00',
            "DevCurrentEstimates": 6.5,
            "TestCurrentEstimates": 7.5,
            "DevOriginalEstimates": 8.5,
            "TestOriginalEstimates": 9.5
        },{
            "Date": '2013-02-18T00:00:00',
            "DevCurrentEstimates": 6.5,
            "TestCurrentEstimates": 7.5,
            "DevOriginalEstimates": 8.5,
            "TestOriginalEstimates": 9.5
        },{
            "Date": '2013-02-18T00:00:00',
            "DevCurrentEstimates": 6.5,
            "TestCurrentEstimates": 7.5,
            "DevOriginalEstimates": 8.5,
            "TestOriginalEstimates": 9.5
        }]
       chartData(items);
    }

    var activate = function(view) {
        system.log("view activated");
        return;
    }

    var viewAttached = function (view) {
        LoadServerData();
        system.log("viewAttached loaded");
        return;
    }

    return {
        pageTitle: pageTitle,
        chartData: chartData,
        activate: activate,
        viewAttached: viewAttached
    }
});

ko.bindingHandlers.jchartFx = {
    init: function (element, valueAccessor) {
        chart1 = new cfx.Chart();
        chart1.getData().setSeries(4);
        chart1.getAxisX().getLabelsFormat().setFormat(cfx.AxisFormat.Date);
        chart1.getAxisX().getLabelsFormat().setCustomFormat("MMM-dd");
        debugger;
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        chart1.setDataSource(value);
        chart1.create(element);
    }
};

and the view file

<div>
    <h2 data-bind="text: pageTitle"></h2>
    <div id="ChartDiv1" class="chartdiv" data-bind="jchartFx: chartData" style="width:550px;height:400px;display:inline-block"></div>
</div>
0
Joseph Gabriel On

The problem seems to do with the view not being yet attached to the DOM.

One possible solution is to create a property in your viewModel that indicates whether the view has been attached, and modify the binding so that the chart doesn't get created until the view is attached.

//in view model
isViewAttached = ko.observable(false);
self.viewAttached = function () {
    self.isViewAttached(true);
};


//in binding
ko.bindingHandlers.jchartFx = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        ...
        viewModel.isViewAttached.subscribe(function () {
            $(element).chart();
        });
        ...

This is probably not the best idea, though, because it relies on a "magic" property in the view model.

A better way might be to create a secondary binding you can bind directly to the isViewAttached property, and access it from jchartFx binding via the allBindingsAccessor parameter. That way all the view model property dependencies are out in the open, and it's easier to tell what's going on.

Regardless, I highly recommend moving the binding out of the activate function - ideally into a global script that's executed when the app is first loaded. You really want to set ko.bindingHandlers.jchartFx only once - plus, you may want to use it from other view models.