Deferred Component Instantiation in Actionscript
You already know when using a ViewStack (or any ViewStack derivatives) in MXML that you can set the creationPolicy property to determine if it’s children are all created at once, automatically as necessary, or not at all. This is a fairly straight forward procedure in MXML and looks as such:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:TabNavigator x="0" y="0" width="100%" height="100%" creationPolicy="auto"> <mx:Canvas label="Tab 1" width="100%" height="100%"> </mx:Canvas> <mx:Canvas label="Tab 2" width="100%" height="100%"> </mx:Canvas> <mx:Canvas label="Tab 3" width="100%" height="100%"> </mx:Canvas> <mx:Canvas label="Tab 4" width="100%" height="100%"> </mx:Canvas> </mx:TabNavigator> </mx:Application> |
*In the above code creationPolicy is “auto” by default so including the creationPolicy is redundant.*
However, for you actionscript purists, isn’t it annoying how the addChild in actionscript doesn’t give you the same deferred component instantiation?
I was a bit annoyed today at Flex when I did something similar to the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="this.onCreationComplete(event)"> <mx:Script> <![CDATA[ import mx.containers.VBox; import mx.events.FlexEvent; private function onCreationComplete(event:FlexEvent):void { for(var i:int=0;i<10;i++) { var vbox:VBox = new VBox(); vbox.percentHeight = 100; vbox.percentWidth = 100; vbox.label = "Tab "+(i+1); this.tn.addChild(vbox); } } ]]> </mx:Script> <mx:TabNavigator x="0" y="0" width="100%" height="100%" id="tn"/> </mx:Application> |
Naturally you would think, “My TabNavigator has it’s creationPolicy on auto by default, so only the first view will be instantiated.” But when my service calls were being fired for other views I quickly realized this assumption was wrong. Adding a few trace statement my application looked like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="this.onCreationComplete(event)"> <mx:Script> <![CDATA[ import mx.containers.VBox; import mx.events.FlexEvent; private function onCreationComplete(event:FlexEvent):void { for(var i:int=0;i<10;i++) { var vbox:VBox = new VBox(); vbox.percentHeight = 100; vbox.percentWidth = 100; vbox.label = "Tab "+(i+1); vbox.addEventListener(FlexEvent.PREINITIALIZE,this.onTabPreInitialize); vbox.addEventListener(FlexEvent.INITIALIZE,this.onTabInitialize); vbox.addEventListener(FlexEvent.CREATION_COMPLETE,this.onTabCreationComplete); this.tn.addChild(vbox); } } private function onTabPreInitialize(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.PREINITIALIZE,this.onTabPreInitialize); trace(VBox(event.target).label+" "+event.type); } private function onTabInitialize(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.INITIALIZE,this.onTabInitialize); trace(VBox(event.target).label+" "+event.type); } private function onTabCreationComplete(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.CREATION_COMPLETE,this.onTabCreationComplete); trace(VBox(event.target).label+" "+event.type); } ]]> </mx:Script> <mx:TabNavigator x="0" y="0" width="100%" height="100%" id="tn"/> </mx:Application> |
And my trace output was:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | Tab 1 preinitialize Tab 1 initialize Tab 2 preinitialize Tab 2 initialize Tab 3 preinitialize Tab 3 initialize Tab 4 preinitialize Tab 4 initialize Tab 5 preinitialize Tab 5 initialize Tab 6 preinitialize Tab 6 initialize Tab 7 preinitialize Tab 7 initialize Tab 8 preinitialize Tab 8 initialize Tab 9 preinitialize Tab 9 initialize Tab 10 preinitialize Tab 10 initialize Tab 1 creationComplete Tab 2 creationComplete Tab 3 creationComplete Tab 4 creationComplete Tab 5 creationComplete Tab 6 creationComplete Tab 7 creationComplete Tab 8 creationComplete Tab 9 creationComplete Tab 10 creationComplete |
Well this wasn’t going to work at all. I had service calls that I only wanted called on the creationComplete (specifically when a user had clicked to activate the tab). I wasn’t going to change my architecture to include MXML so I had to adjust the actionscript to do what I needed. Luckily the fix is pretty simple (and yet there aren’t any examples of the fix on the web). Here is the final change to make the fix.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="this.onCreationComplete(event)"> <mx:Script> <![CDATA[ import mx.containers.VBox; import mx.events.FlexEvent; private function onCreationComplete(event:FlexEvent):void { for(var i:int=0;i<10;i++) { var vbox:VBox = new VBox(); vbox.percentHeight = 100; vbox.percentWidth = 100; vbox.label = "Tab "+(i+1); vbox.creationPolicy = "none"; vbox.addEventListener(FlexEvent.PREINITIALIZE,this.onTabPreInitialize); vbox.addEventListener(FlexEvent.INITIALIZE,this.onTabInitialize); vbox.addEventListener(FlexEvent.CREATION_COMPLETE,this.onTabCreationComplete); this.tn.addChild(vbox); } } private function onTabPreInitialize(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.PREINITIALIZE,this.onTabPreInitialize); trace(VBox(event.target).label+" "+event.type); } private function onTabInitialize(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.INITIALIZE,this.onTabInitialize); trace(VBox(event.target).label+" "+event.type); } private function onTabCreationComplete(event:FlexEvent):void { VBox(event.target).removeEventListener(FlexEvent.CREATION_COMPLETE,this.onTabCreationComplete); trace(VBox(event.target).label+" "+event.type); } ]]> </mx:Script> <mx:TabNavigator x="0" y="0" width="100%" height="100%" id="tn"/> </mx:Application> |
Notice that all I did was add the creationPolicy not to the TabNavigator, but to the VBox that I was adding as my container. This one line made all the difference. And now at startup my trace looks like:
1 2 3 4 5 6 7 8 9 10 11 12 | Tab 1 preinitialize Tab 2 preinitialize Tab 3 preinitialize Tab 4 preinitialize Tab 5 preinitialize Tab 6 preinitialize Tab 7 preinitialize Tab 8 preinitialize Tab 9 preinitialize Tab 10 preinitialize Tab 1 initialize Tab 1 creationComplete |
Just the way we originally intended. Hope that helps!





