Flex and Inconsistent Object Initialization
Posted by
Brad Wood
Sep 25, 2008 09:42:00 UTC
Flex objects extending the UIComponent class have two events that fire during their creation process. The "initialize" event fires when the object is starting to be created, but before its children are created. "creationComplete" fires after that object and all its children have been created. There is a great post and example here. I found this out while trying to figure out an intermittent error I was getting when loading my Flex app that was related to trying to access an object prior to its creation.My app at this point is really nothing more than two tabs, each with a handful of graphs in each tab. The first tab is selected by default when the app loads. I have a single remoteObject call that sets the dataProvider of all the graphs at once with test data when it returns like so:
[code]private function return_data(e:ResultEvent):void { // Charts on first tab my_chart_1.dataProvider = e.result; my_chart_2.dataProvider = e.result; // Charts on second tab my_chart_3.dataProvider = e.result; my_chart_4.dataProvider = e.result; }[/code]Often (but not every time) when the app loaded, I would get this error:
[code] TypeError: Error #1009: Cannot access a property or method of a null object reference.[/code]In my mx:Application tag I am calling the function which makes the remoteObject call when the creationComplete event fires. The error was happening on the line of code that referenced the first chart in the second tab. Apparently the objects in the hidden tab are not always created when the app loads, but the behavior is unpredictable. I added the following into each of my mx:ColumnChart tags:
[code]initialize="mx.controls.Alert.show('chart_name')"[/code]Sure enough, some times when I fresh the app in the browser, all the charts on all the tabs load right off the bat (and nothing errors). But then I will hit refresh again and only the charts on the visible tab will load (and then I will get the error thrown when I try to reference the hidden charts). Perhaps someone can clear up this behavior for me. Should I NOT try to load the data into ALL the graphs when the app loads even if they are not all visible? Should I let each graph initialize its own data when it is loaded? Why is Flex inconsistent in its behavior? Aha-- before posting this I did another round of Googling, and finally stumbled across a property called creationPolicy. Apparently by default this property is set to "auto" and Flex decides when to create your objects. If I were to set it to "all" for all my charts, they would be created immediately. Now, this DOESN'T explain why Flex seemed to randomly decide whether or not to create the hidden objects, but it does tell me what I need to do to fix my problem. Given the performance implications though, I think I am NOT going to initialize all the charts. I think I will set them up to not load their data until they come into view.
anonymous
This is a fairly common thing that flex does. It delays creation of objects until they are really needed. Why create that second tabs children until you actually want to click there?
Charles Dale
Don't set the dataProvider properties directly - use data binding and dependency injection and you don't have to care when your view is initialized (or not). Sounds scarey but it's really easy:
In your return_data function, assign the test data to an ArrayCollection, e.g. called testData. Then add a public property "chartData:ArrayCollection" to each of your tabs, and assign testData to it when you declare the tab. I.e.
<component:Tab1 label="Tab 1" chartData="{testData}" /> <component:Tab2 label="Tab 2" chartData="{testData}" />
That's the dependency injection part.
Then when you define the chart inside those tabs, do it like this:
<mx:LineChart dataProvider="{chartData}" />
Data binding ensures that any changes to the testData collection will get propagated through to your chart.
Cheers, Charlie
shaun
you can set the creationpolicy on the container to have all initialized
Douglas Knudsen
Something to note: "Most Flex objects have two events that fire during their creation process." is just not correct. Objects deriving from UIComponent will though, but a object I create in Flex called MyDataVo will of course not. FlexSprite also does not fire these events for example.
Problems you are encountering can be solved via binding as Charles pointed out or via the invalidation/validation process. See http://www.cubicleman.com/2008/04/10/on-using-a-invalidation-approach-in-flex/ for info on the latter. Setting the creationPolicy to all is not usually a good thing to do, rarely need to actually do that, of course in a small app no big deal.
DK
John Gag
I was wondering the exact same thing. I have 5 graphs on a dashboard and each graph has a button that when clicked shows a datagrid of data. I did this through a view stack. For some reason I was getting the same inconsistent error. Thanks for your help, I am going to try some of the solutions.
Brad Wood
@Charles: Thanks for the pointers. I didn't know I could "inject" a property in by simply specifying an attribute to the tag.
@DK: Thank you for the clarification on the UIComponents. I updated the wording of the post to be correct. I will check out your link to invalidation/validation.
Brad Wood
@John: It's funny you say that-- today I need to figure out the same thing. The user clicks a button and it pops up a datagrid with the result that populates the graph. Perhaps you can give me a good link to learn about the "view stack". Thanks.
Josh Nathanson
Hey Brad -- thanks for blogging your climb up the Flex learning curve, I encourage you to keep doing so. I might be in the same boat myself soon, so I am following these posts closely.