Learning Flex – Lesson 20, Creating Modular Applications

As your application gets larger, it could result in a pretty bulky swf that will take a long time to download and potentially cause users to get code for things they never use. There are also issues around re-using your code in different applications – if you just copy the source, it’s going to be a pain to maintain when you have bug fixes or enhancements for it.

Fortunately, there are ways to break up your code in Flex to reduce some of these issues.

Modules

Flex modules allow you to create a subset of code that can be loaded/removed at run time.  Say you’ve got an application that has features that only certain users have access to. You could provide the core of the app as your swf and then download the “special” functionality after a user of the correct type has logged in and asked to access that area. This saves everyone having to download code they may never  be able to access.

First of all you need to define your Module. The <mx:Module> tag defines the root of a module and you may have more than one of them in an application. It has a layout property that can be set to absolute, horizontal or vertical. More than likely, you’ll convert a current UI container class into a module so you can replace it’s tags with the Module ones.

FlexBuilder has functionality to aid with modules but if you’re using FlashDevelop, you’re going to have to do things a little differently. This post explains how to do that. Basically, you can specify compiler properties in an annotated comment (remember to do this within the <mx:Script> block if you’re Module is defined in MXML). The only addition I would make is to use the -source-path option to define the root of your source code so the compiler can find everything it needs to compile the module if you’ve got multiple folders within your source code.

The ModuleLoader class is a type of container that knows how to load and unload modules. As it’s a container, it can be added directly to the UI. It has a url attribute which specifies where to find the module swf at run time and can be given width and height attributes along with an id. The ready event is fired when the module is loaded and the object data of the module is available through the child property. The unloadModule() method can be used to remove the module.

A sample moduleLoader tag might look like this:

<mx:moduleLoader id="myModule" url="modules/myModule.swf" width="100%" height="100%" ready="handleModuleReady(event)"/>

In which case to set a property within my module I would cast the child to the module class to access the property like so:  MyModule(myModule.child).property=value;

You can also specify an error event handler to deal with problems trying to load your module. If you see an error something like this:

Error #1053: Illegal override of FlexModuleFactory in mx.core.FlexModuleFactory

Make sure you’re using the same  version of the Flex sdk for your module and your application (particularly if you’re using the FlashDevelop method covered above and you’ve got a project specific compiler specified – remember compiling the module with CTRL-F8 uses the default compiler).

Runtime Shared Libraries (RSL)

If you have common code that gets used between several of your applications, you can separate this out into a library and then either compile that in to each application or use it as an RSL. These can be cached on the client so it does not need to be downloaded every time the application is used.

It’s important to consider the size implications of using libraries – if you create ones that try to fit in too much, the overall size of your application at run time will go up as it will be including code from the shared library that it never uses.

Adobe has developed  special RSLs of the framework, data visualization and  data services classes so that they can be cached by the Flash player and any swf can use them, thus removing the need to compile in extra code and reduce the size of all apps. Thse special RSLs are digitally signed and have a .swz extension to ensure that only Adobe certified code is used.  They can be found in your SDK’s frameworks\rsls directory. Only Flash Player 9.0.115 and later can use the signed swz versions but there are swf versions available too.

To specify you wish to use  RSLs, you use the compiler -runtime-shared-library-path option. This is followed by the library file (swc) to compile against, the runtime location of the primary RSL (the swz), a policy file if needed (if this is your own unsigned RSL and you’re using it for a different domain) and the failover RSL (swf) runtime location for older Flash Players.

Here’s an example from my project:

-runtime-shared-library-path=C:\flex3sdk\frameworks\libs\framework.swc,framework_3.4.0.9271.swz,,framework_3.4.0.9271.swf

(I’m putting the framework RSLs in the same place as my application swf).

Creating a Flex Library

FlexBuilder has built in support for creating a Flex library (swc) file but for FlashDevelop you’ll need to use the command line compc compiler (there is a plugin for FlashDevelop but I couldn’t get it to work for me).

The best way to do this if you have several items is via a n xml configuration file.

You can then go to the bin directory under your SDK and run the command:

compc -load-config+=config-file.xml the += means in addition to the default config which makes sure you get access to relevant libraries.  Here’s an example configuration file:

<?xml version="1.0"?>
<flex-config xmlns="http://www.adobe.com/2006/flex-config">
  <output>C:\tools\flex\FlexGrocerLibrary\.\bin\FlexGrocerLibrary.swc</output>
  <use-network>true</use-network>
  <target-player>10</target-player>
  <warnings>true</warnings>
  <benchmark>false</benchmark>
  <compiler>
    <accessible>false</accessible>
    <allow-source-path-overlap>false</allow-source-path-overlap>
    <optimize>true</optimize>
    <strict>true</strict>
    <es>false</es>
    <show-actionscript-warnings>true</show-actionscript-warnings>
    <show-binding-warnings>true</show-binding-warnings>
    <show-unused-type-selector-warnings>true</show-unused-type-selector-warnings>
    <use-resource-bundle-metadata>true</use-resource-bundle-metadata>
    <verbose-stacktraces>false</verbose-stacktraces>
    <source-path>
      <path-element>c:\tools\flex\flexgrocerlibrary\src</path-element>
    </source-path>
    <external-library-path append="true">
      <path-element>C:\tools\flex_sdk_3.4\frameworks\libs\framework.swc</path-element>
      <path-element>C:\tools\flex_sdk_3.4\frameworks\libs\datavisualization.swc</path-element>
    </external-library-path>
  </compiler>
  <include-classes>
    <class>events.CategoryEvent</class>
    <class>events.ObjectDataEvent</class>
    <class>utils.Util</class>
    <class>valueObjects.Category</class>
    <class>views.MaxRestorePanel</class>
    <class>views.dashboard.ChartPod</class>
    <class>views.dashboard.ComparisonChart</class>
    <class>views.dashboard.SalesChart</class>
    <class>views.dashboard.TypeChart</class>
  </include-classes>
 <include-file>
    <name>downArrow.gif</name>
    <path>C:\tools\flex\FlexGrocerLibrary\src\assets\downArrow.gif</path>
</include-file>
<include-file>
    <name>upArrow.gif</name>
    <path>C:\tools\flex\FlexGrocerLibrary\src\assets\upArrow.gif</path>
</include-file>
 </flex-config>

Note the external library path entries for the framework libraries I’m referencing – the append attribute is to make sure these get added to any other libraries required. Make sure you specify these as external libraries rather than just libraries or they’ll get compiled in rather than just referenced.

Note also you can specify classes and asset files for inclusion.

Once this is run, you’ll have a swc file you can include as a library to your apps. To make it an RSL takes a little more work.  First of all, you need to extract the swf file from the swc using winzip or something similar, then you need to optimize it to remove debug information etc (you don’t need to do this if you’re building in your swc file as the compiler takes care of stripping that out when you generate a release build). You can find out how to optimize your RSL swf here.

Learning Flex – Lesson 19, part II More Chart Customization

Chart Legends

The <mx:legend> tag uses it’s dataProvider property to determine what to display. This should be the id of the chart you want the legend for. It has style attributes to determine labelPlacement and direction of layout. It uses the displayName property of a series to determine what to place in the label.

Axis Renderers

You can customize the layout of an axis by using an <mx:AxisRenderer>; tag. This has properties such as canDropLabels, canStagger, title etc. The axis property is used to define which axis the renderer is to be used for. AxisRenderer tags are children of either an enclosing <mx:horizontalAxisRenderers>; or <mx:verticalAxisRenderers> tag.

Vertical axis titles default to rendering top to bottom. To get the more normal bottom to top, you can use an AxisRenderer with the property verticalAxisTitleAlignment set to vertical (thanks to McQuillen Interactive for that gem).

Data Tips

All charts support a boolean property showDataTips that when set to true, will show a tool tip type box when the user mouses over an element. By default, this shows the x and y value but you can specify a function for the property dataTipFunction to customize what’s displayed. This function should accept an object of type mx.charts.HitData and return String. The HitData object has a property item typed to Object which contains the dataProvider item the user has moused over on the chart.

Click Events

If a user clicks in a chart, a chartClick event will be dispatched if no data point is found under the mouse or itemClick if there is an item within the radius specified by mouseSensitivity (default 3 pixels) for the chart. The chartClick event provides the chart the user clicked on, the itemClick event provides a property hitData of type HitData that contains the chart dataProvider item the user clicked on the chart.

Data Selection

You can enable data point selection by setting the property selectionMode on the chart to none, single or multiple. You can select multiple points either by dragging a box around a number of points with the mouse or clicking them while holding down the ctrl/command key.

The selectability of individual chart series can be turned on or off by using the boolean selectable property on the series in question.

The change property on a chart can be used to define a call back function that will be called when the selection in a chart changes. The event fired contains a hitSet property which is an Array of HitData objects corresponding to the chart points selected.

