Skip to content
July 25, 2011 / Daniel Freeman

Tutorial 7: MadComponents AMF Service Test Drive for Mobile

In this final MadComponents tutorial we’re going to look at the third and final Flash Builder 4.5 tutorial (from here), and do it the MadComponents way.  Adobe’s Flex Test Drive for Mobile: Build a mobile application in an hour, takes you through building an AMF web service, and then building a mobile client application that connects to that service.  We’re going to build the same application using MadComponents.

AMF is a binary format used to serialise ActionScript objects.  A binary AMF packet is usually significantly smaller than REST XML or SOAP packet.  Hence AMF services are typically much faster.

This tutorial assumes that you have some expertise in building web services, using PHP, Coldfusion, or Java.  But if you’d rather do a tutorial where the web service already exists, then have a go at building a twitter client.

We recommend that you first take a look at the first six parts of this tutorial series, which will bring you up-to-speed with using MadComponents.

Here is what our application will look like, one the right, next to the original Flash Builder 4.5 version:-

So let’s get started, and go though the steps to build our MadComponents Mobile application:-

 

Set Up The Web Service

1. You’ll need to set up an AMF web service.  You can do this using PHP, Coldfusion, or Java.  You can download the resources from here.  Follow the instructions in the original Flash Builder tutorial.  Just follow the section entitled “Setup for PHP”, “Setup for ColdFusion”, or “Setup for Java”, depending which server-side technology you want to use.  Don’t go further than the section entitled “Step 2: Create a Flex Mobile project.  We’re going to do everything else the MadComponents way.  If having looked at the Flex tutorial, you want a peek at how much easier it is using MadComponents, look here.

Creating the project

Create a new MadComponents project.  We’ve already learnt how to do this in previous tutorials, but I’ll copy the steps here to save you looking it up.

2.  If you haven’t done so, download MadComponents.  Familiarise yourself with the demos, and see what it can do.

Flash users can download the project from http://code.google.com/p/mad-components/downloads/list .

Flex (Flash Builder) users can check-out the latest .swc and MadComponents demos from the Subversion repository at: http://code.google.com/p/mad-components/source/checkout .

Please click “like” on the Google code site, if you like MadComponents.

Although the pictures below depict MadComponentsLib0_5.swc, you’ll need the latest version.  Currently MadComponentsLib0_5_7.swc (or a newer version when available).

3A. If you’re using Flash.  You need to find your libs folder.  For Flash CS5, you’ll find this directory at the following path, from your Flash application folder:-

Adobe_Flash_CS5/Common/Configuration/ActionScript_3.0/libs

Once you’ve found this folder, put MadComponentsLib into it:-

3B. If you’re using Flash Builder (Flex), just include the .swc into the project in the usual way.

( Project -> Properties -> ActionScript Build Path -> Library Path -> Add SWC Folder… lib )

4. Create a new ActionScript application.  Call it “MadTwitterTrends”.  Either Adobe AIR ActionScript applications, or a browser based application.

5. For an Adobe AIR application, set your window size to 300 x 454.  (not applicable to a browser application).  In Flash, you’ll see this in the properties panel:-

( On a real device, it will adapt to the screen size, but in simulation – it assumes 300 x 454 pixels )

6. Now save your application.  In Flash, make sure you’ve made a new folder for it.

7. In Flex, you’ll already have a top ActionScript class.  In Flash you’ll need to create one.  File->New->ActionScript File.  Call it “MadTestDrive.as”.  Save it in the same folder as MadTestDrive.fla .

8. In Flash, create a link between the timeline project, and the ActionScript file.  (No need to do anything in Flex).  Click on the first cell on the timeline, and in the Actions Panel, type:

new MadTestDrive(this);

Now we’re ready to write some code.

 

Model for AMF Service

We’re going to connect to our AMF service.  The EmployeeService.getEmployeesSummary method returns an array of objects.  Here is an example of an object:-

