Home  >  

Handling Delayed Instantiation in Flex 3 (part 1 of 2)

Author photo
AddThis Social Bookmark Button

Delayed Instantiation is a technique in Flex whereby children of IContainer components aren't created until the user actually needs to see them. For instance, if you know that only the top half of your component will be showing, you can take control of the creation of children added to the component through MXML and only show the ones you know they will see at first. Then, when they scroll, you could show the rest.

The most familiar example of a Flex component that uses delayed instantiation is ViewStack and its subclasses, Accordion and TabNavigator. Under most circumstances, the creationPolicy on a ViewStack will be "auto", which means that the direct children of the ViewStack will be created immediately, but in most cases the grandchildren will be created the first time the particular ViewStack "pane" is shown. Usually, this process is fairly painless, but recently I've encountered "gotchas" in two situations, which are: when the ViewStack contains a Container subclass that needs to set properties on its children and when the ViewStack's children are created by a Repeater and those children contain a List-based component.

This week, I'll talk about the first of those situations, and I'll cover the Repeater example next week. The solution to this problem is fairly simple, so if you're up against a deadline and don't want to wade through the explanation of why it works, jump to the solution now.

Subclassing Container isn't all that difficult–you just need to use Canvas, Box, Vbox, etc. as your superclass in an AS class or as the root tag in MXML. For simplicity, I'm going to refer to a hypothetical extended VBox called VBoxWithComboBoxes. Now, comboboxes need a fair amount of setup. First, they need a dataProvider to give you some choices. That's relatively easy to do with data binding. However, the value that determines what's selected in the combobox seldom maps exactly to any of the items in the dataProvider, so usually you have to loop through the dataProvider and find the item that matches your criteria, then set selectedItem or selectedIndex based on that match.

And this leads to a problem, because you need to do this whenever the value that determines what's selected changed or whenever the dataProvider changes. Without getting too much into the component lifecycle, we don't have any guarantees that the ComboBox actually exists when the setters for these values are called, so most developers call invalidateProperties() in the setter and set a flag that we then look at in a commitProperties() override and then make the change there. Usually this works, because commitProperties() is always called after createChildren().

But if you're like me and you assume that, at the end of createChildren() all of the children have been created, you'd be wrong, because of course when a container is created with delayed instantiation, the children likely haven't been created. So then the challenge becomes one of determining whether or not the children have been created yet.

My preliminary research showed me that the children definitely get created by the end of initialize(), so my first thought was to check initialized in commitProperties(). There was just one problem with that, which is that the invalidation flag that got set when I called invalidateProperties() in the setters had already cleared by the time initialization had finished, and so commitProperties() wasn't getting called. So I needed to find a more surefire method of making sure I wasn't trying to access my children before they were created.

To do this, I had to dig into the code of Container and ViewStack, so I thought I'd summarize what happens.

  1. In UIComponent, which is the base class of Container, initialize() calls createChildren().
  2. Container overrides createChildren() and calls a new function, createComponentsFromDescriptors(). This takes a flag that tells it whether or not to recurse its children, and when this is called from within Container, that flag is always true. At the end of this function, it sets a variable, processedDescriptors. This variable originates as a setter in UIComponent. In addition to setting the underlying private variable, this setter generates an event that notifies everyone that the container is initialized.
  3. ViewStack overrides createComponentsFromDescriptors to check the creationPolicy and set the recurse flag based on what's appropriate for the creationPolicy. At the end of this override, the processedDescriptors variable is also true, but keep in mind that this is only the processedDescriptors variable for the ViewStack itself (though of course it will also be true on the component that's on the first pane as well). What this means is that if our VBoxWithComboBoxes is a child of a ViewStack, but not on the first pane, its children will likely not be created (we'll talk about the exception next week), and its processedDescriptors variable will be false, until the ViewStack tells it to process its child descriptors.
  4. ViewStack has another function, instantiateSelectedChild(), which is called, from among other places in ViewStack, in commitProperties() when the selectedIndex setter sets up conditions which might result in the selection of a child that hasn't been instantiated. This function will then call createComponentFromDescriptors() with the recurse flag set to true, which will then tell our VBoxWithComboBoxes to create its children, after which the processedDescriptors instance variable is set to true on the shiny new VBoxWithComboBoxes that's been created.

After doing all of this research, it was pretty clear to me that the variable I needed to be looking for was processedDescriptors. However, I still wasn't sure what to override to persuade commitProperties() to run. My first choice was initialize(), because its simpler method signature meant there was less likelihood that I would screw anything up while calling super.initialize(). However, it appears that the compiler frowns on overriding initialize() in a Container subclass, so I wound up overriding createComponentsFromDescriptors(), only to add a call to invalidateProperties().

So, together with my commitProperties override, the code to resolve this issue looks like this:

 

override public function createComponentsFromDescriptors(recurse:Boolean=true):void {
 	super.createComponentsFromDescriptors(recurse);
 	/*	This makes sure that, now all our components are created, 
 		we can assign properties to them. 
 	*/
 	invalidateProperties();
}
override protected function commitProperties():void {
 	if (processedDescriptors) {
  		if (_comboSelectedPropertyChanged || _comboDataProviderPropertyChanged) {
   		childCombo.dataProvider=_comboDataProviderProperty;
   		//logic to find the selected item based on properties of the items in the dataProvider
   		//init flags
   		_comboSelectedPropertyChanged =false;
   		_comboDataProviderPropertyChanged=false;
   		}
  	}
 	super.commitProperties();
} 

Tune in next week to see how to resolve issues caused by delayed instantiation of List-based containers created by a Repeater.

Read more from Amy Blankenship. Amy Blankenship's Atom feed

Comments

11 Comments

Vladimir Tsvetkov said:

Quite deep of a digging you've done on this one and I guess you had to delve in a lot in the Flex framework guts and inners.

Do you know if this behavior is also valid for Flex 4 components, or since in Gumbo the concept of a Group is totally different from Container we don't have to deal with lazy instantiation issues as this one?

Amy said:

I haven't had a chance to look in this kind of depth in Flex 4 (I usually only find this stuff out when I encounter a probelm in a real project). I will try to test this when I get back from RIAAdventure, as this coming week is pretty full for me.

Florian said:

Hi Amy,

regarding your ViewStack example: basically I think delayed instantiations is something in Flex which is one of these "hot" topics and several workarounds can be applied as possible solutions.

I prefer the clean way, which in this ViewStack case is using modules and load these dynamically. Each view is represented by a single module which itself is able to load/exist on its own - et voila, a much cleaner solution rather than having to deal with things usually handled by the framework...

Just another approach which should be considered before stepping behind the deep secrets of Flex....but just a consideration ;-)

