June 21, 2010

Hiding Series in ExtJS Charts

In the last few posts we saw how to create and handle events in Ext JS charts. We will have a look at some advanced tasks using Ext JS charts. In this post, we will learn how to hide and display a series in a chart.

As usual, I will go with the sales and revenue chart. In our chart we will have two series - Sales and Revenue. We will provide two check boxes to toggle the series' visibility.
Lets begin with the data store (The data store is same as the one used in previous tutorials):
var store = new Ext.data.JsonStore({
fields:['month', 'sales','rev'],
data: [
    {month:'Jan', sales: 2000, rev: 3000},
    {month:'Feb', sales: 1800, rev: 2900},
    {month:'Mar', sales: 1500, rev: 3100},
    .
    .
    .
    {month:'Nov', sales: 2100, rev: 3000},
    {month:'Dec', sales: 2650, rev: 3300}
]
});
The framework does not provide us with APIs for what we are trying to achieve. But before that lets display the chart and the controls without the proposed functionality:
new Ext.Panel({
    title: 'Hiding series in ExtJS Charts',
    renderTo: 'container',
    width:600,
    height:300,
    tbar:[{
         xtype:'checkbox',
         boxLabel:'Toggle Sales',
         handler: function() {
                  alert('Hide / Show Sales');
           }
         },{
         xtype:'checkbox',
         boxLabel:'Toggle Revenue',
         handler: function() {
                  alert('Hide / Show Revenue');
           }
         }],
    items: [{
        xtype: 'columnchart',
        id:'myChart',
        store: store,
        xField: 'month',
        extraStyle: {
                  legend: {
                        display: 'right'
                  }
    },
    series: [{
        yField: 'sales',
        displayName: 'Sales'
        },{
        yField: 'rev',
        displayName: 'Revenue'
    }]
  }]
});
Now we have the chart and the necessary handler methods on the checkbox, let's split the problem into two parts. First is hiding the series in the chart and second is to hide the series name in the legend.

To hide the series, you will have to access the APIs exposed by flash component. The SWF component exposes a method named setSeriesStylesByIndex for setting styles for a series. The method has two parameters - index of the series you are about to modify and the config object that will hold the new style.You can pass the following parameters and its values to the config object:
  • color
  • colors
  • size
  • alpha
  • borderColor
  • borderAlpha
  • fillColor
  • fillAlpha
  • lineSize
  • lineColor
  • lineAlpha
  • image
  • images
  • mode
  • connectPoints
  • connectDiscontinuousPoints
  • discontinuousDashLength
  • skin
  • visibility
