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

August 9, 2011By 16 Comments

A question that’s been coming up often with Flex mobile development is how to pass data between tabs (or stacks of views) within an application. Passing data within a view stack is straightforward, you simply pass it on the pushView or return it from the popped view. A great cookbook example showing this is here. However, what if you want to set certain data to be available when a different tab (separate view stack) is clicked? Remember that when you create a TabbedViewNavigatorApplication, each tab represents its own view stack or set of views that you can push and pop between. So how do you handle the passing of data in this situation? There are a couple different approaches you could use to achieve this including:

1) A tightly coupled approach (hard-wiring dependencies)

2) A loosely coupled approach using a Dependency Injection Framework (my preference)

This post will go over option 1, followed by a post showing my preferred option 2… The samples are not pretty and sorta silly (yes I’m a fan of the Smurfs), but hopefully still get the point across ;)

Tightly Coupled Approach

DISCLAIMER: I do not recommend this approach, however there are likely times where a developer should know they can do this where code does not need to be reused or require additions. This approach revolves around defining data objects in your main application that could be changed and accessed from your views specifically through type casting or via the FlexGlobals.topLevelApplication syntax to access them.

For instance, consider the following main application code, which also shows how you can initialize a tab’s first view data with a data object via the firstViewData property (scroll code to the right to see it on the first ViewNavigator object).

<?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.*"
								  applicationComplete="onApplicationCompleteHandler(event)">
	<fx:Script>
		<![CDATA[			
			import model.Smurf;
			
			import mx.events.FlexEvent;
			
			[Bindable]
			public var smurf:Smurf = new Smurf();
			
			protected function onApplicationCompleteHandler(event:FlexEvent):void
			{
				// Initialize the smurf data...
				smurf.fname="Smurfette";
				smurf.lname="Smurf";
			}
		]]>
	</fx:Script>
	
	<fx:Style source="Styles.css"/>
	
	<s:ViewNavigator id="v1" label="Tab 1" width="100%" height="100%" firstView="views.viewstack1View" firstViewData="{smurf}"/>
	<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>

My Smurf object referenced in the above is a simple data object that looks like this:

package model
{
	[Bindable]
	public class Smurf
	{
		public var fname:String;
		public var lname:String;
		
		public function Smurf()
		{
			this.fname = fname;
			this.lname = lname;
		}
	}
}

Tab 1 – First View with firstViewData

Here is the code for the above view… note that I used bidrectional binding on the TextInput fields so the smurf data can also be updated when the text is changed:

Tab 1 – First 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 1" xmlns:model="model.*" backgroundColor="0x993366"
		viewActivate="view1_activateHandler(event)">
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			[Bindable]
			protected var smurf:Smurf;
			
			protected function view1_activateHandler(event:Event):void
			{
				// Here we can access the data property since the firstViewData was set
				smurf = data as Smurf;
			}
			
			protected function button1_clickHandler(event:MouseEvent):void
			{
				navigator.pushView(subView);
			}
			
		]]>
	</fx:Script>	
	
	<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 id="fn" 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:Button click="button1_clickHandler(event)" label="Show Subview"/>	
</s:View>

This is pretty straightforward and obvious, however, what if we now wanted to display the latest updated data from a different tab and view stack? We can access the smurf property on the main application at any point throughout the application regardless of the view stack it’s in or tab by using a type casting method of accessing the parentDocument or or via the mx.core.FlexGlobals.topLevelApplication property. (Note: in my examples I’m going to use the parentDocument property since it is implicit and does not require an extra import):

So for instance given the above, if I now decide to change my data in tab 1 to look like this:

Tab 1 First View Data Changed

When I click tab 2, I will see the updated data immediately using the view code shown below this screenshot for Tab 2…

Tab 2 First View Data Also Shows Update

Tab 2 First 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.*"
		viewActivate="view1_activateHandler(event)" backgroundColor="0x336699">
	
	<fx:Script>
		<![CDATA[
			import model.Smurf;
			
			import mx.events.FlexEvent;
			
			[Bindable]
			public var smurf:Smurf;
			
			protected function view1_activateHandler(event:Event):void
			{
				var mainApp:TabsDataSample = parentDocument as TabsDataSample;
				smurf = mainApp.smurf; // accesses the smurf property from my main application mxml
			}
			protected function button1_clickHandler(event:MouseEvent):void
			{
				navigator.pushView(subView2);
			}
		]]>
	</fx:Script>
	
	<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: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:Button click="button1_clickHandler(event)" label="Show Subview"/>
</s:View>

This will be the same for any subview’s of any tab as well (note: subview is just a term I’m using to describe those views within a certain view stack, since each tab is a separate set of stacks, I’m referring to one behind a first view of a tab). If I now click that button to show the subview, I will always get the latest smurf data no matter where it was set and can update it accordingly:

Tab 2 Subview Shows Latest Data

