Building a Flex S-Control Mash-Up

Image:header_technote.gif

I had a lot of fun recently working on an Force.com project that let me combine several cool technologies and concepts, including Adobe's Flex, AJAX, and in a small way, the Model-View-Controller (MVC) design pattern. The project ended up being a showcase of what you can do with Flex and AJAX on the Force.com platform as well as a clean example of MVC (at least as I understand it), so I wanted to tell the story here for the benefit of anyone interested in learning more. Of course, I've included links to all the code as well, so you can easily see my development process and profit from any code that you can re-use. I plan to package this app and post on the AppExchange soon after Winter '07 release.

By way of short history, I've spent a few years now developing applications using Ajax and Force.com. While I love the power and ease of use that this combination has to offer, I confess that I've spent way too much time carefully constructing HTML to match the Force.com look and feel. This is not too hard, but it's way too time consuming, and going back later to make minor changes is enough to make my fingers cramp up. Creating S-controls using the MVC approach would ease a lot of this pain, but it didn't seem like you could do that with AJAX and Force.com. Or could you?

Get It Now - with this private AppExchange package.

The Project

The project sounded straightforward: Create a tool to present rich data analysis, in this case a chart or two, using data from a Force.com Account and related Opportunities. But as usual, the team was looking for more than just a graph, it had to have a bit of wow factor. We had demoed some similar things at Dreamforce '06, so I knew it could be done. I would not really be breaking ground, I just wanted to learn it and document what I found out.

Enter Flex, Adobe's Flash-based application and scripting platform. Flex has both a powerful scripting language, a rich set of User Interface elements and a very impressive charting engine. If you haven't looked into what Flex can do, read on and then get ready to try it yourself. It's a robust technology and well worth checking out. At the end of this article I've posted the srcs for everything I've done, which should save you some time.

Getting Started

I startd by installing the Flex language SDK. I admired but didn't use their builder environment as I tend to be a bit of an old-school guy, and the SDK is free, so this article will only use the compiler, not the Eclipse Builder. Then, since I wanted to use the Flex charting engine, I installed the Chart SDK that Adobe provides for a small licensing fee. After downloading the SDKs, I got them setup and compiled the samples.

I then jumped directly to the Flex Chart example and started to study the scripting portion. It looked easy enough to understand, but I realized that I could do all these things from JavaScript in my S-Control. So I skipped implementing anything in the script portion of the Flex file, instead focusing on the XML-based UI language, which offers a fine set of charts. I selected a pie chart and a column chart for this project.

This is when I first realized that this S-Control was unique in that it was the first time I could actually see a clean separation of data, logic, and UI in the design. I'm really not an expert on MVC, but by my definition, this clean separation was getting our design pretty close to the MVC approach. In this case Model is the salesforce Force.com data object (with it's built in security, reporting, etc.), Controller is my AJAX JavaScript S-Control (using Force.con AJAX 8.0 Toolkit), talking to the Data Model, and View is the User Interface, which in this case was 100% contained in the Flex-based SWF.

The View

Once I had the first SWF UI built, I didn't have to recompile it until i wanted to change the colors or position, which, since this was a prototype, was rarely. All the real work to control the UI and Data was done in the AJAX. This made for a clean "separation of powers".

To make this all work, I started the MXML file with an application declaration and the FABridge library, which looks like this

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
	<fab:FABridge xmlns:fab="bridge.*" /> <!-- flex ajax bridge  -->
    <mx:Script>

Here is a portion of the User Interface code so you can get a feel for what the Flex MXML language looks like, this defines the outer box and the Pie chart.

<mx:HDividedBox width="100%" height="100%">
	<mx:Panel id="panel" title="Please wait ..." height="100%" width="100%">
        <mx:PieChart id="chart" 
        	height="100%" width="100%"
            paddingRight="5" 
            paddingLeft="4" 
            showDataTips="true" > 
            
            <mx:series>
                <mx:PieSeries id="chart_series"
                	labelPosition="callout" 
                	labelFunction="getWedgeLabel">

A bit lower down is the code to add a Column Chart

         <mx:ColumnChart id="ColumnChart" 
		width="100%" height="100%" 
		showDataTips="true" >
		<mx:horizontalAxis>
			<mx:CategoryAxis categoryField="Name"/>
		</mx:horizontalAxis>

This made for a very simple MXML file, easy to understand and held all UI elements that i would need, and none of the controlling logic.


Image:FlexPieChart.jpg

The finished User Interface is shown in the screen shot above. By clicking on the pie chart on the left, the column chart will change to show the drill down of opportunities that go into each stage. The pie chart is a visualization of the query Opportunities at this account grouped by stage.

The basic query can be applied to many other standard or custom relationships in your database, you would only need to change a few lines of code to make this work for a custom object.

Controlling Logic

Next, I wanted to link the two chart elements, so that callbacks on the pie chart clicks will reset the data for the column chart, thus offering a "drill down" type of experience. There is a good argument for putting logic in the SWF file, but in this case I wanted to do all my control programing from the S-Control.  This choice was made for two reasons, first because I knew it would be faster for me to work in JavaScript than learning Flex script language and secondly because it was faster to make changes using Eclipse and saving the S-Control (no recompile needed).