For more details on these attributes, have a look at YUI documentation. Back to our method, it will now look something like this:
function setSeriesStylesByIndex(index, styles){
    // Assuming swfObject is an instance of swf available in chart
    swfObject.setSeriesStylesByIndex(index, Ext.encode(styles));
}
Our next hurdle is how to call this method when clicking on our checkbox. To accomplish our task we will use the override method (static method) available in Ext class.
Here is the final code of our function being injected into chart:
Ext.override(Ext.chart.Chart, {
    setSeriesStylesByIndex: function(index, styles){
        this.swf.setSeriesStylesByIndex(index, Ext.encode(styles));
    }
});
If you execute our project and have a look at the chart rendered you will notice that the series display can be toggled but the legend of the chart is not updated properly. To update the chart's legend, we will update the showInLegend property of the series. Each series can be accessed in a chart using the series array. So, to modify the first first series, we just need to use the array notation to access the series and the showInLegend property. For example:
chart.series[0].showInLegend = false;
By just setting the property will not have any effect on the chart. We will have to update the display by calling the refresh method available with the chart object. Here is how our checkbox event handler look finally:
function() { 
var chart = Ext.ComponentMgr.get('myChart');
    if(this.checked) {
         // hide the columns (series in chart)
         chart.setSeriesStylesByIndex(0,
                  {connectPoints: false,alpha:0.0});
         // hide the series label in legend
         chart.series[0].showInLegend = false;
    } else {
         chart.setSeriesStylesByIndex(0,
                  {connectPoints: true,alpha:1.0});
         chart.series[0].showInLegend = true;
    }
    // Refresh the chart
    chart.refresh();
}
You will have to hard-code the appropriate series index so that the correct series properties is modified.The complete code is given below:
new Ext.Panel({
    title: 'Hiding series in ExtJS Charts',
    renderTo: 'container',
    width:600,
    height:300,
    tbar:[{
         xtype:'checkbox',
         boxLabel:'Toggle Sales',
         handler: function() {
             var chart = Ext.ComponentMgr.get('myChart');
             if(this.checked) {
                 chart.setSeriesStylesByIndex(0,
                          {connectPoints: false,alpha:0.0});
                 chart.series[0].showInLegend = false;
             } else {
                 chart.setSeriesStylesByIndex(0,
                          {connectPoints: true,alpha:1.0});
                 chart.series[0].showInLegend = true;
             }
             chart.refresh();
         }
    },{
         xtype:'checkbox',
         boxLabel:'Toggle Revenue',
         handler: function() {
             var chart = Ext.ComponentMgr.get('myChart');
             if(this.checked) {
                 chart.setSeriesStylesByIndex(1,
                          {connectPoints: false,alpha:0.0});
                 chart.series[1].showInLegend = false;
             } else {
                 chart.setSeriesStylesByIndex(1,
                          {connectPoints: true,alpha:1.0});
                 chart.series[1].showInLegend = true;
             }
             chart.refresh();
         }
    }],
    items: [{
        xtype: 'columnchart',
        id:'myChart',
        store: store,
        xField: 'month',
        extraStyle: {
             legend: {
                display: 'right'
             }
        },
    series: [{
        yField: 'sales',
        displayName: 'Sales'
    },{
        yField: 'rev',
        displayName: 'Revenue'
    }]
  }]
});
ExtJS Line Chart showing Sales & Revenue

Well, that's all for now. In the coming weeks we will have some more tutorials on chart and finally build a dashboard. We will also have a look at the new touch UI framework introduced recently.

Read other ExtJS related articles!

June 15, 2010

ExtJS is now Sencha!

The post's heading speaks it all. Yes, Ext JS a javascript UI library has combined forces with jQTouch and Raphael projects. The company's name is being changed to Sencha which is a popular Japanese green tea. It is interesting to see jQtouch and Raphael which are two leading open source projects getting combined.

As a developer using Ext JS, I am very excited to see this merging. I would like to see Ext JS making use of Raphael's graphics capabilities. At the same this, it will be very interesting to see how a jQuery backed mobile web development library is going to fit it. Will they rewrite the library using Ext JS core? Another interesting point is that both Raphael and jQtouch uses MIT license. The company also managed to get hold of Jonathan Stark to maintain jQtouch.

What I would like to see is a better visualization API (charting tools) based on Raphael rather than the YUI charts. It will be also interesting to see how these projects collaborate and how their outcome affects the developer community.

Read other articles and tutorials on ExtJS.

June 03, 2010

Handling Events in ExtJS Charts

After publishing "Getting started with ExtJS Charts" article last week, I got a question on handling event in ExtJS charts. So, in this article we will discuss about the event handling. Before we present the reader’s issue, let’s have a look at how to handle events.


The documentation of chart package is really bad! I wonder why ExtJS team is not updating it. I would actually love to see a java script based chart library (built by ExtJs team) rather than YUI charts being used. Another option for developers would be to use other charting tools available. But for now, let's get back to event handling.

You can attach custom event handlers just like how you do for any other ExtJS component. You can use the listeners property or use methods like on and addListener.

Now if you have a look at the Cart's source code you will find few hidden events:
  • itemmouseover
  • itemmouseout
  • itemclick
  • itemdoubleclick
  • itemdragstart
  • itemdrag
  • itemdragend
Unfortunately, I found some of these not working as it should be. Even forums seem to have less detail about these events.

Item Click Event (itemclick):
Item click event is invoked when a chart item is clicked. With this event you can do drill down, update another chart or do other dynamic effects on the chart. Let’s dive into the code to get a better understanding.

