Flex Mobile Development – Passing Data Between Tabs – Part 2 (includes source)

August 9, 2011By 23 Comments

This is Part 2 of a 2-part series on passing data between tabs in a Flex Mobile tabbed application. See Part 1 for more details on what this series is about as well as how you could use a tightly coupled approach to achieve this. This post will cover how to share data across your tabbed mobile application using a Dependency Injection Framework. Note: I’m using Spicefactory’s Parsley framework in this example, however the concept is the same across other dependency injection frameworks such as Swiz, Robotlegs etc…

A better approach of handling the data within your tabbed mobile application versus using a tightly coupled approach as shown in Part 1 is to use a Dependency Injection (DI) framework to inject the data object into the view where it’s needed. For those that have never used a DI framework (or any for that matter), do not let the idea scare you, it’s actually quite easy to implement and will make your development so much easier and cleaner once you understand what it’s doing. Do note however, this post only scratches the surface of the framework, I advise you to check out their website and docs to get a better understand of what the framework can do.

For my very simple example using Parsley, I first need to set up a configuration file that defines the context of objects to manage for the application. ‘Manage’ in this case means that the framework will create and destroy the object automatically, perform automatic injection and make it available for messaging. I simply create an MXML component named Config.mxml with a root of Object where I”m managing just one object for this simple example – my Smurf data object that will be a shared object across the application. Here’s what my Config.mxml looks like:

Parsley Config File Source Code

<?xml version="1.0" encoding="utf-8"?>
<fx:Object xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns="http://www.spicefactory.org/parsley" xmlns:model="model.*">
	<!--   
		This is a parsley specific file that says which objects I want to manage... 
	-->   
	<fx:Declarations>
		<model:Smurf/>   
	</fx:Declarations>
</fx:Object>

Now below is my main application file where you can see some Parsley specific syntax including a metadata tag for inject, a ContextBuilder tag which specifies my Config file just shown above, and a Configure tag that tells Parsley to look for a managed object to be created/injected etc. If that tag is removed, there is no indicator for Parsley to match one of it’s managed objects. This tag is used in all views that I want the Smurf object to be injected into. If you remove the Configure tag, you will get a null pointer reference when trying to set the fname and lname properties on the smurf variable since no automatic creation was performed by Parsley.

Main Application Source Code

<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
								  xmlns:s="library://ns.adobe.com/flex/spark" 
								  xmlns:model="model.*" xmlns:parsley="http://www.spicefactory.org/parsley"
								  applicationComplete="tabbedviewnavigatorapplication1_applicationCompleteHandler(event)">
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			import mx.events.FlexEvent;
			
			[Inject]
			[Bindable]
			public var smurf:Smurf;    
			
			protected function tabbedviewnavigatorapplication1_applicationCompleteHandler(event:FlexEvent):void
			{
				// Set initial data
				smurf.fname="Papa";
				smurf.lname="Smurf";  
			}
			
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<parsley:ContextBuilder config="Config" />  
		<parsley:Configure/>
	</fx:Declarations>
	<fx:Style source="Styles.css"/>
	<s:ViewNavigator id="v1" label="Tab 1" width="100%" height="100%" firstView="views.viewstack1View"/>
	<s:ViewNavigator id="v2" label="Tab 2" width="100%" height="100%" firstView="views.viewstack2View"/>
	<s:ViewNavigator id="v3" label="Tab 3" width="100%" height="100%" firstView="views.viewstack3View"/>	
</s:TabbedViewNavigatorApplication>

Next is the source for my first tab view, where I am also injecting the smurf object and specify the parsley:Configure tag, so whatever data is in the smurf object currently will be displayed. Also, since I’m using the bidirectional binding reference syntax @{} around the smurf properties, the data will also be directly updated when anyone types into the text components and available wherever the smurf object is injected:

Tab 1 View Source Code

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="Tab 1" xmlns:model="model.*" 
		xmlns:parsley="http://www.spicefactory.org/parsley" backgroundColor="#104E8B">
	
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			[Inject]
			[Bindable]
			public var smurf:Smurf; //shared object managed by Parsley
			
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<!--Configure dispatches an event that tells Parsley you want to manage this view (so injection will occur).-->
		<parsley:Configure />		
	</fx:Declarations>
	
	<s:layout>
		<s:VerticalLayout paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" gap="10" 
						  horizontalAlign="center" verticalAlign="middle"/>
	</s:layout>
	
	<s:Label text="Tab 1 Data"/>
	<s:HGroup verticalAlign="middle">
		<s:Label text="First Name"/>
		<s:TextInput text="@{smurf.fname}" width="180"/>
	</s:HGroup>
	<s:HGroup verticalAlign="middle">
		<s:Label text="Last Name"/>
		<s:TextInput text="@{smurf.lname}" width="180"/>	
	</s:HGroup>
</s:View>

In the screenshot below, you see the data that was set on the smurf object in the main application complete handler:

Tab 1 Shown with Initial Data

Since bidirectional binding is used in the above view (via the @{} syntax), any text that I type into the those fields will be written into my smurf object, so if I update the data in tab 1 and then click on another tab that also references that smurf object, I will always see the latest changes.

Tab 1 Updating Data…

Tab 2 Shows Latest Updates

Tab 2 View Code

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="Tab 2" xmlns:model="model.*"
		xmlns:parsley="http://www.spicefactory.org/parsley" backgroundColor="#336699">
	
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			[Inject]
			[Bindable]
			public var smurf:Smurf; //shared object managed by Parsley 
		 
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<parsley:Configure />
	</fx:Declarations>
	
	<s:layout>
		<s:VerticalLayout paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" gap="10" 
						  horizontalAlign="center" verticalAlign="middle"/>
	</s:layout>
	<s:Label text="Tab 2 Data"/>
	<s:TextArea text="Changing data here will affect the data associated with the other tabs since the object instance is shared using the Parsley dependency injection framework."/>
	<s:HGroup verticalAlign="middle">
		<s:Label text="First Name"/>
		<s:TextInput text="@{smurf.fname}" width="180"/>
	</s:HGroup>
	<s:HGroup verticalAlign="middle">
		<s:Label text="Last Name"/>
		<s:TextInput text="@{smurf.lname}" width="180"/>	
	</s:HGroup>
</s:View>

Now any other tab or viewstack that contains a view with the smurf object injected into it will have the most recent update to the data object without having to do anything more than specifying that metadata [Inject] and the parsley:Configure tag. In tab 2, if I change the first name to ‘Lazy’, here is the result:

Tab 3 Using Latest Data

Tab 3 View Code

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="Tab 3" xmlns:model="model.*"
		xmlns:parsley="http://www.spicefactory.org/parsley" backgroundColor="#6183A6">
	
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			import spark.components.BusyIndicator;
			
			[Inject]
			[Bindable]
			public var smurf:Smurf; //shared object managed by Parsley 
			protected var t:Timer = new Timer(1000,1);
			
			protected function submitButton_clickHandler(event:MouseEvent):void
			{
				bi.visible=true;
				t.addEventListener(TimerEvent.TIMER_COMPLETE,onTimerComplete);
				t.start();
			}
			
			protected function onTimerComplete(event:TimerEvent):void
			{
				t.stop();
				new AlertMsg().open(this, false);
				bi.visible=false;
			}
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<parsley:Configure />
		<fx:Component className="AlertMsg">
			<s:SkinnablePopUpContainer x="85" y="160">
				<s:TitleWindow title="Success" close="close()" width="300">
					<s:VGroup horizontalAlign="center" paddingTop="20" paddingBottom="15" paddingLeft="15" paddingRight="15" gap="15" width="100%">
						<s:Label text="Data Submitted!"/>
						<s:Button label="OK" click="close();"/>
					</s:VGroup>
				</s:TitleWindow>
			</s:SkinnablePopUpContainer>
		</fx:Component>
	</fx:Declarations>
	
	<s:layout>
		<s:VerticalLayout paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" gap="10" 
						  horizontalAlign="center" verticalAlign="middle"/>
	</s:layout>
	<s:Form id="form" width="80%" height="80%" defaultButton="{submitButton}">
		<s:FormHeading label="Submission Data" fontSize="36"/>
		<s:FormItem label="First Name: " width="100%" required="false">
			<s:Label text="{smurf.fname}"/>
		</s:FormItem>	
		<s:FormItem label="Last Name: " width="100%" required="false">
			<s:Label text="{smurf.lname}"/>
		</s:FormItem>
		<s:FormItem>
			<s:Button id="submitButton" label="Submit" click="submitButton_clickHandler(event)"/>	
		</s:FormItem>
		<s:FormItem>
			<s:BusyIndicator id="bi" visible="false"/>	
		</s:FormItem>
	</s:Form>
	
