Accessibility

Beyond the DataGrid Control: Better Data Visualization with Flex Charting Components

Ely Greenfield

www.macromedia.com/flex/

Sometimes a DataGrid control just isn't enough. In the rich world of Macromedia Flex applications, developers quickly want to leverage the capabilities of the Macromedia Flash platform to augment those rows and rows of dry numbers with beautiful, easy-to-understand data visualizations. With the release of Flex 1.5, Macromedia gives you a new set of highly customizable charting components you can use to win friends, impress your coworkers, and build the chart of your dreams.

This article introduces you to the new charting platform in Flex 1.5. I will guide you through the process of building a highly stylized, interactive drill-down chart for the fictitious Zinger Corporation. By the time you're through, you'll know how to build a chart that looks like Figure 1.

This tutorial shows you how to build this chart

Figure 1. This tutorial shows you how to build this chart

You start by building a simple column chart, plotting the units sold for each of Zinger Corporation's product lines for each month of 2003. After the basic chart is constructed, you learn how to customize its appearance through advanced styling, as well as add interactivity and drill-down capabilities. Finally, you learn how to go beyond the built-in chart types to create a custom charting solution of your own.

Requirements

To complete this tutorial you will need to install the following software and files:

Macromedia Flex

Macromedia Flash Player

A text editor or Macromedia Flex Builder

Tutorials and sample files:

Prerequisite knowledge:

Basic experience with the Flex development model and application framework. A passing familiarity with charting concepts would be helpful, but is not required.

Creating the Chart

In your own applications you can use charts to display data you've loaded from one of the Flex network service components: WebService, HTTPService, or RemoteObject. For this tutorial, start your application by including an ActionScript file that will generate some dummy data:

  1. Unzip the flex_charting_tutorials.zip file into the document root of your Flex 1.5 installation.
  2. Create a new file and save it as ZingerChart.mxml in the flex_tutorials directory.
  3. At the beginning of the file, insert a basic application skeleton with a reference to the ActionScript file that is part of this tutorial's sample files:

    <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" initialize="initDummyData();" width="100%" height="100%">
    
        <mx:Script source="zingerChartFunctions.as" />
    
        <mx:Panel title="sample chart" width="100%" height="100%">
        </mx:Panel>
    
    </mx:Application>

    Note: You can find the ActionScript file in the sample flex_charting_tutorials.zip file linked to in the "Requirements" section at the beginning of this article.

  4. Add a simple ColumnChart control inside the panel and connect it to a simple dataProvider object. The chart dataProvider property should be the same format as that of the DataGrid control: an Array object or some other object implementing the simple dataProvider API:

    <mx:ColumnChart dataProvider="{zingerManufacturingData}" width="100%" height="100%" >
    </mx:ColumnChart>

    Note: zingerManufacturingData is defined in zingerChartFunctions.as.

  5. Save your application and load it in the browser. Your chart should look similar to Figure 2.

    This is an empty, unstyled column chart

    Figure 2. This is an empty, unstyled column chart

    That's a good start but it doesn't exactly look like a column chart yet. Unlike the DataGrid control, the charting components make no assumptions about which fields of the dataProvider object to render. You'll add the columns in a moment; for now, let's focus on getting those axes in shape.

Specifying the Axes

Although chart types differ in how they display data, fundamentally they are all about mapping (typically two dimensions of) data to a coordinate on the screen. You specify how a particular chart performs that mapping by specifying its axes.

For example, consider the typical column chart. In most column charts, you see a series of category labels along the horizontal axis (for example, cities, products, demographic groups, and so on) and a numeric range (for example, 0 to 100, 1000 to 5000, and so on) along the vertical axis. The chart maps the position of the column along the horizontal axis based on what category it represents and the height of the bar along the vertical axis based on the numeric value it represents.

In Flex, you dictate how the chart should map values along each axis by specifying an axis type and value.