{id:7, firstname:”Elizabeth”, lastname:”Roe”, title:”Senior Experience Designer”, photofile:”eroe.jpg”}

We’re going to represented each of these objects by a row in a list, using a custom item renderer like this:-

We use a model tag to map the object properties, onto the item renderer:-

<model url=”http://localhost/MobileTestDrive/gateway.php&#8221; service=”EmployeeService.getEmployeesSummary” action=”loadAMF”>
      <firstname>label</firstname>
      <lastname>label</lastname>
      <photofile>image</photofile>
      <title/>
      <id/>
</model>

title, and id are mapped onto components of the same name.  (Actually, there is no component named id, but the row data will contain this property).  photofile is mapped onto image.  Note that both firstname and lastname are mapped onto the same label.  MadComponents will concatenate the two strings, so that “Elizabeth” and “Roe” becomes “Elizabeth Roe”.

Here is our first program with the list, model, and custom renderer:-

package
{
	import com.danielfreeman.madcomponents.*;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	public class MadComponentsAMF0 extends Sprite {

		protected static const AMF_LIST:XML = 	<list id="list" sortBy="lastname">

			<model url="http://localhost/MobileTestDrive/gateway.php" service="EmployeeService.getEmployeesSummary" action="loadAMF">
				<firstname>label</firstname>
				<lastname>label</lastname>
				<photofile>image</photofile>
				<title/>
				<id/>
			</model>

			<horizontal>
				<imageLoader alignV="centre" id="image" base="http://localhost/TestDrive/photos/">40</imageLoader>
				<vertical>
					<label id="label"/>
					<label id="title"><font size="10" color="#999999"/></label>
				</vertical>
			</horizontal>

		</list>;

		public function MadComponentsAMF0(screen:Sprite = null) {
			if (screen)
				screen.addChild(this);

			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

			UI.create(this, AMF_LIST);
		}
	}
}

Notice how we’ve ordered the list by lastname.  sortBy=”lastname”

 

 

Searching

We can easily add a search field to this list.  Within the <list> tags, put:-

<search field=”label”/>

With field=”label”, we don’t need to write any code.  When the user types text into the search field, it filters the list rows by the label field.


Detail Page

When the user clicks on a list row, we want to display a detail page with more details, such as cell phone number, office phone number, email, and office.

<columns id=”detail” widths=”80,100%”>
   <imageLoader id=”photofile” base=”http://localhost/TestDrive/photos/”>80</imageLoader&gt;
   <vertical gapV=”0″>
      <label><b>Title</b></label>
      <label id=”title”>{FONT}</label>
      <label/>
      <label><b>Cell Phone</b></label>
      <label id=”cellphone”>{FONT}</label>
      <label/>
      <label><b>Office Phone</b></label>
      <label id=”officephone”>{FONT}</label>
      <label/>
      <label><b>Email</b></label>
      <label id=”email”>{FONT}</label>
      <label/>
      <label><b>Office</b></label>
      <label id=”office”>{FONT}</label>
      <label/>
   </vertical>
</columns>

This time, we’ve utilised ids that correspond to the property names in the EmployeeService.getEmployeesByID service.  (photofile, title, cellphone, officephone, email, office).  So we don’t have to specify any mappings within the <model> tags.  We can just write:-

<model url=”http://localhost/MobileTestDrive/gateway.php&#8221; service=”EmployeeService.getEmployeesByID”/>

Notice there’s no action attribute.  We’re going to control it using actionscript.

 

Putting it together

We’re going to put the list and the detail page into a <navigation> container.

<navigation id=”navigator” autoTitle=”label”>
      {AMF_LIST}
      {DETAIL}
</navigation>

autoTitle=”label” tells the navigation container to automatically take a property called label from the list item clicked, and set the navigation container’s title to this string.

We are going to load the detail page under actionscript control.  When the user clicks on a list row, we want to extract the id for that row, and pass it as a parameter to the EmployeeService.getEmployeesByID service.

