Skip to end of metadata
Go to start of metadata

Consuming web services with Flex

In this hands-on session, we will create a new flex application that will show us a list of session attendees with their name, location, and age.  First we will use a local xml file, then move on to retrieving the same data from a web service on an SAP NW04s system.  If you follow along and don't fall asleep, the final project should show a table of attendee data and a pie chart showing some statistics.  It assumes no prior experience with Flex.  You will need to download and install Flex Builder 3 from Adobe's website before starting.  This session will be given originally at TechEd 2007 Community Day in both Las Vegas and Munich by Ed Herrmann, Dan McWeeney, and Matthias Zeller.  While you are working through, please feel free to update this tutorial with anything that needs to be added or clarified.   The final application will look like the screen shot below, or you can check out a working version at http://sandbox.ewherrmann.com/flexhandson/

Let's get this party started.  Create a new Flex project File->New->Flex Project named "flexhandson" using default project options.
Go to Design mode of your newly created file "flexhandson.mxml", and set the width of your project to 510, and set the height to 600.  You can resize by dragging the project canvas or by changing the settings manually in the Flex Properties section located on the right.

Creating our first components

Now we need to add some components to our project.  While still in Design mode, from the Components section located on the left, add a new Label to the top center of your project canvas.  The Label is located in the Controls folder, and can be added by selecting the component and dragging it onto the project canvas  
Click on the new Label. From the Flex Properties section, change the text to "Flex Attendee List" and the font size to 16.
From the same Components section, add a new DataGrid to your project.  Resize the datagrid to almost the full width of your project and a height of about half.  Show your datagrid some love by giving it a name.  In the Flex Properties section, set the ID to myDataGrid.
Still awake?  If so, your project should look a little something like this so far.

Before we continue, let's build a quick XML component and bind some test data to our datagrid.  Go to the Source view of your project and you will see the source mxml tags for your application, the label, and the datagrid. We entered our components in  by using the drag and drop visual features on the design view, but the same thing can be accomplished by manually typing in the code on the source view.  While in the source view create a new XML component by typing in <mx:XML>. After hitting enter, you will notice the closing tag has been added for you. Give the new XML component an ID, by adding ID="myXML" inside of the opening <mx:XML> tag.
Inside your XML tag, enter some quick XML style test data.  The result should be similar to this:

<mx:XML xmlns="" id="myXML">
	<Data>
		<Person>
			<Name>Ed Herrmann</Name>
			<Location>New Jersey</Location>
			<Age>34</Age>
		</Person>
		<Person>
			<Name>Dan McWeeney</Name>
			<Location>New Jersey</Location>
			<Age>27</Age>
		</Person>
		<Person>
			<Name>Matthias Zeller</Name>
			<Location>California</Location>
			<Age>34</Age>
		</Person>
	</Data>
</mx:XML>

Now inside of your DataGrid tag, we need to add a DataProvider.  In Flex It is easy to bind data directly to the component allowing for the datagrid to always reflect the data in your provider.  Some popular data providers of flex components include, but are not limited to XML, XMLList, XMLCollection, and CollectionArray.  For this example, we will use the XML that we just created in the previous step. 
To bind the data, add a new attribute called dataProvider inside your opening <mx:DataGrid> tag. Since we want to use the <Person> node for our data set, set the dataProvider to your XML document using:

dataProvider="{myXML.Person}>"

The "{}" symbols let flex builder know that we want to bind the data directly to our DataGrid.
Before we can test, we need to do one more thing by adding the proper field names to our columns. Inside each <mx:DataGridColumn> tag, change the headerText and dataField attributes to use our XML data nodes.

<mx:DataGrid x="10" y="42" width="490" height="237" id="myDataGrid" dataProvider="{myXML.Person}">
	<mx:columns>
		<mx:DataGridColumn headerText="Name" dataField="Name"/>
		<mx:DataGridColumn headerText="Location" dataField="Location"/>
		<mx:DataGridColumn headerText="Age" dataField="Age"/>
	</mx:columns>
</mx:DataGrid>

Now time to see the early seedlings of our hard labor.  Save your application and run it to see what you get.  The green Run icon is located on the left side of the top icon bar about the fourth one over.  
The browser will open with your flex application running inside.

