Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knockoutJS dynamic chart with high charts

So, I have been playing with high charts and KO for the past week. The syntax for the high charts graph is very simple and easy to read.

I'm coming from Java and I am pretty new to JS, so I'm not sure how to really play with the scope.

Is there anyway to use or combine knockout with high charts to easily make a dynamic graph? Can I simply put them in the same JS file?

The high charts code looks so simple there has to be an easy solution! Thanks in advance for the input/help!

Here is the code for my high charts graph:

$(function() {
    var chart;
    //alert(users);
    $(document).ready(function() {
        chart = new Highcharts.Chart({
            chart : {
                renderTo : 'container',
                type : 'line',
                marginRight : 130,
                marginBottom : 25
            },

            title : {
                text : 'Body Weight',
                x : -20 //center
            },
            xAxis : {
                categories : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
            },

            yAxis : {
                title : {
                    text : 'Weight (lbs)'
                },
                plotLines : [{
                    value : 0,
                    width : 1,
                    color : '#808080'
                }]
            },
            tooltip : {
                formatter : function() {
                    return '<b>' + this.series.name + '</b><br/>' + this.x + ': ' + this.y + 'lbs';
                }
            },
            legend : {
                layout : 'vertical',
                align : 'right',
                verticalAlign : 'top',
                x : -10,
                y : 100,
                borderWidth : 0
            },
            series : [{
                name : 'George',
                data : [185,190,185,180]
            }, {
                name : 'Bindu',
                data : [115,110,112,115]
            }, {
                name : 'Phil',
                data : [195,190,190,185]
            }]
        });
    });
});

And here is my KO model:

/*
 * Reading class
 * containts health readings
 */
function Reading(date,weight,glucose)
{
    var self = this;

    self.date=ko.observable(date);
    self.weight=ko.observable(weight);
    self.glucose=ko.observable(glucose);
    self.readingId=ko.observable(0);

      self.formattedWeight = ko.computed(function(){
        var formatted = self.weight();

        return formatted+" lb"
    });
}

/*
 * User class
 * contains a user name, date, and an array or readings
 */
function User(name,date,readings) {
    var self = this;

    self.name = name;
    self.date = date;
    self.userId = ko.observable(0);
    self.readingsArray = ko.observableArray([
        new Reading(99,22,88),new Reading(11,22,33)
    ]); 
}
// Overall viewmodel for this screen, along with initial state
function userHealthModel() {
    var self = this;
    self.inputWeight = ko.observable();
    self.inputDate = ko.observable();
    self.inputId = ko.observable();
    self.inputGlucose = ko.observable();

    // Operations
    self.subscribe = function(){
        var users = this.users();
        for(var x = 0; x < users.length; x++)
        {
            self.users()[x].userId(x); 
        }

    }
    self.addUser = function(){
        self.users.push(new User("",0,0,(new Reading(0,0))));
        self.subscribe();
    }

   self.removeUser = function(userName){
    self.users.remove(userName);
    self.subscribe();}

     //adds a readings to the edittable array of readings (not yet the reading array in a user)
    self.addReading = function(){
        var iD = self.inputId();
        var date = self.inputDate();
        var weight = self.inputWeight();
        var glucose = self.inputGlucose();
        if((weight&&date)||(glucose&&date))
        {
            self.users()[iD].readingsArray.push(new Reading(date,weight,glucose));
            self.readings.push(new Reading(date,weight,glucose));
        }
        else{
            alert("Please complete the form!")
        }
    }
    self.removeLastReading = function(userName){
        var lastIndex = (userName.readingsArray().length)-1;
        var removeThisReading = userName.readingsArray()[lastIndex];
        userName.readingsArray.remove(removeThisReading);
    }


    //editable data
     self.readings = ko.observableArray([
        new Reading(12,99,3),new Reading(22,33,2),
        new Reading(44,55,3)
    ]);

      self.users = ko.observableArray([
        new User("George",2012),new User("Bindu",2012)
    ]);
    self.subscribe();

}
like image 488
Phil Ninan Avatar asked Oct 09 '22 06:10

Phil Ninan


1 Answers

I have experience with both KO and Highcharts but weirdly I've never combined them in this manner. It would be nice to simply hold a KO model in memory that mimics the config object of HC and the two just play nice. Unfortunately this isn't going to happen as KO's observables are functions and HC expects pure json. You could do this but you'd have to destroy the chart each time and recreate with an unmapped copy of your KO model so probably not what you were thinking.

When you say dynamic I am assuming you mean the data only?

There are a number of methods on the chart/series objects documented here that can be used to connect your model to you chart. A really simple and incomplete way would be something like.

self.addUser = function() {
    var user = new User("Foo", 0, 0, new Reading(0, 0));
    self.users.push();
    self.subscribe();

    // chart handler
    /// need to convert the readingsArray into a data array
    chart.addSeries({ name: user.name, data: [180, 175, 195, 180] });
}

Alternatively you could subscribe to the users array and update the chart accordingly. (Note I didn't map your user readings data to the array but you get the idea)

viewModel.users.subscribe(function() {
  .. update chart here
});

See here for more details on subscribing to observables.

Here is the fiddle I created to test this.

http://jsfiddle.net/madcapnmckay/uVTNU/

I'm afraid there is going to be a bit of manual mapping of your model objects to highchart's data format.

Hope this helps.

like image 197
madcapnmckay Avatar answered Oct 12 '22 11:10

madcapnmckay