The best way to see how this all works is to download the project and try out the application and change data between views and subviews yourself. The fxp of the source is located here. Note if you don’t have the back button on your phone, you can get back to the first view of any tab by clicking the tab again.

Note: Another approach but still tightly coupling via using a custom event with a data object can be found here.

The downside of using a tightly coupled approach is that you are creating dependencies within your application. While coding a more loosely coupled application can be more time consuming, require a bit more thought or seem like more work, it can save you a lot of headaches when implementing future requirements. However, I do recognize that this approach could have its useful moments, particularly for samples or prototypes where it would be overkill to implement a framework.

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

About the Author ()

Comments (16)

Trackback URL | Comments RSS Feed

  1. Don Kerr says:

    Thank you Holly! Great timing. :)

    Very good explanation of both approaches. It helps me to understand the pros and cons of both in very relatable terms, since I have no exposure to Dependency Injection.

    However, as I was reading both I couldn’t help to think about this in the context of my early ColdFusion days. We could use session/application variables that could be accessed anywhere in the application. Or we could use CFINCLUDE to include or “inject” code/data inside every page we needed it.

    I’ll need to learn more about Dependency Injection approach. Appreciate the great tips as always! Keep the posts coming related to data-centric apps. I’m getting a lot of client requests for enterprise tablet apps!

    Thanks again!
    Don Kerr

  2. vic says:

    I think showing both a flex approach and a pure .as approach is helpful.

  3. kabalweg says:

    Hi Holly,

    I have a different situation and I couldn’t find the answer in google so I’m posting it here, maybe you could help me.

    I have 3 tabs in a TabbedViewNavigatorApplication, the user first need to login before using the app. So what I did was after loading the first view of the first tab, I push the LoginView for the use to login. The logout button is in the first view of the third tab and when the user logs out, I want to display the LoginView again. The problem is, if I push the LoginView, it will be in the viewstack of the third tab now and when the user re-login and I push the first view if the first tab, it messed up the whole app. Thanks for your help in advance.


    kabalweg

    • Hi kabalweg,

      Is the login view the first view of your first tab? If so, you could just change the selected tab index to the first tab when they logout. There’s a tabbedNavigator property on the main TabbedViewNavigatorApplication that you can set the selectedIndex on. You may have to use FlexGlobals to access it from your logout View of your 3rd view stack such as:

      FlexGlobals.topLevelApplication.tabbedNavigator.selectedIndex=0;

      Let me know if this is what you mean… Cheers! Holly

  4. kabalweg says:

    Hi Holly,

    Thanks for your reply.

    I was looking also on how to programatically change the selectedIndex of the TabbedViewNavigatorApplication. The FlexGlobals.topLevelApplication.tabbedNavigator.selectedIndex=0; will help in my use-case. However if you have another idea, I’d definitely want to know.

    For your question, the LoginView is the second view of the first tab. But basically, the user should not be able to go to other view without logging in first. Again, if you have another idea on how to implement thus, I’m all ears.

    Thanks so much.


    kabalweg

  5. Hi Holly, Great posts, thanks for the info. Do you have a good or know of a good example of a mobile Sqlite add, update, delete.
    Thanks, Tony

  6. Parag says:

    I am new to this…

    How do I know and find TabsDataSample?

    var mainApp:TabsDataSample = parentDocument as TabsDataSample;

  7. Florian S. says:

    Thanx for the examples. But what i don’t understand: why not overriding data:

    private var _smurf:Smurf, _smurfChanged:Boolean;

    [Bindable('dataChange')]
    public function set data(value:Object):void
    {
    _smurf = value as Smurf;
    _smurfChanged = true;
    invalidateproperties(); // call other invalidation methods when needed
    if (hasEventListener(FlexEvent.DATA_CHANGE))
    {
    dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
    }
    }

    and override commitProperties

    override protected function commitProperties():void
    {
    super.commitProperties();
    if (_smurfChanged)
    {
    _smurfChanged = false;
    // processing;
    }
    }

    This would have the following benefits:
    1. the view can be used as IDataRenderer in other contexts
    2. site effects by dispatched events like Event.ACTIVATE (which are like system events, the event handlers will be invoked whether or not the view is on the stage) won’t occur.
    3. optimized rendering because the implementation relies on the flex live cycle.

    Just some constructive comments. What do you think.

  8. Sachin says:

    HI Holly,

    I have one application on which there is tab1,tab2 and tab3 like tabbedview navigator is present.

    On Tab1(tabbed View navigator) there is one button is present.When user clicks the button timer will start and immediately other tabView navigator like Tab2 and tab3 gets disable.

    Please provide your suggestions.

    Thanks

  9. Sachin T. says:

    Hi Holly.. I want to create two simple flex android application say A and B. On application A there is one button.When i press this button i send some string as parameter and application B is launch.
    when application B is launch it displays the string that i have send through application A.
    How can i do it on my android phone..please help me..

  10. Nimai says:

    ok i managed to get it done, just the problem was that i had to link the sql database but got it working

Leave a Reply