Thanx for picking up this topic, Amy;

Cheers, Florian!

Amy said:

Yes, sure. Modules are completely uncomplicated and don't have any issues at all ;-).

Darren said:

Why do people always allude to these mysterious 'module' issues and never spell out exactly what they are? Modules are basic building blocks - we use them every day here. The only thing you have to watch is that you don't create memory leaks which I manage by reusing the same module where possible. They certainly aren't one of the more difficult aspects of Flex development. What are these issues, Amy?

Amy said:

For one thing, look at http://www.magnoliamultimedia.com/flex_examples/Amys_Flex_FAQ.pdf , Question 8. This is not a difficult issue to fix, it does require a fix and requires a knowledge of the Framework to understand.

Se also http://tech.groups.yahoo.com/group/flexcoders/message/121119
http://tech.groups.yahoo.com/group/flexcoders/message/140849

and then there's the relative size...

Again, these problems are not insurmountable, but you have to know why they happen and how to avoid them, but they are all places where you need to know something about what is happening in the Framework to work around them, and at some point someone went through the code and unravelled it all. The fact that you are completely used to whatever workarounds you're employing doesn't mean that someone didn't have to figure out at some point what they were.

Darren said:

@Amy, while I agree with the first point from the FAQ (I commented on that bug sometime ago - https://bugs.adobe.com/jira/browse/SDK-15249#action_180215), do you see my point? I think that modules are a core part of Flex development and yet wherever they're mentioned there's always some implication that there's some sort of voodoo involved in using them. What message does this send to people learning Flex? "Steer clear" is the message I see.

I think modules are a clean solution to your problem. While I wouldn't go so far as to say they are always 'best practice' in this situation, I think your readers would have been better served by steering them towards modules as a possible solution rather than away.

Anyway, sorry to be picky. I read most of your articles and I think it's great that you're putting them out there.

Amy said:

I would agree with you that most things that you want to do with Flex beyond the simple examples shown in the Help will require a fairly in depth knowledge of the Framework and some sort of workaround for an unexpected "gotcha." However, when a tool has a lot of power, it's just difficult to learn.

I think, on balance, that adding an override to createComponentsFromDescriptors() is a less complicated, lower impact solution than Modules, unless you have some other reason to use them. So we will just have to agree to disagree on this point :).

Julien said:

Hi Amy,

Thanks for the tip and the very interesting article

dsi r4 said:

A wonderful article…. this is just what I needed to read today. Thanks for describing the way you work and how you structure your writing projects. I’ll go read that article now.

Amy Blankenship said:

Vladimir;

It looks like you can only add Spark components into a TabNavigator if they are wrapped in a NavigatorContent component. I'm thinking that you wouldn't want to extend this class directly, as it can only be used in a Navigator component. Since the problem only occurs in custom containers that are direct children of Navigators, I think it's extremely unlikely that you'd have the problem extending a Spark container.

So the good news is that in Flex 4, you can probably avoid this problem in all container extensions by simply using the supplied NavigatorContent wrapper. In Flex 3, another quick fix that didn't occur to me is that you could just wrap your custom component in a Canvas and allow that Canvas to be the direct child of the Navigator.

Thanks for suggesting I look into this. I learned something today :)

Leave a comment


Tag Cloud

iPad

What's your take on the iPad? (Putting aside the Flash/iPad flame war)

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.