To map the column's position by category, place a CategoryAxis object in the chart's horizontalAxis property. Use a CategoryAxis object when you want to map a series of discrete categories evenly along the axis onscreen. The CategoryAxis object draws the categories from its own dataProvider property. In this tutorial, you use the same dataProvider property to generate the category labels and specify which field of the dataProvider property's content it should draw from by setting the categoryField property.

  1. Add a CategoryAxis object to the horizontalAxis property inside the ColumnChart tag:

    <mx:ColumnChart ...>
        <mx:horizontalAxis>
            <mx:CategoryAxis dataProvider="{zingerManufacturingData}" categoryField="Month" />
        </mx:horizontalAxis>
    </mx:ColumnChart>

    Note: As with other Flex controls, the categoryField property is optional. You can assign an array of strings to the CategoryAxis object's dataProvider property instead and leave categoryField blank.

    Similarly, to map the height of each column to a numeric value in the dataProvider property, specify a LinearAxis object in the chart's verticalAxis property. You use a LinearAxis object when you want to map numeric values within a specific range. You can either explicitly specify the minimum and maximum values of the range or let the LinearAxis object determine them automatically from the data being rendered in the chart.

  2. Add a LinearAxis object to the verticalAxis property inside the ColumnChart tag:

    <mx:ColumnChart ...>
        ...
        <mx:verticalAxis>
            <mx:LinearAxis />
        </mx:verticalAxis>
    </mx:ColumnChart>

    Note: The vertical and horizontal axis of all the standard chart types (except for the pie chart) default to LinearAxis objects. You don't have to declare them in your chart if you have no additional properties to specify.

  3. Save your application and load it in the browser. Your chart should appear as shown in Figure 3.

    This is your column chart with a horizontal category axis

    Figure 3. This is your column chart with a horizontal category axis

    Now your axes are all set to map the chart data properly. Next let's add a couple of series objects to specify which of the dataProvider fields to display.

Adding the Series

A chart's relationship to its series is similar to a DataGrid control's relationship to its columns: just as the DataGridColumn control indicates which fields of the dataProvider property the grid should display, a series object indicates which fields of the dataProvider property to render as columns, lines, plots, and so on. Unlike the DataGrid control, the series objects in a chart are not optional; with no series specified, a chart will render no data.

Although DataGridColumn controls use only one field to render data, most series objects can render multiple pieces of data. A PlotSeries class, for example, uses one property of the dataProvider object to determine its horizontal position and another to determine its vertical position. The ColumnSeries class can use one field to determine its height, one field to determine its category (or horizontal position), and one field to determine the base of the column—where the bottom of the column should lie along the vertical axis:

  1. Add an array of two ColumnSeries objects inside the ColumnChart tag's series property:

    <mx:ColumnChart ...>
        ...
        <mx:series>
            <mx:Array>
                <mx:ColumnSeries yField="Widgets" />
                <mx:ColumnSeries yField="Blinkies" />
            </mx:Array>
        </mx:series>
    </mx:ColumnChart>

    Note: Many field properties on the various series types are optional. For example, a ColumnSeries object with no xField value specified will render the data in the order it appears in the dataProvider property—along the x-axis from left to right. With no minField value specified, the columns will base themselves at the origin.

  2. Save your application and load it in the browser. Your chart should look similar to Figure 4.

    This is your column chart with two ColumnSeries classes added

    Figure 4. This is your column chart with two ColumnSeries classes added

Customizing Your Chart

Now that you have your chart showing the right information, it's time to put some effort into making it look right.

Cleaning Up Your Chart's Labels

The first task at hand is to do something about those unsightly labels along the horizontal axis. Try resizing the browser window; notice how cluttered and hard to read those labels get when the window's size is reduced? Isn't there a better way to squeeze in those labels?

Well, it turns out there is. The Flash client has a long-standing limitation that says if you want to rotate text, you have to use an embedded font. Your chart component knows this and will only consider rotating those labels to fit if you provide it with an appropriate font:

  1. Add a style block to your Application tag with an embedded font declaration. Assign this as the font-family style for your ColumnChart selector:

    <mx:Style>
        @font-face {
            font-family: chartLabelFont;
            src: local("Arial");
        }
    
        ColumnChart {
            font-family: chartLabelFont;
        }
    </mx:Style>

  2. Save your application and load it in the browser. Your chart should now look similar to Figure 5.

    This is your column chart with rotated labels

    Figure 5. This is your column chart with rotated labels

    Now try resizing the browser window. You'll notice that as the chart changes size, the angle and spacing of the horizontal labels change also. While you can specify exact spacing and rotation values through additional style properties, the Flex charts do their best to default always to a reasonable appearance, no matter how much data they render.

    Next, make those labels on the vertical axis a little more readable. While the Axis objects (LinearAxis and CategoryAxis) are responsible for mapping values from data to screen coordinates, it's the AxisRenderer object that is responsible for drawing the axes themselves onscreen. Each chart (with the exception of the pie chart) has two AxisRenderer objects, one for the horizontal and one for the vertical axis. To format the labels of an axis, you can provide a labelFunction value to the appropriate AxisRenderer object.

  3. Add an AxisRenderer object with a labelFunction value specified in the verticalAxisRenderer property of the ColumnChart tag:

    <mx:ColumnChart ...>
        ...
        <mx:verticalAxisRenderer>
            <mx:AxisRenderer labelFunction="formatThousands" />
        </mx:verticalAxisRenderer>
    </mx:ColumnChart>

    Note: The formatThousands function can be found in the zingerChartFunctions.as file.

Using Fills

Rather than specifying a color style to affect the appearance of chart components, Flex 1.5 introduces the Fill object. Using fills rather than numeric RGB values enables you to leverage the full range of the Flash platform's rendering abilities to customize your chart.

Next use different Fill types to customize the way your columns are rendered:

  1. Apply the following SolidColor object to the fill style of your first ColumnSeries tag:

    <mx:ColumnSeries yField="Widgets">
        <mx:fill>
            <mx:SolidColor color="#82C9EB" />
        </mx:fill>
    </mx:ColumnSeries>

  2. Apply the following LinearGradient object to the fill style of your second ColumnSeries tag:

    <mx:ColumnSeries yField="Blinkies" >
        <mx:fill>
            <mx:LinearGradient angle="0">
                <mx:entries>
                    <mx:Array>
                       <mx:GradientEntry ratio="30" color="#258BC8" />
                       <mx:GradientEntry ratio="80" color="#82C9EB" />
                       <mx:GradientEntry ratio="100" color="#258BC8" />
                    </mx:Array>
                </mx:entries>
            </mx:LinearGradient>
        </mx:fill>
    </mx:ColumnSeries>

  3. Save your file and reload it in the browser. Your chart should look similar to Figure 6.

    Use Fill objects to style your ColumnSeries class

    Figure 6. Use Fill objects to style your ColumnSeries class

Using CSS to Style Subcomponents

You can use Cascading Style Sheets (CSS) to specify style properties for nested components of the chart, such as the Axes, GridLines, and Series objects. Next use CSS type selectors to control the appearance of the chart's GridLines and AxisRenderer objects:

  1. Add a CSS selector to the Style tag in your application to specify the appearance of the gridlines:

    <mx:Style>
        ...
        .vBarGridlines {
            direction: both;
            verticalFill: #FFEEAA;
            verticalTickAligned: false;
        }
    </mx:Style>

  2. Pass the gridlines' new style name to your chart in the gridLinesStyle property of the ColumnChart selector:

        ColumnChart {
            font-family: chartLabelFont;
            gridLinesStyle: vBarGridlines;
        }

  3. Use the horizontalAxisStyle and verticalAxisStyle properties to change the appearance of the chart's axes. Flex ships with a number of predefined axis styles; for this tutorial, use the linedNumericAxis and hangingCategoryAxis styles. Add these properties to the ColumnChart style selector:

    ColumnChart {
            font-family: chartLabelFont;
            gridLinesStyle: vBarGridlines;
            verticalAxisStyle: linedNumericAxis;
            horizontalAxisStyle: hangingCategoryAxis;
    }
  4. Save your file and reload it in the browser. Your chart should appear as shown in Figure 7.

    You can style the axes and gridlines with nested CSS selectors

    Figure 7. You can style the axes and gridlines with nested CSS selectors

Adding Interactivity