It’s also possible to programatically  select chart items by setting the selectedIndex property for a single point or selectedIndices Array property of a series for multiple points.

Chart Animations

Three built in animations are provided for charts. They are subclasses of the mx.charts.effects.SeriesEffects class and are specified for a series showDataEffect or hideDataEffect attributes.

  • seriesInterpolate – this effect moves the graphics that represent the existing data in a series to the new points. It creates a smooth animation between the two sets of data. This effect is only valid for showDataEffect.
  • seriesSlide – this effect slides the chart data into and out of the chart boundaries. If you use seriesSlide with the hideDataEffect trigger, the series slides from the current position to off screen in the specified direction. For showDataEffect, the effect is reversed, from the specified direction into the chart.
  • seriesZoom – this effect implodes and explodes the chart data into and out of the focal point specified.

Customizing Chart Appearance

You can set styles using CSS for the chart as a whole, individual series in a chart or the axes. Many style elements may also be set inline as attributes of the relevant MXML tag. For instance, you may use the <mx:stroke> tag to specify the style of a chart’s item renderer and <mx:lineStroke> to define a line chart’s lines or <mx:areaStroke> for an area chart.

Here’s a more customized version of the line chart I had in the previous post:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.charts.LinearAxis;

      [Bindable]
      public var notorious:ArrayCollection = new ArrayCollection(
      [ { money: 1000, problems: 1 },
      {money: 5000, problems: 2 },
      {money: 20000, problems: 4 },
      {money:100000, problems: 6 },
      {money:150000, problems:10 } ]);

      private function renderMoney(value:Number, previousValue:Number,axis:LinearAxis):String{
        return dollars.format(value);
      }
    ]]>
  </mx:Script>
  <mx:CurrencyFormatter currencySymbol="$" id="dollars" useThousandsSeparator="true"/>
    <mx:Style>
      LineChart {
         fontFamily:TimesRoman;
         fontSize:14;
     fontWeight:bold;
      }
    </mx:Style>

  <mx:Panel title="Notorious Line chart">
    <mx:LineChart id="myChart"
           dataProvider="{notorious}"
           showDataTips="true">
      <mx:series>
        <mx:LineSeries yField="money" xField="problems">
          <mx:lineStroke>
            <mx:Stroke color="0x33FF33" weight="7" />
          </mx:lineStroke>
        </mx:LineSeries>
      </mx:series>
    <mx:verticalAxis>
            <mx:LinearAxis id="vert" title="Money" labelFunction="renderMoney" />
        </mx:verticalAxis>
    <mx:horizontalAxis>
            <mx:LinearAxis title="Problems"/>
        </mx:horizontalAxis>
    <mx:verticalAxisRenderers>
      <mx:AxisRenderer axis="{vert}" verticalAxisTitleAlignment="vertical"/>
    </mx:verticalAxisRenderers>
        <mx:backgroundElements>
           <mx:Image
                source="@Embed('01-large-cash-money.gif')"
                alpha=".4"
           />
        </mx:backgroundElements>
    </mx:LineChart>
  </mx:Panel>
</mx:Application>

here’s a picture of the “improved” chart:

customized "Notorious" graph