THANK YOU!! THANK YOU!!
You saved me hours!!! of debugging. I spent about 2 hours already trying to solve this problem.
Thank you again.
Thank you sooo much, I’ve been fighting with this issue for 3 hours, it never occurred to me that setting the creation policy to none on the individual components would be neccessary. After reading at least 50 blogs, finally I discovered yours.
Thanks
Great post, one thing I would like to know. If you are not using a TabNavigator but some other container, how would one go about instantiating the children using createComponentFromDescriptor as it seems that only MXML components are in this array, not the ones created dynamically.
Thanks
The process of deferring component instantiation within a container and for a viewstack is a bit different but with the same goal, improved performance.
Neil, for your question let’s use the example of having a really long page with multiple containers. It’s stupid to instantiate components that are off the screen, so we can wait till the scroll bar or view port gets to a specific value and then start to instantiate the container’s children components.
This would be a good use of the createComponentFromDescriptor or createComponentsFromDescriptors. Then when your case is hit you can call vbox.createComponentsFromDescriptors(true); Now all the children components that you originally set the creationPolicy to ContainerCreationPoicy.NONE to be created.
That help?
You da man! That was exactly what I was looking for!
Great catch!
In my case, I have a main ViewStack (of VBoxes) controlled by a LinkBar. I have two states, before and after the user logs in. In the ‘before’ state they can only see the home page, but in the ‘after’ state, they can access all the pages.
The correct solution then is to only have the home page on the view stack initially, and then add the other pages once the user logs in. The problem was that adding the pages would cause them to load all their children, so when the state changed, all the non-home page pages would be completely created.
Without states, deferred instantiation worked as expected, and with this fix it now works as expected WITH states as well. Each page sets its creationPolicy=”none” (in the first (VBox) tag of the MXML definition), and it is loaded by the ViewStack container when the user switches to it.
Again, bravo!
Thats a superb post.
Thanks for sharing these with us.
I have one more doubt on same lines, I have posted it on Stackoverflow, can you plz help me here ? Link
“http://stackoverflow.com/questions/3265411/difference-between-array-and-array-collection-in-flex/3265732#3265732″