1

I have a website that uses AJAX to deliver a JSON formatted string to a HighCharts chart.

You can see this as the middle JSON code part at:

http://jsfiddle.net/1Loag7pv/

$('#container').highcharts(

//JSON Start

{
"plotOptions": {
"series": {"animation": {"duration": 500}}
,"pie": {
"allowPointSelect": true,
"cursor": "pointer",
"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}
    }
},
"chart":{"renderTo":"divReportChart"}
,"title":{"text":"Sales Totals"}
,"xAxis":{"title":{"text":"Item"}, "categories":["Taxes","Discounts","NetSalesTotal"], "gridLineWidth":1}
,"yAxis":[{"title":{"text":"Amount"}, "gridLineWidth":1}]
,"series":[{"name":"Amount","type":"pie", "startAngle": -60,"yAxis": 0,"data":[["Taxes",17.8700],["Discounts",36.0000],["NetSalesTotal",377.9500]]}]
}

 //JSON end

);

The problem is that the function part...

"dataLabels": {"formatter":function(){return this.point.name+': '+this.percentage.toFixed(1) + '%';}}

is not being transferred via the JSON

All research tells me that there is NO WAY to do this.

IE... Is it valid to define functions in JSON results?

Anybody got an idea on how to get around this limitation?

Community
  • 1
  • 1
Scott Savage
  • 373
  • 1
  • 4
  • 17
  • Sorry; I misread the introduction. Well it's true that you cannot send functions through JSON, but nothing's stopping you from adding those functions in the client before you call Highcharts. – Pointy Oct 20 '14 at 18:04
  • Why are you sending your Highcharts options to the server via AJAX? Highcharts is a client-side plotting library, what's your server going to do with the config? – Mark Oct 20 '14 at 18:19

3 Answers3

3

It is true that you cannot pass functions in JSON. Javascript is a superset of JSON.

A common approach is for the chart to be defined in javascript (e.g. during the page load), and the page then requests just the data via Ajax. When the data is returned it can be added to the chart object, either before it is rendered or afterwards using the highcharts API.

If you really want to pass the formatter function from the server with the chart, send it as a string, and then turn it into a function like this:

var fn = Function(mystring);

and use it in highcharts like:

chart.plotOptions.pie.dataLabels = {"formatter":fn};

I've re-factored your example to show the approach: http://jsfiddle.net/wo7zn0bw/

SteveP
  • 18,840
  • 9
  • 47
  • 60
2

I had a similar conundrum. I wanted to create the JSON server side (ruby on rails) so I could create images of charts for a web API and also present it on the client web browser with the same code. This is similar to SteveP's answer.

To conform with JSON standards, I changed all formatter functions to strings

{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}

On the web side, I navigate the hash looking for formatter keys and replace them with the function using this code (may be a better way!?). javascript:

function HashNavigator(){

    this.navigateAndReplace = function(hash, key){
        if (!this.isObject(hash)){
            //Nice if only navigated hashes and arrays
            return;
        }

        var keys = Object.keys(hash);
        for(var i = 0; i< keys.length; i++){
            if (keys[i] == key){
                //convert string to js function
                hash[keys[i]] = this.parseFunction(hash[keys[i]]);
            } else if (this.isObject(hash[keys[i]])){
                //navigate hash tree
                this.navigateAndReplace(hash[keys[i]], key);
            } else {
                //continue
            }
        }
    };

    this.isObject = function(testVar) {
        return testVar !== null && typeof testVar === 'object'
    }

    //http://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript
    this.parseFunction = function(fstring){
        var funcReg = /function *\(([^()]*)\)[ \n\t]*{(.*)}/gmi;
        var match = funcReg.exec(fstring.replace(/\n/g, ' '));

        if(match) {
            return new Function(match[1].split(','), match[2]);
        }

        return null;
    };

}

To use this, would be something similar to this javascript:

hashNavigator = new HashNavigator();
hashNavigator.navigateAndReplace(myHighchartsHash, "formatter")

At that point the hash/js-object is Highcharts ready

Similar idea was used for the web image API.

I was really hoping that hacking at the JSON was not the only solution, but it works!

Matthew Pautzke
  • 548
  • 5
  • 13
0

I used a different approach. I created a JSON like below

{"formatter": "function(){ return this.point.name+':'+this.percentage.toFixed(1) + '%';}"}

When I came to evaluating the expression, I used (assuming that the value of the 'formatter' is formatterValueString)

formatterValueString = formatterValueString.replace('function()', '');
let opts = (new Function(formatterValueString)).call(this);
formatterValue = opts;

The reason to use this approach was it became hard to bind 'this' with the function. The eval() function did not go well with accessing variable this. I am sure there are ways to do it. Just thought this was quick.

Sacky San
  • 1,535
  • 21
  • 26