This worked out very well, and had me thinking that I'd like to do more my programing like this, where Model, Controller and View were cleanly separated.

Development Process

The process goes something like this: Model your data; Create the Custom Objects ( if needed ), then visualize the app by constructing a simplified UI in Flex; Compile it and launch it in a browser to see how it looks, all with no coding yet. At this point the UI and the Data Model are not connected, and each can be "run" and reviewed separately. The data model can be tested by writing reports to ensure that the relationships are correct and the desired information can be summarized in the required ways.

The UI can even be reviewed to make sure that it provides a proper experience and meets the client's expectations by emailing the link to the SWF file to the client or team. They can see the UI and make change requests even before you finish coding the control logic.

Connecting the Parts

Now all that remains is to connect the UI and data model using logic which is contained in the S-Control. This is done partly by a clever piece of code offered on the Adobe Labs site, called Flex Ajax Bridge. This bridge consists of two parts and implements a wrapper around the ExternalInterface.call() call in Flex. This functionality allows the Flex UI to send events to my S-Control when the user click on elements. Inside the Flex src file I include the fab.* bridge implementation, and inside my S-Control I include the fab.JS file. Once configured the interface is very easy to understand and easy to use.

This S-Control will be installed in the Account Page as an inline S-Control by dragging it directly onto the page layout in a new page section that holds just this one S-Control. I set the height to 250 in the page layout and the Flex component will resize itself in both height and width nicely. Resizing is one less thing I need to worry about when using Flex components.

Main S-Control

The main S-Control brings things together, this begins by including the AJAX toolkit and the Adobe Flex Bridge. Note: here the FlexAjaxBridge is stored in another S-Control which is of type Snippet:

<script src="/soap/ajax/8.0/connection.js"></script>
<!--  snippet holds the src for FlexAJAX bridge -->
{!INCLUDE($SControl.FlexAjaxBridge)}

I then compile the SWF file and load it up to salesforce as the file attached to my S-Control. The attached file is accessed using the JavaArchive merge field. To cause the UI to load when this S-Control is run, we add into the html body of the scontrol the <object> tags, with paramaters to auto-play the flash UI, and reference to the JavaArchive merge field in the correct place ( embed src=)

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="PieChartExample" width="100%" height="100%"
 codebase="https://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
<param name="movie" value="{!Scontrol.JavaArchive}" /> 
<param name="quality" value="high" /><param name="play" value="true" />
<param name="bgcolor" value="#f3f3ec" /><param name="allowScriptAccess" value="always" />
<embed src="{!Scontrol.JavaArchive}"  play="true"  bgcolor="#f3f3ec" width="100%" height="250" name="PieChartExample" align="middle"
 loop="false" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer"></embed>
</object>

I can then write Javascript functions to control data and UI:

function sfdcQueryCallback(queryResult) {

  // how to set a property across the bridge
  flexApp.panel().setTitle( chartTitle ); 

  // this field must be set, used to proportion pie
  flexApp.chart_series().setField('Amount'); 

  // pass data array to the chart, it will then draw
  flexApp.chart().setDataProvider(oppsByStage);  

to get my on click functions, i use the Flex Chart object model to add a listener which specifies my JavaScript callback function

 // register for click callback by adding an event listener on a pie item
 // ask for the itemClick event to get the data we need
 flexApp.chart().addEventListener('itemClick', explodePieOnClick);

 // respond to clicks on the column chart also
 flexApp.ColumnChart().addEventListener('itemClick', jumpColumnChartClick);

On Load

I simply add an init callback to let my code run upon init (bridge init) and the scontrol has only to read the database using AJAX toolkit and load data into the UI widgets

function sfdcOnLoad() {
  flexApp = FABridge.flash.root();  // set a global for the bridge

  // launch a query to load the initial chart data
  sforce.connection.query(query,sfdcQueryCallback); 
}

</script>
</head>
<body onload="FABridge.addInitializationCallback('flash',sfdcOnLoad);">

Summary

I hope i've demonstrated a simple idea built using the Force.com Platform and applying a bit of the MVC design lesson. In the end I was able to quickly solve a somewhat common problem of representing your sales data in a new and interesting way. The whole thing only took a few days to build, and result is a unique and rich user interface element that can add visual impact to the data it describes.

Compile Notes

When compiling the FAB into the MXML files, you may benefit from this tip passed on to me recently. Add the FAB path to your mxmlc compile line, in your version of a makefile.

 
mxmlc -source-path+=c:\FAB\dist\src\ -use-network sfdcchart.mxml
 

Where c:\FAB\dist\src\ is the location for the FAB source files


Also, the FAB toolkit throws this warning, which you can safely ignore.

Loading configuration file C:\Flex_SDK_2\frameworks\flex-config.xml
C:\FAB\dist\src\bridge\FABridge.as(340): col: 11 Warning: Duplicate variable definition.
 
                for(var i:int = 0;i < xAcc.length();i++) {
                        ^

Source Code

The following links will allow you to view the sources used to produce the above s-control

--Ron Hess Jan 30 2007 (CST)