29 January 2010

Link Adobe Flex 3.5 Chart to ColdFusion webservice

As promised, here is a (not very) short blog about how to get Adobe Flex 3.5 to consume a coldfusion cfc based webservice and display it as a chart.

As mentioned in my previous post it's EPIC that Adobe decided to put charting in the SDK. I use the SDK (coz I'm poor) and with it and a little patience you can achieve some amazing Flex apps.

So to do this you'll need the Flex 3.5 SDK, you'll need the Flex data visualization pack and obviously you'll need coldFusion.

I'm sorry for the code here, Google blogger doesn't make posting code easy.

1)The cfc:


<cfcomponent>

<cffunction name="getPersonByDept" access="remote" returntype="query" output="false">
<cfargument name="deptname1" type="string" required="true" />
<cfargument name="deptname2" type="string" required="true" />
<cfargument name="deptname3" type="string" required="true" />
<cfset var myQuery = queryNew('counttt,deptName') />

<cfquery name="myQuery" datasource="jayLocal">
select count(personid) * 100 as [counttt], [deptName]
from [person]
group by [deptName]
having
[deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname1#" />
or [deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname2#" />
or [deptName] = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.deptname3#" />
</cfquery>

<cfreturn myQuery />
</cffunction>

</cfcomponent>



I know, I know, the function isn't great, i don't need to pass in 3 separate params, but this is just for illustration purposes. Take this cfc and save it as latestone.cfc in your coldfusion webroot or in a webservices folder. Whatever you fancy. You're going to want to be careful of Application.cfc because it can eat your webservices. You should be able to then open up the wsdl in your browser.
http://localhost/webserrvices/latestone.cfc?wsdl
You should see a bunch of xml data. This is coldfusion doing the hard work for you and describing the webservices offered at this wsdl.
The datasource and query are linked to a ms sql server, a single table called person. It should all be pretty straightforward.



2)The test.cfm:

OK so far so good, lets create a quick test.cfm and make sure the webservice works so far. Create the cfm file and paste in the following code:


<cfobject type="JAVA"
action="Create"
name="factory"
class="coldfusion.server.ServiceFactory">
<cfset RpcService = factory.XmlRpcService />
<cfset RpcService.refreshWebService("http://localhost/webserrvices/latestone.cfc?wsdl")>


<cfinvoke webservice="http://localhost/webserrvices/latestone.cfc?wsdl" method="getPersonByDept" returnvariable="hello2">
<cfinvokeargument name="deptname1" value="development" />
<cfinvokeargument name="deptname2" value="sales" />
<cfinvokeargument name="deptname3" value="marketing" />
</cfinvoke>



<cfdump var="#hello2#" />





OK quick bit of explaining. First and foremost the first paragraph, the JAVA stuff may be the single most useful piece of code when working with webservices in CF. CF does some aggressive caching when it comes to webservices. So if you're in dev mode this code forces CF to keep re-looking. DO NOT leave this code on a production server as it'll slow it right down. But for now it's useful. The second bit of code is a simple cfinvoke based on a webservice consumption.


3)The mxml:

Finally the Flex part. OK So assuming you've downloaded Flex, and tried a helloWorld already you should be able to take the following code and paste it into a .mxml file. Then compile it using the mxmlc command as before. Here's the mxml Flex code:




<?xml version="1.0" encoding="utf-8"?>

<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
viewSourceURL="src/HelloWorld/index.html"
horizontalAlign="center" verticalAlign="middle" initialize="initApp()" backgroundColor="white">


<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.utils.ObjectUtil;
import mx.core.Application;
import mx.validators.Validator;
import mx.managers.CursorManager;
import mx.events.ListEvent;
import mx.controls.DataGrid;
import mx.controls.dataGridClasses.DataGridListData;
import mx.utils.ArrayUtil;


[Bindable]
private var myDebpts: ArrayCollection;



public function initApp():void {
//onload send webservice
WSgetTasks.getPersonByDept.send();
}


private function server_fault(event:FaultEvent):void{
//on error, show...an error message!
Alert.show(ObjectUtil.toString(event.fault));
}


private function returnLoadTasks(evt:ResultEvent):void {
//when webservice returns, we grab the results and stick it in the bindable myDebpts
myDebpts = WSgetTasks.getPersonByDept.lastResult;
}


]]>
</mx:Script>

//This is the crus of the app, the webservice call. the operation tells it what method to call and the request stuff is the parameters, that simple!
<mx:WebService id="WSgetTasks" wsdl="http://localhost/webserrvices/latestone.cfc?wsdl" fault="server_fault(event);" result="returnLoadTasks(event)">
<mx:operation name="getPersonByDept">
<mx:request>
<deptname1>development</deptname1>
<deptname2>sales</deptname2>
<deptname3>marketing</deptname3>
</mx:request>
</mx:operation>
</mx:WebService>


<mx:Panel title="Users By Department" height="300" width="400">
//the chart
<mx:ColumnChart id="myChart"
dataProvider="{myDebpts}"
height="100%"
width="100%"
showDataTips="true">


//this bit is confusing but it works!
<mx:horizontalAxis>
<mx:CategoryAxis dataProvider="{myDebpts}" categoryField="DEPTNAME" />
</mx:horizontalAxis>

<mx:series>
<mx:ColumnSeries yField="COUNTTT" xField="DEPTNAME" displayName="Department Name" />
</mx:series>
</mx:ColumnChart>


<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>


</mx:Application>





There we go. Hopefully the comments provide some clarity, again I'm sorry Google blogger does such a poor job of showing the code properly. To talk you through it briefly, on app initialize the initApp() is called, this triggers the webservice. The mx:WebService calls the cfc which we defined as a webservice. It uses the mx:operation and the mx:request as parameters, obviously you can bind these to a text field or something. Then on completion the result runs the returnLoadTasks() function which loads the lastresult into the bindable arraycollection. This arraycollection is bound to the chart with the appropriate x and y axis names.

4)Display the generated flash file:

Done. Compile this and flex will spit out a lovely flash file. Copy this to your web directory somewhere and add the following to output the flash file:



<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="100%" height="100%">
<param name="movie" value="wschart.swf">
<param name="quality" value="high">
<embed src="wschart.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="100%" height="100%"></embed>
</object>




No comments: