ExtJS 4: Passing Filters Between a Grid and a Chart Store

In preparation for another round of Envato screencasts, I’ve been messing around with the new Charting API that comes with ExtJS 4. For this project, I wanted to show how you can filter a grid full of data and have it change the data in the pie chart. Unfortunately, I soon discovered that pie charts (or rather, charts in general) and data grids take two different types of data.

For example: here’s the JS ON response for the store that runs my pie chart:

~~js [ { “favorite_color”: “blue”, “total”: “48”, “percentage”: “12” }, { “favorite_color”: “green”, “total”: “53”, “percentage”: “13” }, { “favorite_color”: “orange”, “total”: “56”, “percentage”: “14” }, { “favorite_color”: “pink”, “total”: “59”, “percentage”: “15” }, { “favorite_color”: “red”, “total”: “38”, “percentage”: “10” }, { “favorite_color”: “violet”, “total”: “45”, “percentage”: “11” }, { “favorite_color”: “yellow”, “total”: “101”, “percentage”: “25” } ]


For a pie chart, your store needs to have two main things: a field that the
pie chart’s labeling system will use (e.g., ``favorite_color``) and a
field that the pie chart’s series will use (e.g., ``total``). As you can
see from the feature image, the pie chart uses the ``favorite_color`` and
``total`` fields to display the data in the correct proportions. Now
let’s look at a single record from the JSON response for the data grid:

~~~js
{
    "id": "50",
    "first_name": "Wynne",
    "last_name": "Aguilar",
    "age": "94",
    "gender": "2",
    "hair_color": "Other",
    "eye_color": "Hazel",
    "state": "ND",
    "favorite_color": "yellow",
    "car_model": "Toyota",
    "housing_type": "Mobile Home",
    "marriage_status": "Single",
    "has_pets": "1",
    "num_children": "10"
}

As you can see from the drastic difference between the two data stores, if we tried to power the grid and the pie chart from the same store, neither would render correctly; the pie chart wouldn’t have a field to calculate it’s segment sizes with and the grid wouldn’t have any of the fields it’s expecting through the column model. Long story short, there’s no way to tie the grid’s data to the pie chart (or vice versa) without causing serious issues.

To get around this limitation, we need to simultaneously filter both stores at the same time. Here’s the controller for both the grid and the pie chart:

Ext.define('App.controller.People', {
    extend: 'Ext.app.Controller',
    models: [
        'Person',
    ],
    stores: [
        'people.People',
        'people.FavoriteColorCounts'
    ],
    views:[
        'person.Grid',
        'chart.FavoriteColorPieChart'
    ],
    refs: [{
        ref: 'favoriteColorPieChart',
        selector: 'favoritecolorpiechart'
    }, {
        ref: 'personGrid',
        selector: 'persongrid'
    }],
    init: function() {
        this.getPeoplePeopleStore().on(
            'load',
            function(store, records, successful, operation, options) {
                var filters;
                var gridFilterFeature = this.getPersonGrid().filters;
                    filters = gridFilterFeature.buildQuery(
                        gridFilterFeature.getFilterData()
                    );
                this.getPeopleFavoriteColorCountsStore().load({
                   params: {
                       filter: filters
                   }
                });
            },
            this
        )
        this.callParent(arguments)
    }
});

There’s a lot going on there, but we really need to only focus on the init() method:

...
    init: function() {
        this.getPeoplePeopleStore().on(
            'load',
            function(store, records, successful, operation, options) {
                var filters;
                var gridFilterFeature = this.getPersonGrid().filters;
                    filters = gridFilterFeature.buildQuery(
                        gridFilterFeature.getFilterData()
                    );
                this.getPeopleFavoriteColorCountsStore().load({
                   params: {
                       filter: filters
                   }
                });
            },
            this
        )
        this.callParent(arguments)
    }

Let’s break it down:

...
this.getPeoplePeopleStore().on(
    'load',
    function(store, records, successful, operation, options) {
...

Here, we’re adding a listener to the load event of the people.People store.

...
    var filters;
    var gridFilterFeature = this.getPersonGrid().filters;
    filters = gridFilterFeature.buildQuery(gridFilterFeature.getFilterData());
    this.getPeopleFavoriteColorCountsStore().load({
        params: {
            filter: filters
      }
...

Here, we’re grabbing the filters from the grid we want to use on the pie chart’s store, rebuilding them into a JSON string using buildQuery() and then reloading the pie chart’s store while passing in the filters.

Well, I hope that helps someone out because God knows I wasted fours of my life figuring it out.