_Tip: If you didn't see the data you were expecting, double check to see if your dataProvider and dataField attributes match the text exactly from your XML._  Flex/ActionScript is case sensitive and will not be able to match the fields up otherwise.
Well, what do you think? Pretty neat, eh?  With very little coding effoert, we already have a table showing XML data inside a flash application running in the browser.  Good stuff for sure, however, not very useful when we typed our data directly into the code. 

Call an HTTP service to a local XML file

Next, let's use a local file to simulate a simple HTTP web service request.  First we need an XML document.  From the Flex Navigator on the left, right click on the src folder of your application and create a New->Folder called "assets".  This step isn't required, but it's considered a good practice to have a separate folder to store your application files and images.  Once the assets folder has been created, right click on the new folder and create a New->File called AttendeeList.xml.  Cut and paste your XML data from your flex application into the new file.  Include the XML data only, as we don't need the <mx:XML> tags anymore.  You can also delete them from your application now, because we won't be using it anymore for this session.
Create new <mx:HTTPService> with:

id="MyService" url="./assets/AttendeeList.xml" resultFormat="e4x"

The url tells the service where to send the request.  In this case, we are using a local file url. The result format E4X is the standard for creating ECMAscript for XML.  It allows easy parsing of xml results with dot notation.  For example, our Location node from our XML document can be easily accessed as myXML.Person.Location. 
Since our old XML component has changed, we now need to bind the data grid to your new service.  Change the dataProvider attribute of  the data grid to

dataProvider="{MyService.lastResult.Person}"

"lastResult" is always the return object of an HTTP Service that you want to check, and "Person" again, is the XML node we want to use.
Now try to save and run your program and check the results of your table.  What do you mean no data showed up?  Oh yeah, I may have forgotten to tell you that just because you created the service, doesn't mean it magically knows to send it's request to the URL.   We have to call the send method of the HTTPService class for the service to invoke the call.  This can be done anywhere programmatically that fits the architecture of the application, but for our case, we just want it to send the request as soon as we load up the application.  For this we will use the creationComplete event from the main Application instance (<mx:Application> tag).  We need to set it up so that the send method of the HTTP Service is called when the creation of the application is complete.  To do this, add creationComplete="MyService.send()" to your mx:Application open tag.  Now that that's set, save and run the program again.
Now that should look better! We now have data coming in from a simulated HTTP service provided by a local XML file. 

Consuming an ABAP web service