Now that your chart is looking stylish, it's time to make it more than just another pretty face. Charts are great for getting an overall view of the landscape, but most users need to know the specifics as well. Enable the built-in DataTips object of the chart to give your users access to the individual values:

  1. Enable the DataTips object by adding the showDataTips=true attribute to your ColumnChart tag:

    <mx:ColumnChart dataProvider="{zingerManufacturingData}" width="100%" 
    height="100%" showDataTips="true">

  2. Save your file and reload it in the browser. Try moving your mouse across the chart. You'll see a data tip appear showing you the individual column values (see Figure 8).

    Flex supports automatic data tips on every chart type

    Figure 8. Flex supports automatic data tips on every chart type

    That's a good start but you can provide additional hints to the chart to help it format the data in the tips. The more information you provide, the easier it will be for your users to understand the data.

  3. Provide a name for your ColumnSeries element by setting the name attribute to something meaningful to your users:

    <mx:ColumnSeries yField="Widgets" name="Luxury Widgets">
    ...
    <mx:ColumnSeries yField="Blinkies" name="Mass Market Blinkies">

  4. Do the same for your Axis objects to label the values displayed in the data tip:

    <mx:horizontalAxis>
        <mx:CategoryAxis dataProvider="{zingerManufacturingData}" categoryField="Month" name="Month" />
    </mx:horizontalAxis>
    ...
    <mx:verticalAxisRenderer>
        <mx:LinearAxis name="Units Sold" />
    </mx:verticalAxisRenderer>

  5. Save your file and reload it in the browser. Once again try moving your mouse across the chart. You will now see a friendlier data tip (see Figure 9).

    Naming your axes and series can make your data tips more readable

    Figure 9. Naming your axes and series can make your data tips more readable

Taking It to the Next Level

At this point you have a nice-looking chart that gives your users an overall sense of the data trend as well as access to the individual values. In this last section, I show you how to push your application a little bit farther in each direction—customization, styling, and interactivity—to really show off what you can do with the Flex charting components.

A common interaction technique for a chart is to allow the user to drill down on the details of a particular item. For example, in our fictitious Zinger Corporation chart, a user may want to drill down on how many units were sold on each day of any particular month.

To make drill-down interaction possible, you can make use of a few new events in Flex charts. All Flex components broadcast the mouseOver, mouseOut, or mouseMove events when the mouse pointer enters its bounds, leaves its bounds, or moves while over its bounds. With charts, you can also use the more specific mouseOverData, mouseOutData, and mouseMoveData events to give you additional information about where the mouse pointer is with respect to the data being rendered inside the chart.

As you might expect, the mouseOverData and mouseOutData events are broadcast when the mouse moves over and away from a data point onscreen, while the mouseMoveData event is broadcast when the mouse pointer moves while over a data point. Additionally, a mouseClickData event is broadcast when the user clicks a data point in the chart.

Each of these new events comes with a reference to an mx.charts.HitData structure, which contains all the information that describes which data object it relates to, which series, how far the mouse pointer is, and so on. See the API reference (ASDoc) included with Flex 1.5 for more information.

Use the mouseClickData event to determine which month the user wants to drill down on, and fetch additional data for display:

  1. Add a mouseClickData event handler to your ColumnChart tag. This handler will load the drill-down data based on which specific month or column was clicked on. Typically you load the new data from one of the Flex network service types; for this tutorial, use a utility function to generate sample data. This function can be found in the zingerChartFunctions.as file included in the flex_tutorials directory:

    <mx:ColumnChart dataProvider="{zingerManufacturingData}" width="100%" height="100%" 
      showDataTips="true" 
     mouseClickData="getDrillDownDataForMonth(event.hitData.element.yField, event.hitData.item);" >

    After your application has loaded the drill-down data, you need to display it somewhere for your users to examine it. You might choose to drill down in place, replacing the current chart with a new one displaying the new data. Alternatively, you could load the data into a grid to display the actual values. In this application, add an additional chart to render the drill-down values side by side.

    Rather than using a simple column chart this time, bind the drill-down data into a more advanced combination chart. This chart will show columns for the individual unit sales count broken out by day, plus a trend line showing the running average sales count over a five-day period.

    To build combination charts in Flex 1.5, use the CartesianChart component, the base class for all the standard built-in chart types (except for the PieChart component). The CartesianChart component embodies almost all of the behavior you'll find in the various subtypes, with the added benefit that it makes no assumptions about the types of series objects contained within it.

  2. Add a new CartesianChart and Label component beneath your initial ColumnChart component, inside the Panel tag. This CartesianChart component will be bound to a new array, drilldownValues. The drilldownValues array is declared in the generateData.as file that accompanies this tutorial:

    <mx:Label id="drilldownLabel" text="{drilldownTitle}" fontSize="16" fontWeight="bold" />
    
    <mx:CartesianChart id="drillDownChart" width="100%" height="30%" dataProvider="{drilldownValues}" fontFamily="chartLabelFont"
    horizontalAxisStyle="hangingCategoryAxis" verticalAxisStyle="linedNumericAxis">
    
        <mx:horizontalAxis>
            <mx:CategoryAxis dataProvider="{drilldownValues}" categoryField="week" />
        </mx:horizontalAxis>
    
        <mx:horizontalAxisRenderer>
            <mx:AxisRenderer canDropLabels="true" />
        </mx:horizontalAxisRenderer>
    
        <mx:verticalAxisRenderer>
            <mx:AxisRenderer labelFunction="formatThousands" />
        </mx:verticalAxisRenderer>
    
        <mx:series>
            <mx:Array>
                <mx:ColumnSeries yField="sales" />
                <mx:LineSeries form="curve" yField="avg" >
                </mx:LineSeries>
            </mx:Array>
        </mx:series>
    
    </mx:CartesianChart>

  3. Save your file and reload it in the browser. Click one of the columns in the top chart. You should see your drill-down chart fill with data (see Figure 10).

    You can use the CartesianChart class to build more complex combo charts

    Figure 10. You can use the CartesianChart class to build more complex combo charts

    Next apply a little more styling to modify the appearance of the trendline in your drill-down chart. First change its color and size by setting a stroke. Similar to Fill objects, Stroke objects are used by the charting components to control the color, weight, and transparency of the lines and edges drawn when rendering the data.

  4. Add a Stroke object to the stroke style of the LineSeries object in your drill-down chart:

    <mx:LineSeries form="curve" yField="avg">
        <mx:stroke>
            <mx:Stroke weight="3" color="#82C9EB" />
        </mx:stroke> 
    </mx:LineSeries>

  5. Save your file and reload it in the browser. When you drill down on your data, you should see a nice blue trendline (see Figure 11).

    Stroke objects can modify the color, weight, and transparency of a chart line

    Figure 11. Stroke objects can modify the color, weight, and transparency of a chart line