protected function pageChange(event:Event):void {
       if (_navigator.pageNumber == 1) {
             _detail.data = {image:null};   //clear old image in detail page
             _detail.model.loadAMF(“”, “”, [_navigator.row.id]);
      }
      else {
            _navigator.title = “”; 
      }
}

The heart of this code sample is the line:-

_detail.model.loadAMF(“”, “”, [_navigator.row.id]);

The first parameter is the URL, the second parameter is the service.  But as we’ve already specified both of these in the model tag, so we just pass an empty string “”, to utilise our defaults.  The third parameter is an array of parameters.  _navigator.row.id is the id of the list row clicked.

Putting all the pieces together, here is the complete code:-

package
{
	import com.danielfreeman.madcomponents.*;

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;

	public class MadComponentsAMF extends Sprite {

		protected static const AMF_LIST:XML = 	<list id="list" sortBy="lastname">

			<model url="http://localhost/MobileTestDrive/gateway.php" service="EmployeeService.getEmployeesSummary" action="loadAMF">
				<firstname>label</firstname>
				<lastname>label</lastname>
				<photofile>image</photofile>
				<title/>
				<id/>
			</model>

			<search field="label"/>

			<horizontal>
				<imageLoader alignV="centre" id="image" base="http://localhost/TestDrive/photos/">40</imageLoader>
				<vertical>
					<label id="label"/>
					<label id="title"><font size="10" color="#999999"/></label>
				</vertical>
			</horizontal>

		</list>;

		protected static const FONT:XML = <font size="14"/>;

		protected static const DETAIL:XML =  <columns id="detail" widths="80,100%">

				<model url="http://localhost/MobileTestDrive/gateway.php" service="EmployeeService.getEmployeesByID"/>

					<imageLoader id="photofile" base="http://localhost/TestDrive/photos/">80</imageLoader>
					<vertical gapV="0">
						<label><b>Title</b></label>
						<label id="title">{FONT}</label>
						<label/>
						<label><b>Cell Phone</b></label>
						<label id="cellphone">{FONT}</label>
						<label/>
						<label><b>Office Phone</b></label>
						<label id="officephone">{FONT}</label>
						<label/>
						<label><b>Email</b></label>
						<label id="email">{FONT}</label>
						<label/>
						<label><b>Office</b></label>
						<label id="office">{FONT}</label>
						<label/>
					</vertical>
				</columns>;

		protected static const NAVIGATION:XML = <navigation id="navigator" autoTitle="label">
								{AMF_LIST}
								{DETAIL}
							</navigation>;

		protected var _detail:UIForm;
		protected var _navigator:UINavigation;

		public function MadComponentsAMF(screen:Sprite = null) {
			if (screen)
				screen.addChild(this);

			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

			UI.create(this, NAVIGATION);

			_detail = UIForm(UI.findViewById("detail"));
			_navigator = UINavigation(UI.findViewById("navigator"));
			_navigator.addEventListener(Event.CHANGE, pageChange);
		}

		protected function pageChange(event:Event):void {
			if (_navigator.pageNumber == 1) {
				_detail.data = {image:null};
				_detail.model.loadAMF("","",[_navigator.row.id]);
			}
			else {
				_navigator.title = "";
			}
		}

	}
}

What’s next ?

That was the last in this series of tutorials.  I set out to show how the could be utilised to create powerful and lightweight mobile applications – but also how MadComponents can simplify the development process – making them a viable alternative to Flash Builder.  I’m bound to be blogging more about MadComponents, so watch this space, and keep the feedback and questions coming.

 

Further reading

To find out more about MadComponents, you can read the other related threads on this blog, or download the documentation or examples for Flash at http://code.google.com/p/mad-components/downloads/list

There are also lots of code examples to view or check-out from the subversion repository at http://code.google.com/p/mad-components/source/checkout

Please subscribe to this blog if you want to catch subsequent posts about MadComponents.

 

Extended Madness

