Search code examples
javascriptjsonextjssencha-chartsextjs5

ExtJS 5 Pie Chart Not Rendering Using Remote Store


I have a basic pie chart in ExtJS 5. The issue I am having is that the chart renders with a static JsonStore but won't render properly with a remote data.store?

Here is my code:

View (Chart)

Ext.define('APP.view.core.graphs.Countytotals', {
    extend: 'Ext.Panel',
    alias: 'widget.countytotalchart',
    id: 'countyTotalsGraph',

    width: 650,

    initComponent: function() {
        var me = this;

        //  Doesn't work?
        var countyStore = Ext.create('APP.store.Countytotalsgraph');


        // Works
        var store = Ext.create('Ext.data.JsonStore', {
            fields: ['COUNTY', 'AMOUNT'],
            data: [{
                'COUNTY': 'London',
                'AMOUNT': 10.92
            }, {
                'COUNTY': 'Lancashire',
                'AMOUNT': 6.61
            }, {
                'COUNTY': 'Kent',
                'AMOUNT': 5.26
            }, {
                'COUNTY': 'West Yorkshire',
                'AMOUNT': 4.52
            }, {
                'COUNTY': 'Nottinghamshire',
                'AMOUNT': 4.01
            }, {
                'COUNTY': 'Other',
                'AMOUNT': 68.68
            }]
        });

        var chart = new Ext.chart.PolarChart({
            width: '100%',
            height: 500,
            insetPadding: 50,
            innerPadding: 20,
            legend: {
                docked: 'bottom'
            },
            listeners: {
              afterrender: function (chart) {
                  if (chart.isVisible()) {
                      countyStore.load();
                      chart.redraw();
                  }
              }
            },
            interactions: ['itemhighlight'],
            store: countyStore,
            series: [{
                type: 'pie',
                angleField: 'AMOUNT',
                label: {
                    field: 'COUNTY',
                    display: 'outside',
                    calloutLine: {
                        length: 60,
                        width: 3
                        // specifying 'color' is also possible here
                    }
                },
                highlight: true,
                tooltip: {
                    trackMouse: true,
                    renderer: function(storeItem, item) {
                        this.setHtml(storeItem.get('COUNTY') + ': ' + storeItem.get('AMOUNT') + '%');
                    }
                }
            }]
        });


        me.items = [chart];

        this.callParent();
    }
});

Store

Ext.define('APP.store.Countytotalsgraph', {
    extend: 'Ext.data.Store',
    model: 'APP.model.Countytotalsgraph',
    autoLoad: false,
    storeId: 'countyTotalsGraphStore',

    proxy: {
        type: 'ajax',
        url : '/dashboard/countytotals',
        method : 'POST',
        reader: {
            type: 'json',
            rootProperty: 'data'
        }
    },

    listeners: {
        beforeload: function(store, eOpts) {
            //if ( this.data.items.length ) {
            //Ext.getCmp('optionsGrid').getView().refresh();
            //}
            store.proxy.extraParams = {
                percentage: 'true'
            }
        }
    }
});

Model

Ext.define('APP.model.Countytotalsgraph', {
    extend: 'Ext.data.Model',
    fields: ['COUNTY', 'AMOUNT']
});

This is how is renders with the static store:

With Static JsonStore

This is how it renders with the remote store:

With remote Data.Store

I am on the latest version of the GPL although the charts were built using Sencha CMD and the "sencha ant build" command in the sencha-charts directory.

Why does the static store display it (well still there is still an issue regarding the legend at the bottom) but the remote json not?

Iv'e tried to load the store after it the chart is rendered and is visible as I have seen a previous post regarding holding off on loading the store to give the chart time to render but this did not work:

listeners: {
    afterrender: function (chart) {
        if (chart.isVisible()) {
            countyStore.load();
            chart.redraw();
        }
    }
},

Thanks in advance :)

Nathan


Solution

  • Probably a bug in Ext.

    The chart colors are set in Ext.chart.AbstractChart#updateColors. This is a "config" method, that is called automatically when setColors is called, and also from the constructor, when the config is initialized.

    In your case, it is only called at construction time, before the remote store has been loaded; and it happens that polar series need to know the number of records in the store in order to know how many colors it must used (unlike other kind of charts that rely on number of axis or so).

    Here's the code of that method:

    updateColors: function (newColors) {
        var me = this,
            colors = newColors || (me.themeAttrs && me.themeAttrs.colors),
            colorIndex = 0, colorCount = colors.length, i,
            series = me.getSeries(),
            seriesCount = series && series.length,
            seriesItem, seriesColors, seriesColorCount;
    
        if (colorCount) {
            for (i = 0; i < seriesCount; i++) {
                seriesItem = series[i];
    
                // Ext.chart.series.Polar#themeColorCount uses store.getCount()
                // so seriesColorCount will be 0
                seriesColorCount = seriesItem.themeColorCount();
    
                // ... hence seriesColor will be an empty array
                seriesColors = me.circularCopyArray(colors, colorIndex, seriesColorCount);
                colorIndex += seriesColorCount;
                seriesItem.updateChartColors(seriesColors);
            }
        }
        me.refreshLegendStore();
    }
    

    You could probably get it working by creating the chart after the load event of the store, but that's kind of kinky given your usage is as intended, and the bug will probably get smashed in a coming release.

    For now, a possible fix is to override the onRefresh of the chart, that is called, well, when the store is refreshed, and force colors to be updated at this time:

    Ext.define(null, {
        override: 'Ext.chart.PolarChart'
        ,onRefresh: function() {
            this.callParent(arguments);
            var colors = this.getColors();
            if (colors) {
                this.updateColors(colors);
            }
        }
    });