Passing Data Across Views in Flex Mobile
Posted on Dec 14, 2010
If you've done any development using Flex "Hero" for mobile (i.e. Android), you already know that you can pass any arbitrary struture of data when you push or pop a View to the ViewStack. I talk a little bit about this topic in an earlier post covering passing data on pop methods (i.e. popView() and popToFirstView()). In this post, I want to cover using an ActionScript class to encapsulate your View data rather than passing arbitrary data to each View. This entails passing data into your first View, passing data when pushing Views and passing data when popping Views. This isn't an official best practice, but rather a practice I've found useful and wanted to share and discuss. As a side note, some of this code comes from my upcoming article on advanced AIR for Android development with Flex for the Adobe Edge which should be published some time soon.
Encapsulating View Data in an ActionScript Class
As I said, when you pass data across Views in Flex "Hero" for mobile, it can be any arbitrary structure of data. Usually, you'll see this passed as in the following line of code:
navigator.pushView(views.SomeView, {firstName:"Brian",lastName:"Rinaldi"});
This works fine, but it's now incumbent on you to ensure the code in the next view knows exactly what data properties and of what type you are sending. The same goes for passing data to the first view or when popping views. As your application grows in complexity this can cause difficult to debug issues and simply lacks consistency. Instead, I prefer to encapsulate all data passed across Views in a Model.as class (you don't need to call it Model if it sounds too Cairngorm-y for you). Then, every View always expects to receive the model. Below is the code for a sample class of this sort - as you can see, there's nothing special about it. One thing you may notice is that I generally store the SQLConnection if I am using a SQLite database within my application, which allows me to reuse the connection throughout any View in my application.
package valueObjects
{
import flash.data.SQLConnection;
import mx.collections.ArrayCollection;
public class Model
{
public var connection:SQLConnection;
public var items:ArrayCollection = new ArrayCollection();
public var selectedItem:Number = 0;
public function Model()
{
}
}
}
Passing Data to the First View
To begin with, we need to pass this data into my first View (i.e. the view defined in the firstView property of my MobileApplication MXML tag. To pass data down to your firstView, you can use the navigator.firstViewData property (thanks to Ray Camden for this tip). This can be any arbitrary data but as you'll notice, I am passing in an instance of my Model class (though in this example, it doesn't yet contain any data). I might create my SQLite database here and pass down the connection but I will cover that in more detail in a follow up post. This is handled within an initialize event handler by specifying initialize="init()" in my MobileApplication tag. This will call the init() method below:
import valueObjects.Model;
private var model:Model = new Model();
protected function init():void
{
navigator.firstViewData = model;
}
Receiving Data in All Views
To receive the Model class, I use a viewActivate event handler in my View tag specified by viewActivate="init(event)". I check for the existence of the data variable and, assuming it is not null, set my View's instance of the Model class to the value of data (since we know that data is always an instance of Model). Truthfully, this should probably throw some form of error if data is null since, by convention, I always expect an instance of Model.
protected function init(event:ViewNavigatorEvent):void
{
if (data != null) {
model = data as Model;
}
}
Passing Data on PushView()
Pushing data to the next View is pretty simple, as you'd expect. Instead of passing the arbitrary object of properties, we pass the View's local instance of the Model:
protected function goToSomeView():void
{
navigator.pushView(views.SomeView, model);
}
Passing Data on Pop
As discussed in my prior post, you can pass data when a View is popped by overriding the createReturnObject() method. You should do this in every view even if you don't physically call either popView() or popToFirstView() anywhere. This is because if the user clicks the back button on the physical device it will pop the View and all your Views now expect the Model to be passed.
override public function createReturnObject():Object
{
return model;
}
Summary
Since most mobile applications contain only a handful of Views, factoring in a full framework seems like overkill. Still, having a consistent way to enapsulate and pass data across Views makes mobile Flex development a little easier in my opinion. I'd love to hear if others have different ways of handling this or prefer to use a framework.
Comments
So here is a question. If in your root view you want to open a db connection and open it async, then would you be able to do navigator.firstViewData in the event handler? Ie, when is it "too late" to set firstViewData?
Posted By Raymond Camden / Posted on 12/15/2010 at 5:04 AM
I haven't actually tested that (the sample apps I wrote used a synchronous db connection) but that was one of the things I wanted to test for the follow-up covering specifically handling SQLite.
Posted By Brian Rinaldi / Posted on 12/15/2010 at 6:27 AM
Let me know. Obviously this isn't just for dbs, but _any_ async process.
Dude - I think I'm just being dumb. If you get rid of firstView in the MobileApplication tag, then it won't load.
So just run all the crap you need to and _then_ do the manual pushView.
Posted By Raymond Camden / Posted on 12/15/2010 at 6:48 AM
@Raymond - I am working on a mobile application at the moment and I had to figure this exact issue out
I have a model which parses and formats all of my data and then on my listener in my main app for ModelEvent.DATA_COMPLETE i am doing this
this.navigator.pushView(FirstView, model.getMovies());
So the answer is YES - you can leave firstView blank and push it later with the data...one thing i noticed though that i hope i can clear up.
If you define a "splashScreenImage" for a loading screen, this is not efficient because the splash screen automatically goes away after the application complete event...Your data might take longer to load than that - So i would manually add a loading screen with createChildren and then just have it go away when you push your fistView manually later
In my opinion in most cases firstViewData gets set too early when you define firstView before your data is done loading.
You can always capture this.data in the INITIALIZE event of the view being passed the data.
Posted By Joe / Posted on 12/16/2010 at 1:56 PM
Joe - thanks for sharing this.
Posted By Raymond Camden / Posted on 12/16/2010 at 2:01 PM
Your welcome..there's a property called splashScreenMinimumDisplayTime which actually halts the application complete event untill it reaches the time you specify..if u dont specify a time it fires when the app creation completes or 1 second...but still its a guess as to when the data will be done loading.
Posted By joe / Posted on 12/16/2010 at 6:46 PM
Hi, I have a TabbedViewNavigatorApplication with 3 views(ViewNavigator),
I can use the navigator.pushView(view,dataobject) from actionscript to navigate to another view with a data object of my choice.
How do I pass a data object of my choice when the user clicks on the default tab button along the bottom to switch views?
Posted By steve / Posted on 07/13/2011 at 12:59 PM