Search code examples
javascripthighcharts

Highcharts How to dynamically update legend with grouped values?


I'm trying to get the legend to show the grouped value for the series, and to dynamically update it when the range is changed.

By changing the range, I mean clicking on range selector, moving the navigator or zooming in/out.

I've managed to show the total when the chart is loaded. But I want to show only the total for the current range selected, not of the whole data.

I tried with a function on labelFormatter. I can access this.yData, which is all the data. But I can't access this.groupedData or this.processedYData, to get only the current data grouped. It gives me undefined.

But I suppose the best way is to use redraw on events to update the legend. But even with this I can't access the grouped values, only the total of all data.

Here is the fiddle with what I got so far: http://jsfiddle.net/3H3ac/

Any idea how I can do this?


Solution

  • There are a couple of solutions to achieve that, one of them is to re-render label in afterSetExtremes event: http://jsfiddle.net/UaaZ3/

        xAxis: {
            events: {
                afterSetExtremes: function () {
                    var chart = this.chart,
                        legend = chart.legend,
                        merge = Highcharts.merge;
                    
                    // force redraw each element in legend
                    this.chart.isDirtyLegend = true;
                    
                    for (var i in legend.allItems) {
                        var item = legend.allItems[i];
                        
                        item.legendItem.destroy(); // destroy previous label
                        
                        item.legendItem = chart.renderer.text(
                            legend.options.labelFormatter.call(item),
                            legend.symbolWidth + legend.options.symbolPadding,
                            legend.baseline)
                        .css(merge(item.visible ? legend.itemStyle : legend.itemHiddenStyle)) 
                        .attr({
                            align: 'left',
                            zIndex: 2
                        })
                        .add(item.legendGroup); // add new label
                    }
                    this.chart.redraw(false); // redraw chart, just in case
                }
            }
        },
    

    And labelFormatter:

            labelFormatter: function () {
                var total = 0,
                    data = this.processedYData ? this.processedYData : this.yData; // series is rendered, or first render
                for (var i = data.length; i--;) {
                    total += data[i];
                };
                return this.name + '- Total: ' + total;
            },
    

    Also, for now you are using selected option in rangeSelector. Unfortunately for you, first is rendered legend, with series, then calculations for series are done. To avoid that, remove selected option, and in chart.events.load use chart.setExtremes() to force legend update.

    EDIT:

    To prevent error I developed better solution, see: http://jsfiddle.net/UaaZ3/3/

    Formatter:

    function getLegendFormat(serie) {
        var total = 0,
            data = serie.processedYData ? serie.processedYData : serie.yData; // series is rendered, or first try?
        for (var i = data.length; i--;) {
            total += data[i];
        };
        return serie.name + '- Total: ' + total;
    
    }
    

    AdterSetExtremes:

        xAxis: {
            events: {
                afterSetExtremes: function () {
                    var chart = this.chart,
                        legend = chart.legend;
    
                    for (var i in legend.allItems) {
                        var item = legend.allItems[i],
                            newText = getLegendFormat(item);
    
                        item.legendItem.attr({ text: newText })
                    }
                }
            }
        },
    

    labelFormatter:

            labelFormatter: function () {
                return getLegendFormat(this);
            },