Although we have certainly taken a leap forward, we should now try to actually call a real web service. Since this is an SAP event, we might as well hook it up to some good ol' ABAP.  We have created a standard custom table with a standard custom function module in SAP that returns the same type of attendee data.  We then wrapped it in a web service so we could call it from Flex. So let's give it a try.  If you are reading this and following along on the web, you will have to skip this section because I only have my ABAP stack up and running here on the local conference network...sorry, but you can skip down to the next section where we add charts, that's pretty fun I suppose.
For time's sake, we have already created the table, function module, and web service in our SAP NW04s system to be consumed from Flex.  If you want more details, check out the ABAP Web Services section on SDN:  https://www.sdn.sap.com/irj/sdn/go/portal/prtroot/docs/webcontent/uuid/e07cd5e4-ed76-2910-2daa-e5a4bbfbbb3e#section2
You can get details of the web service by viewing it's WSDL (Web Service Definition Language) document. The URL for our ABAP web service can be viewed from the wsdl address provided at the end of this document.
The good news is that Flex makes it fairly simple to create a web service.  By giving it the web service WSDL, Flex can do all the dirty work and other SOAP processing for us (If you think it's weird that I just used SOAP and dirty work in the same sentence, you may want to check out the definition of SOAP on wikipedia).
Create a new <mx:WebService> component inside your application with ID= "abapWS".  Now there are a few things that we are going to have to set up here to make this work.  First we need to set the wsdl and endpointURI properties.  Since our wsdl and uri properties are faily large strings with some funky characters in it, we will want to URI encode them first.
The components we have used so far have all been flex components, but we are also able to use pure ActionScript in our app as well.  All ActionScript code needs to be contained inside of  a script and CDATA tag.  Go ahead and enter a new tag of <mx:Script> after the opening <mx:Application> and before the tags for your newly created components.  You should notice after typing <mx:Script> and then enter, the closing tag and necessary <![CDATA]> tags magically appeared. Now that's much love right there, thanks Adobe!
We need to add wsdl and endpointURI attributes to our new web service, but since the URI string contains symbols that may cause trouble, we should URI encode them into a new string. Now that  we can put all of our raw ActionScript code inside our script/cdata tags, we will declare some new string variables with our data:

[Bindable] private var abapWSDL:String = "INSERT WSDL ADDRESS IN HERE";
[Bindable] private var wsEndpoint:String = "INSERT ENDPOINT URI HERE";

The [Bindable] decoration allows us to, yes you guessed it, make our new strings bindable.  So let's bind them to our web service attributes. In your mx:WebService tag, set the wsdl and endpointURI attributes.  You can bind the data directly and actually make the encode an inline function call. Since we want to make the results of our web service call bindable to our data grid, you also need to set the attribute makeObjectsBindable="true".

<mx:WebService id="abapWS" wsdl="{encodeURI(abapWSDL)}" endpointURI="{encodeURI(wsEndpoint)}"
		makeObjectsBindable="true">
</mx:WebService>

Note: The endpoint URI does not have to be set if the WSDL file contains the correct information.  If you look at our WSDL file, you will see that it points to http://localhost:8000 So for obvious reasons, we need to explicitly specify our endpoint URI.
Web services use the concept of operations to communicate what action you want to perform while calling the web service.  If you look at the WSDL file for the ABAP web service, you can see that our only operation is "Zattendees".  Since this is what we are looking for, we need to add it to our web service.  Add a new <mx:operation> component in between your opening and closing <mx:WebService> tags.  Set the name to "Zattendees" and resultFormat to "e4x".

<mx:operation name="Zattendees" resultFormat="e4x"/>

Now we are almost ready, but we still need to call the web service somewhere.  As before, we can add it to the creationComplete event in our Application tag.  We need to call the send method of our web service operation that we just added: creationComplete="abapWS.Zattendees.send()".
For the last step, we need to bind our web service results to our data grid.  By looking at either the result set with the debugger or analyzing the WSDL, we can see that the structure of the return XML has changed slightly from our original HTTP service call.  Instead of abapWS.lastResult.Person, we have a new XML structure of abapWS.Zattendees.lastResult.Attendees.item. Set the data grid attribute

dataProvider="{abapWS.Zattendees.lastResult.Attendees.item}"

Our data fields still look the same, so we should be OK there.  Save and run to test.  Hopefully, you got the results you were looking for. 

Adding a pie chart

If you got this far, you are doing good.  So let's add make our app a little more spiffy and add a pie chart that will show us the results of our XML data.  Go back to design mode of your application and add a PieChart from the Components section. When you drag the pie chart into your app, you will be prompted for some options.  Name the chart "myPieChart" and uncheck the Include Legend checkbox. Everything else is fine, so finish and resize your chart to fit in the bottom half of your application area.
To do a quick test, let's bind some numeric data set to the chart to see some results.  Since we already have a numeric field called "Age", it will have to do for now.  Switch back to Source mode, and bind your web service call to your chart, the same way you did for the data grid.  For the <mx:pieSeries> component we have set some attributes to let it know which fields to use.  Set field="Age" labelField="Name" labelPosition="callout".  Now we have come a long way since the beginning of this tutorial, so I am going to let you figure out what these attributes do on your own.

<mx:PieChart x="10" y="287" id="myPieChart" width="490" height="303" dataProvider="{abapWS.Zattendees.lastResult.Attendees.item}">
	<mx:series>
		<mx:PieSeries field="Age" labelField="Name" labelPosition="callout"/>
	</mx:series>
</mx:PieChart>

Save and run. 

Parsing data to get some meaningful results

Cool, we have data! Too bad the data we have really doesn't make much sense in this context.  In order for it to have any meaning, we will need to parse and manipulate the data ourselves.  To do this, we are going to have to drop down a level and write some more ActionScript. We need to create a new function and then set it up to be called after the web service results have arrived. 
Inside the Script/CDATA tags, create a new private function called parseData that takes in a ResultEvent as an argument.

private function parseData(event:ResultEvent):void{

}

To get this function to be called when the web service has results, set the result event of the web service component to call your function and send it the event.

<mx:WebService id="abapWS" wsdl="{encodeURI(abapWSDL)}" endpointURI="{encodeURI(wsEndpoint)}"
		makeObjectsBindable="true" result="parseData(event)">
	<mx:operation name="Zattendees" resultFormat="e4x" />
</mx:WebService>

To test this and see our event data, set a breakpoint inside your empty function, and run the application in debug mode.   
To set a breakpoint, double-click on the left side of the line number located on the left of each source line.  If it worked, you will see a small dot appear indicating the breakpoint has been set.  To remove, you can double-click the same area again.

 

Once the breakpoint has been hit, you will be automatically switched to the debug perspective.  From the Variables tab, expand the event variable and see what you got.  As you can see, it has our web service results stored in an XMLList called result.
_NOTE: If you get the error "1046: Type was not found or was not a compile-time constant: ResultEvent", you need to import the proper library for your application to understand the definition of the type ResultEvent._  Add this line inside your Script/CDATA tags: 

import mx.rpc.events.ResultEvent;

Normally, you may only need to use this data locally, so you can throw the results into an Array or ArrayCollection and have your way with it.  For this session, we want to learn how to parse and create an new XML file from web service results.  This could be useful if you ever needed to send back XML or have a composite web service that called other web services to send back a mashup result set.
A useful data set to see in the chart may be the number of attendees that are in each different location. To get this information, we need to collect the results into a summarized array and then create a new XML data set to bind to our chart.
Create a new XML variable to hold the new results data and that can be bound to your pie chart.

private function parseData(event:ResultEvent):void{
	//check results
	if (event.result != null){
		//build an array to store our totals for each location
		var locationArray:Array = new Array;
		for each (var node:XML in event.result.Attendees.item){
			if (locationArray[node.Location] is Number){
				locationArray[node.Location]++;
			} else {
				locationArray[node.Location] = 1;
			}
		}

		//build the new XML file with our data
		locationXML = new XML;
		locationXML  = <Data/>;
		for (var loc:String in locationArray){
			var locNode:XML = new XML;
			locNode = <Location/>;
			var itemNode:XML = new XML;
			itemNode = <Name>{loc}</Name>;
			locNode.appendChild(itemNode);
			itemNode = new XML;
			itemNode = <Value>{locationArray[loc]}</Value>;
			locNode.appendChild(itemNode);
			locationXML.appendChild(locNode);
		}
	}
}

Set and bind your pie chart dataProvider to use the new XML result set with

dataProvider="{locationXML.Location}"

In the <mx:PieSeries> component, keep the labelField to use "Name", but change field="Value".  Save and run. 

Adding a little pizzazz

If all is well, you now have your data grid filled with data and your pie chart showing the percentage of attendees that are from each location.  To add a little pizzazz to our app, we can do a few quick additions before we wrap it up. 
It would be nice to actually see the numbers that actually make up each slice of the pie chart.  So instead of using the labelField for our labels, we can use a labelFunction instead.  Create a new private function that will take in the appropriate arguments and return a better looking label:

private function getLabel(data:Object, field:String, index:Number, percentValue:Number):String{
	// a labelFunction allows you to calculate and manipulate the data before being shown
	return data.Name + "\n" + data.Value;
}

Now remove the labelField attribute from your <mx:PieSeries> component, and add labelFunction="getLabel".  This will call our function and let us provide a label programmatically.  Save and run.
Pretty neat, eh?  Since you have made it this far, I leave you with one more bonus that just gets you cool points.  Add this new effect component somewhere outside of your <mx:PieChart>.

<mx:SeriesInterpolate id="interpolateIn" duration="2000"/>

Now to show the new visual effect, add the attribute

showDataEffect="{interpolateIn}"

 to the <mx:PieSeries> component.
Save and Run.  Awww, it's the little things in life that make us happy.  Anyway, congratulations, you are well on your way to being a Flex guru.  Good luck on your most rewarding journey.

Full Source Code to Final Application

The working version for calling a local XML file can be found at http://sandbox.ewherrmann.com/flexhandson/; To view source, visit the link, right click on the flash application, and select "View Source".

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="510" height="600" creationComplete="abapWS.Zattendees.send()">
	<mx:Script>
		<![CDATA[
			import mx.rpc.events.ResultEvent;
			[Bindable]
			private var abapWSDL:String = "http://192.168.1.102:8000/sap/bc/srt/rfc/sap/zflexattendees?wsdl&client=001";
			[Bindable]
			private var wsEndpoint:String = "http://192.168.1.102:8000/sap/bc/srt/rfc/sap/zflexattendees?client=001";
			[Bindable]
			private var locationXML:XML;

			private function parseData(event:ResultEvent):void{
				//check results
				if (event.result != null){
					//build an array to store our totals for each location
					var locationArray:Array = new Array;
					for each (var node:XML in event.result.Attendees.item){
						if (locationArray[node.Location] is Number){
							locationArray[node.Location]++;
						} else {
							locationArray[node.Location] = 1;
						}
					}

					//build the new XML file with our data
					locationXML = new XML;
					locationXML  = <Data/>;
					for (var loc:String in locationArray){
						var locNode:XML = new XML;
						locNode = <Location/>;
						var itemNode:XML = new XML;
						itemNode = <Name>{loc}</Name>;
						locNode.appendChild(itemNode);
						itemNode = new XML;
						itemNode = <Value>{locationArray[loc]}</Value>;
						locNode.appendChild(itemNode);
						locationXML.appendChild(locNode);
					}
				}
			}

			private function getLabel(data:Object, field:String, index:Number, percentValue:Number):String{
				// a labelFunction allows you to calculate and manipulate the data before being shown
				return data.Name + "\n" + data.Value;
			}

		]]>
	</mx:Script>

	<mx:WebService id="abapWS" wsdl="{encodeURI(abapWSDL)}" endpointURI="{encodeURI(wsEndpoint)}"
		makeObjectsBindable="true" result="parseData(event)">
		<mx:operation name="Zattendees" resultFormat="e4x" />
	</mx:WebService>

	<mx:DataGrid x="10" y="42" width="490" height="237" id="myDataGrid" dataProvider="{abapWS.Zattendees.lastResult.Attendees.item}">
		<mx:columns>
			<mx:DataGridColumn headerText="Name" dataField="Name"/>
			<mx:DataGridColumn headerText="Location" dataField="Location"/>
			<mx:DataGridColumn headerText="Age" dataField="Age"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Label text="Flex Hands On Attendees" fontSize="16" fontFamily="Verdana" fontWeight="bold" right="130.5" left="130.5" top="10"/>
	<mx:SeriesInterpolate id="interpolateIn" duration="2000"/>
	<mx:PieChart x="10" y="287" id="myPieChart" width="490" height="303" dataProvider="{locationXML.Location}">
		<mx:series>
			<mx:PieSeries field="Value" labelFunction="getLabel" labelPosition="callout" showDataEffect="{interpolateIn}"/>
		</mx:series>
	</mx:PieChart>
</mx:Application>

Web Service Resources (for attendees only)

Other Information

  • For an excellent video tutorial, check out this episode of Rich Heilman on the ABAP Freak Show

4 Comments

  1. Unknown User (e7n8g2x)

    Excellent Wiki, just what I was looking for.  Is there any chance you could provide the ABAP code/ table structure details so I can get this working on my system.  I am trying with my own RFC but the WSDLs that SAP create are large and difficult to trawl through.  Thanks!

  2. Unknown User (th6khh9)

    Nice article but missing three very important pieces of the puzzle

    1. How to perform authentication with the web services from Flex

    2. How to send a request that includes variables including tables

    3. Binding non data grid controls like Text input fields

    Would be grateful if you could include this information for the benefit of all

  3. Former Member

    Hi,

    it seems we can use Flex Builder Beta 3 only for a limited time as a trial, what shall we do after the trial? Is it possible to get a official licence for the Beta 3?

    Regards,

    Hua 

  4. Unknown User (ygdgfgn)

    Hallo many thanks,

    but 

    1- we need more details for the integration between SAP and Flex, what did u exactly do

    2- WSDL page not found