1
votes

I am trying to plot a grouped bar chart in ExtJs similar to Sencha's example.

Can you use a field that is a field of the Child Model belonging to the chart's store's Model in the Series xField / yField?

If a "Golfer" Model has many "GolfClubs", is it possible to render a grouped bar chart showing bars for each GolfClub that belongs to a Golfer (each golfer's name will be an axis label)?

In sencha's example the store has all the data in the one record but I'm hoping it can bind automatically to a "hasmany" associated Model?

//Models

 Ext.define('GolfClub', {

extend : 'Ext.data.Model',

fields : [{
    name : 'ClubType',
    type : 'string'
}, {
    name : 'Weight',
    type : 'float'
}]

});

Ext.define('Golfer', {
    extend : 'Ext.data.Model',
    requires: ['GolfClub'],
    fields : [{
        name : 'GolferName',
        type : 'string'
    }],

    hasMany: {model: 'GolfClub', name: 'golfClubs'} 
});

//end Models

//Local Data (just to get it working first)

function data(){

var golfers = [];
    var rory = Ext.create('Golfer', {
        GolferName : 'Rory'
    });

    var rorysDriver = Ext.create('GolfClub', {
        ClubType : 'Driver',
        Weight : 80
    });

var rorysPutter = Ext.create('GolfClub', {
        ClubType : 'Putter',
        Weight : 60
    });

var rorysSandWedge = Ext.create('GolfClub', {
        ClubType : 'SandWedge',
        Weight : 50
    });

    var rorysClubs = rory.golfClubs();

    rorysClubs.add(rorysDriver);
    rorysClubs.add(rorysPutter);
    rorysClubs.add(rorysSandWedge);

    golfers.push(rory);



    var tiger = Ext.create('Golfer', {
        GolferName : 'Tiger'
    });

    var tigersDriver = Ext.create('GolfClub', {
        ClubType : 'Driver',
        Weight : 85
    });

var tigersPutter = Ext.create('GolfClub', {
        ClubType : 'Putter',
        Weight : 55
    });

var tigersSandWedge = Ext.create('GolfClub', {
        ClubType : 'SandWedge',
        Weight : 58
    });

    var tigersClubs = tiger.golfClubs();

    tigersClubs.add(tigersDriver);
    tigersClubs.add(tigersPutter);
    tigersClubs.add(tigersSandWedge);

    golfers.push(tiger);

return golfers;
}

//end Local Data

//Local Store

function store1(){
 var golferStore = Ext.create('Ext.data.Store', {
     model: 'Golfer',
     data : data()});

return  golferStore; 
}

//end Local Store

Ext.onReady(function () {
       var chart = Ext.create('Ext.chart.Chart', {
        style: 'background:#fff',
        animate: true,
        shadow: true,
        store: store1(),
        legend: {
          position: 'right'  
        },
        axes: [{
            type: 'Numeric',
            position: 'bottom',
            fields: ['golfClubs.Weight']
        }, {
            type: 'Category',
            position: 'left',
            fields: ['GolferName'],
            title: 'Golfer'
        }],
        series: [{
            type: 'bar',
            axis: ['bottom'],
            xField: ['golfClubs.Weight'],//Is that how you bind to child record?
            yField: ['GolferName']
        }]
    });


var win = Ext.create('Ext.Window', {
    width: '100%',
    height: '100%',
    title: 'Breakdown of Golfers and their Clubs..',
    autoShow: true,
    layout: 'fit',
    items: chart
});
});

Cheers, Tom.

2

2 Answers

2
votes

This has been answered in the Sencha forum in case anyone was interested with this question.

The chart's store will need to have all of the data / records populated within the store (-vs- the store being populated with parent records with each having its own associated set of child records). So, I'm afraid what you're wanting to do you can't do directly.

Answer from Sencha Support Team Member

The solution seems to be the merging of parent and child Model's fields into a new Model that can be used in the chart's store combined with groupField and extending series.

Ext.define('Result', {
    extend: 'Ext.data.Model',
    fields: [
        { name: 'state', type: 'string', mapping:0 },
        { name: 'product', type: 'string', mapping:1 },
        { name: 'quantity', type: 'int', mapping:2 }
    ]
});
var store = Ext.create('Ext.data.ArrayStore', {
    model: 'Result',
    groupField: 'state',
    data: [
        ['MO','Product 1',50],
        ['MO','Product 2',75],
        ['MO','Product 3',25],
        ['MO','Product 4',125],
        ['CA','Product 1',50],
        ['CA','Product 2',100],
        ['WY','Product 1',250],
        ['WY','Product 2',25],
        ['WY','Product 3',125],
        ['WY','Product 4',175]
    ]
});

Ext.define('Ext.chart.series.AutoGroupedColumn', {
    extend: 'Ext.chart.series.Column',
    type: 'autogroupedcolumn',
    alias: 'series.autogroupedcolumn',
    gField: null,
    constructor: function( config ) {
        this.callParent( arguments );
        // apply any additional config supplied for this extender
        Ext.apply( this, config );
        var me = this,
            store = me.chart.getStore(),
            // get groups from store (make sure store is grouped)
            groups = store.isGrouped() ? store.getGroups() : [],
            // collect all unique values for the new grouping field
            groupers = store.collect( me.gField ),
            // blank array to hold our new field definitions (based on groupers collected from store)
            fields = [];
        // first off, we want the xField to be a part of our new Model definition, so add it first
        fields.push( {name: me.xField } );
        // now loop over the groupers (unique values from our store which match the gField)
        for( var i in groupers ) {
            // for each value, add a field definition...this will give us the flat, in-record column for each group 
            fields.push( { name: groupers[i], type: 'int' } );
        }
        // let's create a new Model definition, based on what we determined above
        Ext.define('GroupedResult', {
            extend: 'Ext.data.Model',
            fields: fields
        });
        // now create a new store using our new model
        var newStore = Ext.create('Ext.data.Store', {
            model: 'GroupedResult'
        });
        // now for the money-maker; loop over the current groups in our store
        for( var i in groups ) {
            // get a sample model from the group
            var curModel = groups[ i ].children[ 0 ];
            // create a new instance of our new Model
            var newModel = Ext.create('GroupedResult');
                // set the property in the model that corresponds to our xField config
                newModel.set( me.xField, curModel.get( me.xField ) );
            // now loop over each of the records within the old store's current group
            for( var x in groups[ i ].children ) {
                // get the record
                var dataModel = groups[ i ].children[ x ];
                // get the property and value that correspond to gField AND yField
                var dataProperty = dataModel.get( me.gField );
                var dataValue = dataModel.get( me.yField );
                // update the value for the property in the Model instance
                newModel.set( dataProperty, dataValue ); 
                // add the Model instance to the new Store
                newStore.add( newModel );
            }
        }
        // now we have to fix the axes so they work
        // for each axes...
        me.chart.axes.each( function( item, index, len ) {
            // if array of fields
            if( typeof item.fields=='object' ) {
                // loop over the axis' fields
                for( var i in item.fields ) {
                    // if the field matches the yField config, remove the old field and replace with the grouping fields
                    if( item.fields[ i ]==me.yField ) {
                       Ext.Array.erase( item.fields, i, 1 );
                       Ext.Array.insert( item.fields, i, groupers );
                       break;
                    }
                } 
            }
            // if simple string
            else {
                // if field matches the yField config, overwrite with grouping fields (string or array)
                if( item.fields==me.yField ) {
                    item.fields = groupers;
                }
            }
        });
        // set series fields and yField config to the new groupers
        me.fields,me.yField = groupers;
        // update chart's store config, and re-bind the store
        me.chart.store = newStore;
        me.chart.bindStore( me.chart.store, true );
        // done!
    }
})

Ext.create('Ext.chart.Chart', {
    renderTo: Ext.getBody(),
    width: 500,
    height: 300,
    animate: true,
    store: store,
    legend: {
      position: 'right'  
    },
    axes: [
        {
            type: 'Numeric',
            position: 'left',
            fields: ['quantity'],
            label: {
                renderer: Ext.util.Format.numberRenderer('0,0')
            },
            title: 'Quantity',
            grid: true,
            minimum: 0
        },
        {
            type: 'Category',
            position: 'bottom',
            fields: ['state'],
            title: 'State'
        }
    ],
    series: [
        {
            type: 'autogroupedcolumn',
            axis: 'left',
            highlight: true,
            xField: 'state',
            yField: 'quantity',
            gField: 'product'
        }
    ]
});

Please see here

1
votes