</s:View>

You could now add more views behind the tabs (or subviews as I like to call them) and use the same syntax to access your Parsley managed data objects. Feel free to download the sample project with source above from here.

Hopefully after reviewing both parts of this 2-part blog series you will see how using a Dependency Injection framework such as this yields a much cleaner approach and better practice over the other options.

Filed in: Flash Builder 4.5Flex 4.5Flex 4.5/MobileFlex Mobile Tags:

About the Author ()

Comments (23)

Trackback URL | Comments RSS Feed

  1. Michael says:

    Very interesting indeed. However wouldn’t the use of a ‘Registry’ class serve better? As it could store and distribute a larger amount of data through out the application?

    I’m talking about a simple Class with public static variables which would be probably objects or arrays of objects.

  2. Bruno Santos says:

    Great tutorial!

    Still, and in my own opinion, a developer should be very careful when using Dependency Injection/IoC in embedded systems since it is easy to leave resources around that we do not really need to have up from one view to another. This is one of the reasons why Views are destroyed from the stack when no longer required.

    By the way, instead of pointing to my example in Stackoverflow you could point to my WordPress site: http://bit.ly/nVliZP

    I’ll update mine to point to your great options.

  3. Kris Schultz says:

    Holly, another great post. I’ve used both Robotlegs and Parsley. I’m just now getting into mobile development with Flex. I’m having a difficult time wrapping my mind around a way to use dependency injection while also taking advantage of the Flex 4.5 mobile data/view persistence. It would be great if you’d consider exploring that in a blog post.

  4. Al says:

    Thanks for the article and great site. I would look into fast inject instead of configure, reflection is heavy.

  5. Marco Pallotta says:

    Brilliant Work!

  6. Hey, if it is a small data of objects & if you do not want to use the framework, then you could use PersistenceManager to store/get the data across the entire application.

  7. Why not just use a singleton class??

  8. Kris Schultz says:

    @Mogens, Singletons and Dependency Injection (DI) are two patterns that solve similar problems. They each have their benefits and drawbacks. As with all design patterns, the context of your specific project/design problem will dictate when each is appropriate.

    Some benefits of Singletons are that they are easy to create, use, and comprehend. However, Singletons usually result in tighter coupling between your classes because any class that wants to access a Singleton has to do so by referencing the Singleton class definition directly. As a side effect of this rigid coupling it’s difficult to write unit tests for classes that require access to a Singleton.

    Dependency Injection enables looser coupling between your classes. This makes unit testing much easier because any object dependencies that a given class has can be easily set to “mock”, lightweight, or otherwise specialized objects by the test code. And when combined with an Inversison of Control framework (IoC), it’s still very easy to ensure that a single instance of an object is shared by all the classes in your application much like what the Singleton pattern provides. The downsides to DI and IoC are that they do add complexity and a level of abstraction to your architecture which can hurt comprehesibility. In other words, someone looking at your code/architecture for the first time will have a tougher time understanding what’s going on than if Singletons are used.

    This topic is much bigger than can be adequately covered in a blog comment. :) But if you’re interested in digging deeper I recommend reading Martin Fowler’s excellent coverage of these patterns. You can start here: http://martinfowler.com/articles/injection.html

  9. Secret007 says:

    When i run the project it says:

    Process terminated unexpectedly.

    invalid application descriptor: Unknown namespace: http://ns.adobe.com/air/application/3.0

    Launch command details: “C:\Program Files\Adobe\Adobe Flash Builder 4.5\sdks\4.5.1\bin\adl.exe” -runtime “C:\Program Files\Adobe\Adobe Flash Builder 4.5\sdks\4.5.1\runtimes\air\win” -profile mobileDevice -screensize 320×460:320×480 -XscreenDPI 163 -XversionPlatform IOS “C:\Documents and Settings\Administrator\Adobe Flash Builder 4.5\TabsDataSampleParsley\bin-debug\TabsDataSample-app.xml” “C:\Documents and Settings\Administrator\Adobe Flash Builder 4.5\TabsDataSampleParsley\bin-debug”

    • Hi there, go into the app descriptor file for the project (TabsDataSample-app.xml) and change the the first line to:

      I was using a newer AIR build (3.0) when I did these samples but the 2.6 AIR version will work just fine too and is what comes with Flash Builder 4.5.1.

      Thanks!
      Holly

      • Secret007 says:

        change first line to what ? … and could you tell me where is the app descriptor file ?

        • Oops, the line didn’t come through in my comments I see – you basically need to change the 3.0 namespace in the first line of the app-descriptor to 2.6.

          <application xmlns=”http://ns.adobe.com/air/application/2.6″>

          The file is located in the project root with a -app.xml extension on it (so it’s usually your project name like TabsDataSample-app.xml)…

          • Secret007 says:

            i see, yes its now working fine :) and its an easy and good way to pass data between tabs, thanks for the help, cheers !!!

  10. Jason Graham says:

    Trying to use your same logic with a ViewNavigatorApplication, the main problem I am finding is if I set my firstView in MXML on the ViewNavigatorApplication tag, I cannot set my injected object up on the applicationCreation event. If I do my firstView is already created and ready to go, plus the ContextBuilder has already finished creating the object and fired the bindings so the object is empty. This basically results the data not getting set and my view trying to do what it needs to do with an empty object. Is there a way to make this work?

    This also breaks with a very simple example not even using parsley, just create a Object in the applications initialize or applicationComplete, bind that to the firstViewData property on ViewNavigatorApplication. Then in your firstView try to use data in the viewActivate handler, it’s null.

    Is this a bug with the lifecycle for ViewNavigatorApplication?

  11. Jason Graham says:

    More info. Seems like this works ok for UI related fields that are bound, but for data that the view might need thats not bound to a UI component for example I am assuming that I would need to programmatically bind to those in the or what for property change events throughout the code? Is this not right?

  12. Adrian says:

    Holly you have no idea how this has been helpful for me!!
    I’m wondering you know if this is possible…..

    I’m trying to add a favorites button for a FLEX Mobile App I’m building. The idea is, when the user is a browsing a list and decides they want that favorite an item on that list. They press the favorite button, which then puts that item into the favorites tab.
    Do you have any ideas? Or sample code for me to work with and TRY to understand :(

  13. marius says:

    how can you determine if a view, on its activation, is pushed or activated after a popping another ?

  14. Tim John says:

    Hi Holly,

    Very interesting stuff. Despite being a Flex developer I’ve never even heard of Parsley.

    I was wondering what you do to navigate a complex Flex Mobile application. Can injection (with Parsley or something similar) be used as a Navigation system? Navigating a Flex Mobile project seems a little trickier due to the fact that views are constantly destroyed to save memory.

    I believe the term is Deferred Navigation.

    Thanks for all your advice so far!

Leave a Reply