(cash money image courtesy of   http://mindmillion.com/MONEY/)

For other chart components, you should take a look at Kap IT Lab or ILOG Elixir from IBM for charts that are free for non commercial use or the open source project Axiis if you want to construct your own data visualizations.

Learning Flex – Lesson 19, Visualizing Data

The Flex charting components are part of the Data Visualization component library, a separate  download found on the Flex SDK download page. Prior to Flex 3.4, components created with this library would show a trial watermark if used without a Flex Builder professional key. Version 3.4 no longer has this watermark but it’s my understanding that Adobe still expects you to have a professional license to use these components “for real”.

Flex supports the following chart types: area, bar, bubble, candlestick, column, high low open close (for stocks), line, pie and plot.

All these chart types have a similar construction – each chart type defines what kind of chart to display, has attributes for the data to render (dataProvider), whether tool tips are shown etc. Child tags then define axes, one or more series and styling elements.

Each chart must have a series defined before data will be rendered and each type of chart has it’s own series class that can be used (eg <mx:PieChart> has <mx:PieSeries>). Some charts allow you to mix different series types within the same chart. The series will define which field of the dataProvider is to be used for the chart.

The Axis class is required for any of the Cartesian charts to specify what data should be rendered for the horizontal and vertical axes. Subclasses are used to specify numeric (LinearAxis) or String based (CategoryAxis) data.

An AxisRenderer optionally defines how the axis will be shown (including labels, tick marks and title). CSS, text properties, label rotation and spacing (to allow labels to be dropped for readability) are items that can be specified by an AxisRenderer.

Other optional elements are backgroundElements for such things as background images and annotationElements for items like grid lines (both are subclasses of ChartElement).

Pie Charts

A PieSeries has a labelPosition style attribute which is used to define how labels are rendered for the chart. The available values are:

  • none – default, no labels
  • outside – draw labels around the boundary of the pie
  • callout – draw labels in two vertical columns on either side of the pie. The pie will shrink to accommodate them.
  • inside – labels are drawn approximately 70% along each wedge, labels will be shrunk to avoid interference. Labels shrunk below the calloutPointSize property are removed and larger wedge labels have priority if they overlap.
  • insideWithCallout – labels are drawn inside the pie but if they shrink below a legible size, they are converted to callouts.

The label will default to the data being rendered in the pie but this can be changed by using the property labelField to define an element in the dataProvider to use or labelFunction to specify a rendering function. This function should have the signature –

(data:Object, field:String, index:Number, percentValue:Number):String

The data is the dataProvider item being rendered, field is the name of the field in that item that’s being rendered, index is the index into the dataProvider for this wedge and percentValue is the percentage of the whole this wedge represents.

You may also specify a nameField property which defines the field of the dataProvider to use as the name of the wedge in tool tip objects and legends.

You can generate a doughnut or wheel chart (a hollow center) by specifying the innerRadius property on the PieChart. This is a number between 0 and 1 as a fraction of the entire pie’s radius.

Pie charts can be “exploded” so wedges are drawn partially outside the pie. To explode all wedges uniformly, set the explodeRadius property on the PieSeries to a value between 0 and 1 representing the fraction of pie radius to use. To explode wedges by different amounts, define an array of values between 0 and 1 and pass this to the PieSeries perWedgeExplodeRadius property.

Here’s the code for a simple pie chart:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      [Bindable]
      public var snowSports:ArrayCollection = new ArrayCollection(
      [ { sport: "Ski", percent: 48 },
      {sport: "Snowboard", percent: 46 },
      {sport: "Snowshoe", percent: 4 },
      {sport: "Other", percent: 2 }]);
    ]]>
  </mx:Script>
  <mx:Panel title="Pie Chart">
    <mx:PieChart id="myChart"
           dataProvider="{snowSports}"
           showDataTips="true">
      <mx:series>
        <mx:PieSeries field="percent"
                labelField="sport"
                labelPosition="callout"/>
      </mx:series>
    </mx:PieChart>
  </mx:Panel>

</mx:Application>

here’s a picture of the chart created:

Pie Chart Example

Line Charts

For a LineChart or other Cartesian graph, you specify the yField property of your series type to define which field of the dataProvider is to be used for for the y axis plot and an xField property to determine the x axis location. If you omit the xField, Flex arranges the data points in the order of the data in the dataProvider.

The axis labeling is defined using <mx:horizontalAxis> and <mx:verticalAxis> tags. Flex supports 4 types of axes:

  • CategoryAxis – map data of discrete values such as cities, teams etc. A CategoryAxis can optimize it’s rendering if it’s data is more static
  • LinearAxis – map numeric data to the axis. This can be used to provide valid numeric ranges, numbers to skip etc
  • LogAxis – map logrithmic data to an axis (labels for powers of 10)
  • DateTimeAxis – map time based values to an axis and format the labels. You may also disable particular dates from being shown (only show workdays or weekends for instance).

It is possible to define multiple axes for a chart. To do this, you create axes for each series being displayed.

Here’s the code for a simple line chart:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      [Bindable]
      public var notorious:ArrayCollection = new ArrayCollection(
      [ { money: 1000, problems: 1 },
      {money: 5000, problems: 2 },
      {money: 20000, problems: 4 },
      {money:100000, problems: 6 },
      {money:150000, problems:10}]);
    ]]>
  </mx:Script>
  <mx:Panel title="Notorious Line chart">
    <mx:LineChart id="myChart"
           dataProvider="{notorious}"
           showDataTips="true">
      <mx:series>
        <mx:LineSeries yField="money"
                xField="problems"/>
      </mx:series>
    </mx:LineChart>
  </mx:Panel>
</mx:Application>

here’s a picture of the chart created:

Notorious BIG Chart

I’ll continue with charts in part 2 shortly.