Search code examples
extjsextjs4.1sencha-architectextjs4.2

How to give different field for label and legend in piechart in extjs


Can anybody tell me how to give a different field for a label and legend in pie chart in extjs? I have a store with two fields with name,value. I want to display name field in legend and value field in label. Now I am getting the same value for both labe1 and legend.

Thanks


Solution

  • Configure the label property of your pie serie like this:

    label: {
        // name of the model field to use as legend
        field: 'name'
        ,renderer: function(value, label, storeItem) {
            // storeItem is your model, so return the value you want as label
            return storeItem.get('value');
        }
    }
    

    Edit:

    The previous code only works from since Ext 4.2. To use it with 4.1, you'd have to override Ext.chart.series.Pie#onPlaceLabel() method to replace the lines:

        label.setAttributes({
            text: format(storeItem.get(field[index]))
        }, true);
    

    With these, from Ext4.2 code:

        label.setAttributes({
            text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index)
        }, true);
    

    Edit 2: How to override for Ext 4.1

    The syntax for overriding a class in Ext 4 is the same as the one for extending, except that you use override instead of extend. The override class is named, like any other class. In order for the override to be loaded, you have to require it, exactly like a normal class (if you don't use Ext Loader, then you must include the file in a script tag.

    Now, here's the code that will make the value renderer works in the same way as Ext4.2:

    // Again, you're free to chose the class name, just ensure the Loader can find the file
    // (and don't forget to require it!)
    Ext.define('MyApp.Ext.char.series.Pie', {
        override: 'Ext.chart.series.Pie'
    
        ,onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
            var me = this,
                chart = me.chart,
                resizing = chart.resizing,
                config = me.label,
                format = config.renderer,
                field = [].concat(config.field),
                centerX = me.centerX,
                centerY = me.centerY,
                middle = item.middle,
                opt = {
                    x: middle.x,
                    y: middle.y
                },
                x = middle.x - centerX,
                y = middle.y - centerY,
                from = {},
                rho = 1,
                theta = Math.atan2(y, x || 1),
                dg = theta * 180 / Math.PI,
                prevDg;
    
            opt.hidden = false;
    
            if (this.__excludes && this.__excludes[i]) {
                opt.hidden = true;
            }
    
            function fixAngle(a) {
                if (a < 0) {
                    a += 360;
                }
                return a % 360;
            }
    
            label.setAttributes({
                // Removed:
                // text: format(storeItem.get(field[index]))
                // Added:
                text: format(storeItem.get(config.field), label, storeItem, item, i, display, animate, index)
            }, true);
    
            switch (display) {
                case 'outside':
                    rho = Math.sqrt(x * x + y * y) * 2;
                    //update positions
                    opt.x = rho * Math.cos(theta) + centerX;
                    opt.y = rho * Math.sin(theta) + centerY;
                    break;
    
                case 'rotate':
                    dg = fixAngle(dg);
                    dg = (dg > 90 && dg < 270) ? dg + 180: dg;
    
                    prevDg = label.attr.rotation.degrees;
                    if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
                        if (dg > prevDg) {
                            dg -= 360;
                        } else {
                            dg += 360;
                        }
                        dg = dg % 360;
                    } else {
                        dg = fixAngle(dg);
                    }
                    //update rotation angle
                    opt.rotate = {
                        degrees: dg,
                        x: opt.x,
                        y: opt.y
                    };
                    break;
    
                default:
                    break;
            }
            //ensure the object has zero translation
            opt.translate = {
                x: 0, y: 0
            };
            if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
                me.onAnimate(label, {
                    to: opt
                });
            } else {
                label.setAttributes(opt, true);
            }
            label._from = from;
        }
    });
    

    As you can see, I had to copy-paste the whole method code. I don't like that because that makes us largely exposed to any code change in future version, however there is no other possible way to change the bit of code I'm targeting.

    In real life, in order to safeguard against this risk, I would add a version check and issue a warning if Ext version is greater than 4.1:

    // Using a function instead of a raw object, in order to run some code at the time of class definition
    Ext.define('MyApp.Ext.char.series.Pie', function() {
        if (Ext.getVersion().isGreaterThanOrEqual('4.2')) {
            Ext.Logger.warn('This override is rendered useless since Ext4.2');
            return {};
        } else {
            return {
                override: 'Ext.chart.series.Pie'
    
                ,onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
                    var me = this,
                        chart = me.chart,
                        resizing = chart.resizing,
                        config = me.label,
                        format = config.renderer,
                        field = [].concat(config.field),
                        centerX = me.centerX,
                        centerY = me.centerY,
                        middle = item.middle,
                        opt = {
                            x: middle.x,
                            y: middle.y
                        },
                        x = middle.x - centerX,
                        y = middle.y - centerY,
                        from = {},
                        rho = 1,
                        theta = Math.atan2(y, x || 1),
                        dg = theta * 180 / Math.PI,
                        prevDg;
    
                    opt.hidden = false;
    
                    if (this.__excludes && this.__excludes[i]) {
                        opt.hidden = true;
                    }
    
                    function fixAngle(a) {
                        if (a < 0) {
                            a += 360;
                        }
                        return a % 360;
                    }
    
                    label.setAttributes({
                        // Removed:
                        // text: format(storeItem.get(field[index]))
                        // Added:
                        text: format(storeItem.get(config.field), label, storeItem, item, i, display, animate, index)
                    }, true);
    
                    switch (display) {
                        case 'outside':
                            rho = Math.sqrt(x * x + y * y) * 2;
                            //update positions
                            opt.x = rho * Math.cos(theta) + centerX;
                            opt.y = rho * Math.sin(theta) + centerY;
                            break;
    
                        case 'rotate':
                            dg = fixAngle(dg);
                            dg = (dg > 90 && dg < 270) ? dg + 180: dg;
    
                            prevDg = label.attr.rotation.degrees;
                            if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
                                if (dg > prevDg) {
                                    dg -= 360;
                                } else {
                                    dg += 360;
                                }
                                dg = dg % 360;
                            } else {
                                dg = fixAngle(dg);
                            }
                            //update rotation angle
                            opt.rotate = {
                                degrees: dg,
                                x: opt.x,
                                y: opt.y
                            };
                            break;
    
                        default:
                            break;
                    }
                    //ensure the object has zero translation
                    opt.translate = {
                        x: 0, y: 0
                    };
                    if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
                        me.onAnimate(label, {
                            to: opt
                        });
                    } else {
                        label.setAttributes(opt, true);
                    }
                    label._from = from;
                }
            };
        }
    }()); // the define function must be executed manually with overrides (this is documented in the API)