You may have noticed ExtendedMadness on the download site.  I’ll be blogging about it soon.  ExtendedMadness library is an alternative to MadComponents.  It does everything that MadComponents does, but has lots more components.   (Take a look at the MadComponentsDesktop.as example on the code repository.)   I wanted to keep the core MadComponents library light, and that’s why further components have been included in the bigger ExtendedMadness library.

Please Blog about MadComponents

Please help spread the word about MadComponents and blog or twitter about it.  It would be great if you could contribute more examples and tutorials, or even just talk about MadComponents, and what it can do.

24 Comments

Leave a Comment
  1. Danny Froberg / Sep 16 2011 4:09 pm

    Authentication against a ZendAMF service would usually be done like;

    employeeService.setCredentials(‘test’,’test’);
    employeeService.setRemoteCredentials(‘test’,’test’);

    how would one go about doing it in MadComponents?

    • Daniel Freeman / Sep 17 2011 1:33 am

      I haven’t implemented an authentication API, so if you wanted to do this, don’t include an action property in your model.

      (i.e., don’t do this: <model …. action=”loadAMF”> )

      Instead, you’d need to instigate the load from actionscript.

      Essentially, copy the loadAMF() method of Model.as (don’t worry about the _action=”loadAMF” line – you can omit this), and insert the authentication lines before the connect. You’ll also need to duplicate the loadAMFList() method in your own class. No need to dispatch the event though. You can link this back into Model’s dataAMF setter, and everything should work fine.

      ( http://code.google.com/p/mad-components/source/browse/trunk/MadComponentsLib/src/com/danielfreeman/madcomponents/Model.as )

      Ok, I probably need to provide an API for authentication. I’m not that much of an expert on AMF though, and I’m aware that there was an issue with HTTP headers not working as expected in Flex. So now that MadComponents is open-source, if you want to suggest a specific code patch, I can see about incorporating it in the next version.

      Otherwise, I can just put a 4th optional parameter onto the Model’s loadAMF() method. A NetConnection object, passed as a parameter, allowing you to set headers before you call loadAMF().

      • Daniel Freeman / Oct 6 2011 7:56 am

        In version 0.6.1, I have indeed added a fourth, NetConnection, parameter to the loadAMF() method.

  2. Albulescu Cosmin / Oct 6 2011 7:34 am

    Hi, very nice post. Very usefull

  3. Federico / Oct 19 2011 3:07 pm

    Hi. I can’t use Zend. I can use only httservice and a php file then load data from Mysql. How can i fill a list ?
    Tank you.

  4. Zvero / Oct 20 2011 8:28 am

    Hi Daniel,

    Is it possible to use MadComponents in conjunction with labraries like Cairngorm? And if so, how?

    I really like to seperate all my business logic into their own seperate as-files.

    • Daniel Freeman / Oct 20 2011 12:30 pm

      I think MadComponents lends itself to separation of MVC. But I haven’t tried Cairngorm. It would be great if someone else could crack this, (Or Robotlegs if not Cairngorm) and blog about it. The approach shouldn’t be too different from the separation of concerns technique:- http://caffeineindustries.com/?p=234

  5. Siva / Nov 23 2011 7:43 am

    Hello Daniel,it is a nice post. But i am using Java as backend. I am having testdrive application in my tomcat server.Inside testdrive folder i am having WEB-INF/classes/services/EmployeeService.class file.

    In action script project i declared model as follows

    label
    label
    image

    should i need to include sendModel as follows

    Thanks.

    • Daniel Freeman / Nov 23 2011 11:18 pm

      With AMF, you don’t use the sendModel. Just set up the model tag, with no action attribute, and if you’re passing parameters up to the server, put them in an array for the third parameter of loadAMF…

      loadAMF(url:String = “”, service:String = “”, parameters:Array = null, netConnection:NetConnection = null):void

      I haven’t tried with Java, just PHP. Sorry. But you can probably use Flash Builder to test that the AMF server is working. The Flex tutorial I link to talks about that.

  6. Carl / Dec 2 2011 10:45 pm

    Hey Daniel. Judging from the demos, MadComponents is a gorgeous library.

    I’m having problems leveraging the AMF demo to my own CFC, though. I’m in Flash Pro CS5.5, using ActionScript3, hitting a ColdFusion8 server running under IIS on a Windows 2003 Server.

    My CFC returns “fullname” as a column name in the query. And I have the AMF set up as

    protected static const AMF_LIST:XML =

    label

    ;

    But when the component is rendered, I’ll I get is one row with a value of “Undefined”.

    I’ve called the same CFC directly in ActionScript with

    var AMFGateway:NetConnection = new NetConnection();
    AMFGateway.objectEncoding = ObjectEncoding.AMF3;
    AMFGateway.connect(“http://mydomain.com/flashservices/gateway/”);
    AMFGateway.call(“Employees.getAllEmployees”, new Responder(onResult,onFault));

    function onResult(evt:Object):void{
    trace(“RESULT: “+ObjectUtil.toString(evt));
    }

    and the trace dump looks perfect…

    RESULT: (Object)#0
    serverInfo = (Array)#1
    [columnNames] (Array)#2
    [0] “fullname”
    [cursor] 1
    [id] (null)
    [initialData] (Array)#3
    [0] (Array)#4
    [0] “Jane Doe”
    [serviceName] “PageableResultSet”
    [totalCount] 1
    [version] 1

    So I’m fairly sure the gateway and CFC are set up correctly.

    My query is very straight forward

    SELECT employees.fullname FROM employees LIMIT 1

    (I’m limiting during testing, but no matter if I limit it to 1 or 10, I always only get one row in the MadComponent list with the value of “Undefined”).

    I’ve also called the webservice from a pure CFM page and get proper results as well.

    Any ideas?
    -Carl

    • Daniel Freeman / Dec 3 2011 4:09 pm

      Not sure. You could try looking at my AMF code, and see how it differs from what you’re doing…

      http://code.google.com/p/mad-components/source/browse/trunk/MadComponentsLib/src/com/danielfreeman/madcomponents/Model.as

      I see you’ve got AMFGateway.objectEncoding = ObjectEncoding.AMF3;

      I don’t have that line. Is it important? You could try setting it up and passing it as the final parameter to loadAMF().

      var AMFGateway:NetConnection = new NetConnection();
      AMFGateway.objectEncoding = ObjectEncoding.AMF3;
      myList.model.loadAMF(url, “Employees.getAllEmployees”, null, AMFGateway);

      Let me know if the ObjectEncoding.AMF3; fixes it. If so, I may need to put this in my code.

      • Carl / Dec 4 2011 9:24 pm

        Thanks for the response, Daniel.

        The ObjectEncoding doesn’t look like it’s necessary. I’ve just always used it.
        But if I comment it out in the ActionScript for my direct test, I still get a proper result.

        The only other difference I see when comparing your project to mine is that you’re using the Apache Derby driver, while I’m using data coming out of an actual MySQL database. I wonder if it might have something to do with the MySQL database/table type (currently InnoDB), or the character encoding (which is currently “latin1 — cp1252 West European”… not sure where that came from, that’s just what it was when I inherited the database)?

        Regardless, I worked around the issue for the time-being by having my webservice method loop through the query and stuff all the rows into an Array of Structs, then return that array rather than a query. Once I did that, the list displays perfectly with no other changes to the AS code.

        Sure would like to know why the Query version doesn’t work, though.

        Thanks again!
        -Carl

  7. Carl / Dec 5 2011 8:21 pm

    Hey Daniel,

    This question may be a bit off-topic, but it came up while I was trying to follow this tutorial to it’s logical conclusion and add buttons in the detail screen to dial and/or send a SMS message to the employees phone(s).

    a) How best to get the phone number from the current detail’s data. Right now, I have:

    _phonecall = UIButton(UI.findViewById(“phonecall”));
    _phonecall.addEventListener(UIButton.CLICKED, phoneCallEmployee);

    protected function phoneCallEmployee(event:Event):void {
    var phoneNumber=””;
    try{
    phoneNumber=event.currentTarget.parent.parent.model.dataAMF.phone;
    } catch(dataError:Error){
    phoneNumber=””;
    }
    navigateToURL(new URLRequest(“tel:”+phoneNumber));
    }

    But I’m not convinced that’s efficient or reliable. In straight Flex, I’d use dataResult.lastResult.phone… but that doesn’t seem to be exposed in MadComponents

    b) How to set an icon to the button. Tried _phonecall.setStyle(“icon”,myDialIcon) … but setStyle doesn’t appear to be an exposed property in MadComponents, either.

    c) And as a UI nicety… how best to hide the dial/SMS buttons if the device does not have the capability (tablet, or non-dialing device).

    Any pointers would be appreciated!
    -Carl

    • Daniel Freeman / Dec 6 2011 6:41 am

      model.dataAMF is the MadComponents equivalent to dataResult.lastResult

      So what you have is ok, but a bit messy.

      You could set this:-

      _detail = UIForm(UI.findViewById(“detail”));

      and say _detail.model.dataAMF.officephone

      . . . . .

      Alternatively, because you’re displaying this in a label inside the detail page, you could say:-

      _officephone = UILabel(UI.findViewById(“officephone”));

      then inside your button handler:-

      navigateToURL(new URLRequest(“tel:”+_officePhone.text));

      . . . . .

      You can skin a button. Not with setStyle(), that’s for Flex, (or JavaScript). Take a look at:-

      https://madskool.wordpress.com/2011/06/23/tutorial-2-images-and-nine-patch-images-in-madcomponents/

      ( A tip about skinned buttons… If you set the text of the button to “”, the skin size isn’t changed. But if you set it to ” ” (space), or a character string, the skin will scale to the size of the button )

      . . . . .

      You can use the visible attribute to hide or show a button…

      _phonecall.visible = false;

      • Carl / Dec 7 2011 12:03 am

        Thanks again, Daniel. Your suggestions work great.Much appreciated.

        I knew I could call visible=false on the button, and that hides the element, but the it doesn’t collapse the layout (ie – the “footprint” of the button still allocates real estate on the UIForm, whether it’s visible or not.

        I was hoping there was a way to do more than just hide the button. I wanted it to go away.

      • Daniel Freeman / Dec 7 2011 5:29 am

        Ok, I’ve given you a way to do something like Flex includeInLayout in version 0.6.7. I’ve described it in this blog post:-

        https://madskool.wordpress.com/2011/12/05/10-new-madcomponents-features/

  8. Tom Dagosa / Dec 6 2011 4:52 pm

    Thank you! Thank you! These are the components that really should’ve been included in the standard library for AIR for Mobile in the first place!

    Question: I’m using AMF, as in this tutorial. But there doesn’t seem to be listenable events dispatched for when the model is fully loaded or errored. That would be helpful (to remove a splashscreen or preloader). Or is there a way??

    • Daniel Freeman / Dec 7 2011 5:24 am

      try:-

      _list.model.addEventListener(Model.LOADED, loadCompleteHandler);

      _list.model.addEventListener(Model.ERROR, loadErrorHandler);

  9. Lance / Mar 8 2012 1:31 am

    When setting up the test database, I went the ColdFusion route. If this example, you show how to set up a call to a database using PHP, but not ColdFusion :

    Can you explain how to connect to the ColdFusion cfc using MadComponents?

    • Lance / Mar 8 2012 1:32 am

      Sorry, left off the part of your example I was referring to:

      model url=”http://localhost/MobileTestDrive/gateway.php” service=”EmployeeService.getEmployeesSummary” action=”loadAMF”

Trackbacks

  1. Tutorial 7: MadComponents AMF Service Test Drive for Mobile … | Flash | Adobe-Tutorial.com
  2. AS3 MadComponents Tutorial Series – Starting Out | Caffeine Industries

To discuss MadComponents/MC3D, join the Facebook group!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: