score:0

Accepted answer

here is the solution i was able to find, works for doughnut charts with n datasets.

   $('#' + chart_name_donut + '_datalabels').on('change', function () {
    var selected = $('#' + chart_name_donut + '_datalabels').val()
    if (selected == 'perc') {
        isvalue = false;
    }
    else {
        isvalue = true;
    }
    chart.update();
});

//data recieved from backend
inputarray = <%series_list%>;
mydata = getcolor(inputarray);      //assign background color for each part

var mydoughnutextend = chart.controllers.doughnut.prototype.draw;

var ctxdoughnut = document.getelementbyid(chart_name_donut + "_chart").getcontext('2d');

//config options for the doughnut chart, 
var config = {
    // the type of chart we want to create
    type: 'doughnut',

    // the data for our dataset
    data: mydata,

    // configuration options go here
    options: {
        responsive: true,
        maintainaspectratio: false,
        legendcallback: function (chart) {
            var text = [];
            var legsouter = [];
            var legsinner = [];

            var innerlabel = "";
            var outerlabel = "";
            //custom legend
            for (var j = 0; j < chart.data.datasets.length; j++) {
                var legendlabel = chart.data.datasets[j].label;
                for (var i = 0; i < chart.data.datasets[j].data.length; i++) {
                    var newd = { label: chart.data.datasets[j].labels[i], color: chart.data.datasets[j].backgroundcolor[i] };

                    if (j == 0) {
                        if (!containsobject(newd, legsouter)) {
                            legsouter.push(newd);
                            outerlabel = legendlabel;
                            outerkeycount = outerkeycount + 1;
                        }
                    }
                    else if (j == 1) {
                        if (!containsobject(newd, legsinner)) {
                            legsinner.push(newd);
                            innerlabel = legendlabel;
                            innerkeycount = innerkeycount + 1;
                        }
                    }
                }
            }

            //change text-align from center to start(left) and end(right)
            text.push('<ul class="' + chart.id + '_chart-legend mylegend" style="display:inline-block;position:relative">');
            text.push('<li class="labeltitle" style="color:black;display:inline-block;float:left;">' + outerlabel + ': </li>');
            for (var k = 0; k < legsouter.length; k++) {
                text.push('<li class="labels labels-outer" indexdata="' + k + '" indexdataset="0" ><span style="background-color:' + legsouter[k].color + ';"></span>');
                text.push(legsouter[k].label);
                text.push('</li>');
            }
            text.push('<br>')
            text.push('<li class="labeltitle" style="color:black;display:inline;float:left;">' + innerlabel + ': </li>');
            for (var k = 0; k < legsinner.length; k++) {
                text.push('<li class="labels" indexdata="' + k + '" indexdataset="1"><span style="background-color:' + legsinner[k].color + ';"></span>');
                text.push(legsinner[k].label);
                text.push('</li>');
            }
            text.push('</ul>');

            return text.join("");
        },
        tooltips: {
            position:'cursor',
            backgroundcolor: 'rgba(250,250,250,1)',
            xpadding: 10,
            caretpadding: 5,
            caretsize: 0,
            cornerradius: 0,
            bordercolor: 'rgba(150,150,150,1)',
            borderwidth: 1,
            bodyfontcolor: 'rgba(0,0,0,1)',
            callbacks: {                                                                //tooltip callback showing what to display on hover
                label: function (tooltipitem, data) {
                    var dataset = data.datasets[tooltipitem.datasetindex];
                    var index = tooltipitem.index;
                    return dataset.labels[index] + ": " + dataset.data[index];

                }
            }
        },
        legend: {
            display: false
        }
    }
}

//extension to draw labels on the donut elements, position them, and avoid chart blinking on hover
chart.helpers.extend(chart.controllers.doughnut.prototype,{

    draw: function () {

        mydoughnutextend.apply(this, arguments);

        
        ctxdoughnut.font = chart.helpers.fontstring(chart.defaults.global.defaultfontfamily, 'normal', chart.defaults.global.defaultfontfamily);
        ctxdoughnut.textalign = 'center';
        ctxdoughnut.textbaseline = 'bottom';

        this.chart.data.datasets.foreach(function (dataset) {

            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[object.keys(dataset._meta)[0]].data[i]._model,
                    total = dataset._meta[object.keys(dataset._meta)[0]].total,
                    mid_radius = model.innerradius + (model.outerradius - model.innerradius) / 2,
                    start_angle = model.startangle,
                    end_angle = model.endangle,
                    mid_angle = start_angle + (end_angle - start_angle) / 2;

                var x = mid_radius * math.cos(mid_angle);
                var y = mid_radius * math.sin(mid_angle);

                ctxdoughnut.fillstyle = '#fff';
                if (i == 3) { // darker text color for lighter background
                    ctxdoughnut.fillstyle = '#444';
                }
                var percent = string(math.round(dataset.data[i] / total * 100)) + "%";
                //don't display if legend is hide or value is 0  ---- 
                if (dataset.data[i] != 0 && dataset._meta[object.keys(dataset._meta)[0]].data[i].hidden != true) {
                    if (isvalue == true) {
                        ctxdoughnut.filltext(dataset.data[i], model.x + x, model.y + y+6);
                    }
                    else if (isvalue == false) {
                        // display percent in another line, line break doesn't work for filltext
                        ctxdoughnut.filltext(percent, model.x + x, model.y + y+6);
                    }
                }
            }
        });
    }

})





//init chart with config options and type
chart = new chart(ctxdoughnut, config);

//get legend container id
var mylegendcontainer = document.getelementbyid(chart_name_donut + '_legendcontainer');

//draw legend
$(mylegendcontainer).html(chart.generatelegend());






//function to hide/show inner and outer donut parts
function legendclickcallback(event) {
    var event = event || window.event;

    var target = event.target || event.srcelement;
    while (target.nodename !== 'li') {
        target = target.parentelement;
    }
    var parent = target.parentelement;
    var chartid = parseint(parent.classlist[0].split("-")[0], 10);
    var chart = chart.instances[chartid];
    var indexdataset = parseint($(target).attr('indexdataset'));
    var indexdata = parseint($(target).attr('indexdata'));
    var indexstart = 0;
    var indexend = 0;
    var meta = chart.getdatasetmeta(indexdataset);
    var spanelement = $(target).children();
    var colortarget = $(spanelement).css('background-color');

    //outer doughnut
    if (indexdataset == 0) {
        var retainhidden = [];
        meta.data[indexdata].hidden = meta.data[indexdata].hidden == false ? !chart.data.datasets[indexdataset].data[indexdata].hidden : false;

        indexstart = indexdata * innerkeycount;
        indexend = indexstart + innerkeycount;
        if (meta.data[indexdata].hidden == true) {
            var metainner = chart.getdatasetmeta(indexdataset + 1);
            for (var i = indexstart; i < indexend; i++) {
                metainner.data[i].hidden = true;
            }
        }
        else if (meta.data[indexdata].hidden == false) {
            var numberhidden = $('.donuthiddenelement span').length
            var metainner = chart.getdatasetmeta(indexdataset + 1);
            if (numberhidden == 0) {
                for (var i = indexstart; i < indexend; i++) {
                    metainner.data[i].hidden = false;
                }
            }
            else {
                for (var i = indexstart; i < indexend; i++) {
                    for (var j = 0; j < numberhidden; j++) {
                        spanelementcolor = $('.donuthiddenelement span')[j];
                        if ($(spanelementcolor).css('background-color') == metainner.data[i]._model.backgroundcolor) {
                            metainner.data[i].hidden = true;
                            retainhidden.push(metainner.data[i]);
                        }
                        else {
                            if (retainhidden.length == 0) {
                                metainner.data[i].hidden = false;
                            }
                            else {
                                for (var k = 0; k < retainhidden.length; k++) {
                                    if (metainner.data[i] == retainhidden[k]) {
                                        metainner.data[i].hidden = true;
                                    }
                                    else {
                                        metainner.data[i].hidden = false;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    //inner doughnut
    else {
        //add case here to see which outer element is hidden and which is not
        var shownouterelements = [];
        var hiddenouterelements = [];
        var metaouter = chart.getdatasetmeta(indexdataset - 1);
        for (var i = 0; i < metaouter.data.length; i++) {
            if (!metaouter.data[i].hidden) {
                shownouterelements.push(i);
            }
        }

        for (var i = 0; i < metaouter.data.length; i++) {
            if (metaouter.data[i].hidden) {
                hiddenouterelements.push(i);
            }
        }

        //if the outer element is shown, change hidden status
        if (shownouterelements.length != 0) {
            for (var indexouter of shownouterelements) {
                initiali = indexouter * (meta.data.length / metaouter.data.length);
                endi = initiali + (meta.data.length / metaouter.data.length)

                for (var i = initiali; i < endi; i++) {
                    if (colortarget === meta.data[i]._model.backgroundcolor) {
                        meta.data[i].hidden = meta.data[i].hidden == false ? !chart.data.datasets[indexdataset].data[i].hidden : false;

                        if (meta.data[i].hidden == true) {
                            innerdatavalue[i] = meta.controller._data[i];
                            metaouter.controller._data[indexouter] = metaouter.controller._data[indexouter] - meta.controller._data[i];
                        }
                        else if (meta.data[i].hidden == false) {
                                metaouter.controller._data[indexouter] = metaouter.controller._data[indexouter] + innerdatavalue[i];
                                delete innerdatavalue[i];

                        }
                    }
                }
            }
            shownouterelements =[]
        }
        //if the outer element is hidden, keep hidden status but change data anyway
        if (hiddenouterelements.length != 0) {
            for (var indexouter of hiddenouterelements) {
                initiali = indexouter * (meta.data.length / metaouter.data.length);
                endi = initiali + (meta.data.length / metaouter.data.length)
                for (var i = initiali; i < endi; i++) {
                    if (colortarget === meta.data[i]._model.backgroundcolor) {
                        //bug here
                        if (!$(target).hasclass('donuthiddenelement')) {
                            innerdatavalue[i] = meta.controller._data[i];
                            metaouter.controller._data[indexouter] = metaouter.controller._data[indexouter] - meta.controller._data[i];
                        }
                        else if ($(target).hasclass('donuthiddenelement')) {
                            metaouter.controller._data[indexouter] = metaouter.controller._data[indexouter] + innerdatavalue[i];
                            delete innerdatavalue[i];
                        }
                    }
                }
            }
            hiddenouterelements = []
        }
        $(target).toggleclass('donuthiddenelement');
    }
    chart.update();
}

//hide outer donut elements by toggling class
$('.labels-outer').click(function () {
    $(this).toggleclass('hiddenelementouter')
})


//apply legend callback (on click expand or collapse)
var legenditems = mylegendcontainer.getelementsbyclassname('labels')
for (var i = 0; i < legenditems.length; i += 1) {
    legenditems[i].addeventlistener("click", legendclickcallback, false);
}




return chart;

Related Query

More Query from same tag