Now for the grand finale. It's time to add some transition effects to your drill-down chart. Flex 1.5 introduces three new built-in effect types that are specific to the charting components: SeriesZoom, SeriesSlide, and SeriesInterpolate. These effects, which are only designed to be used on the various Series types in the Flex charting components, provide you with a wide variety of ways to transition the data onscreen when building interactive charts.

You can use these effects in your charting applications by attaching them to the hideDataEffect and showDataEffect triggers of the series of any chart. Here's what happens when a chart series detects a change in the underlying dataProvider property being rendered:

  1. It fires the hideDataEffect trigger to transition the old data values off-screen.
  2. It updates the axes, loads the new data, and calculates new rendering positions.
  3. It fires the showDataEffect trigger to transition the new data values back onscreen.

Now add a SeriesInterpolate effect to your drill-down chart to animate the change as new values are loaded:

  1. Add an Effect tag as a child of your Application tag, with a SeriesInterpolate definition inside:

    <mx:Effect>
        <mx:SeriesInterpolate duration="500" elementOffset="5" name="drillDownEffect" />
    </mx:Effect>

  2. Assign the new effect to the showDataEffect trigger of the Column and Line series in your drill-down chart:

    <mx:ColumnSeries yField="sales" showDataEffect="drillDownEffect" />
    <mx:LineSeries form="curve" yField="avg" showDataEffect="drillDownEffect" >

  3. Save your file and reload it in the browser. When you click a column, your drill-down chart should animate smoothly from one state to the next.

Where to Go from Here

This tutorial has really only scratched the surface of what you can do with the new charting components in Flex 1.5. Beyond the column and Cartesian charts used in this example, Flex ships with a bar, bubble, pie, line, plot, and area chart, along with their associated series objects. With each chart type you have myriad additional display options—whether to render stacked or overlaid, floating or based, with labels or without—to build exactly the chart your project requires.

Flex 1.5 also ships with a very comprehensive chapter describing the charting platform, explaining and expanding on many of the concepts outlined here. The new API reference also includes complete documentation of the variety of controls and options available on the charting controls.

Beyond that, the best way to get a handle on what's capable with the Flex charting components is to fire up the server and play around with them. If there's something you want to build with the charting components that doesn't seem possible, let us know! There's a wealth of charting features that we can add in future releases of Flex, and your requests and opinions will help guide how we prioritize them.

About the author

Ely Greenfield is an architect with the Flex team. He joined Macromedia as part of the Shockwave team in 1996. Ely likes to sit at home on weekends and chart the comings and goings of gophers in his backyard. Lately, he has started seeing XML in his dreams.