First let’s set up our data store:
var store = new Ext.data.JsonStore({
    fields:['month', 'sales','rev'],
    data: [
            {month:'Jan', sales: 2000, rev: 3000},
            {month:'Feb', sales: 1800, rev: 2900},
            .
            .
            .
     {month:'Dec', sales: 2650, rev: 3300}
 ]
});
We will create a simple column chart using this store:
new Ext.Panel({
    title: 'A Demo Application',
    renderTo: 'container',
    width:600,
    height:350,
    items: [{
 xtype: 'columnchart',     
 id:'myChart',
        store: store,
        xField: 'month',
 yField: 'sales',
 listeners: {
            itemclick: function(o) {
         var record = store.getAt(o.index);
  Ext.Msg.alert('Item Selected',
                    "You Clicked on Sales: " +rec.get('sales') + 
                    " for " + rec.get('month'));
     }
 }      
    }]
});
In the above code, I have used listeners property to specify my itemclick events handler. Using the parameter passed to the event’s method, we will be able to access all the necessary information. Here I am just displaying the value of sales.

Item Mouse Over Event (itemmouseover):
itemmouseover event is fired when user mouse over an item. This can be used for display some dynamic selection. For example, let’s assume we had a data grid with the same values ie sales and revenue and we would like to automatically select the row when use mouse over on an item in the chart.

We will introduce a data grid that will render the same data store. This time, I will make use of the line chart and display both sales and revenue details.
new Ext.Panel({
        title: 'A Demo Application',
        renderTo: 'container',
 layout:'column',
        height: 350,        
        items: [{
     xtype: 'linechart',
     width:600,
          height:350,
     id:'myChart',
            store: store,
     xField: 'month',
     xAxis: new Ext.chart.CategoryAxis({
                 title: 'Month'
     }),
     yAxis: new Ext.chart.NumericAxis({
  title:'USD', 
         majorUnit: 500
     }),
     series: [{
  yField: 'sales',
  displayName: 'Sales',       
     },{
  yField: 'rev',
  displayName: 'Revenue'
            }],
     listeners: {
  itemmouseover: function(o) {
             var myGrid = Ext.getCmp('myGrid');
      myGrid.selModel.selectRow(o.index);       
  }
     }      
 },{
     xtype: 'grid',
     title:'The Sales Grid',
     id:'myGrid',
     store: store,
     height: 350,
     width: 300,
     stripeRows: true,
     sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
     columns:[
  {header:'Month',dataIndex:'month'},
  {header:'Sales',dataIndex:'sales'},
  {header:'Revenue',dataIndex:'rev'}
     ]
 }]
 });

The area of interest would be to see how we did it. Have a close look at the itemmouseover event handler. Basically, the key to manipulation is the object that is passed to the event handler. Using this you will be able to access not only the index but also the chart object itself. The attributes of this object are:
  • Component – The chart component itself.
  • Index – The index position of the item in store.
  • Item – The row from store corresponding to the index. You can access individual attributes of the row.
  • seriesIndex – The index position of the series. The values starts with 0.
  • type – Name of the event
  • x – x coordinate 
  • y – y coordinate

Drag Events:
itemdrag, itemdragstart and itemdragend are used for drag events. I tried making use of these events to make a graph wherein user will be able to drag the points and modify the values. Unfortunately, I am stuck! If I get it running, I will present it in future.

Double Click Event (itemdoubleclick):
I had very bad time working with this event! It just doesn’t get fired. From the forum discussions I see that this event might not be implemented yet. I wonder why, but It would have been great if user can double click on a specific month’s sales and he get it a detail report to download.

Now, lets tackle the reader's problem.

Reader’s problem:
The reader asked how the click event works and how to know which series was clicked.

Solution:
Now, solving this is very easy. You can handle click using the itemclick event and to identify the series and values you can make use of the values provided by the object. Just modify the as shown below:
itemclick: function(o){
 var rec = store.getAt(o.index);
 if(o.seriesIndex==0)
  Ext.Msg.alert('Item Selected',
                "You Clicked on Sales: " +rec.get('sales') + 
                " for " + rec.get('month'));
 else
  Ext.Msg.alert('Item Selected',
               "You Clicked on Revenue: " +rec.get('rev') +
               " for " + rec.get('month'));
}

Well, thats all for now. Let me know your feedback through comments or my contact form. Enjoy the day and time for me to relax!

Read other ExtJS related articles!