views:

50

answers:

3

I'm new to flex and actionscript. I'm trying to create a small flex app that has multiple states, but if I have nested containers, it looks like some of the objects are not getting initialized when I expected them to be, even when I have the creationPolicy set to "all."

I've reduced the issue to a small example, with a commented block showing when it does work.

Using the existing code, I get this error: "TypeError: Error #1009: Cannot access a property or method of a null object reference at main/init()" and the event handlers are not installed.

If I instead use the commented block, which has the Panel and VBox elements removed, it does work.

I know I could add a click attribute to the mxml elements, but this is just a simplified example, and I'm more interested knowing why the objects are not initialized when the app loads.

<mx:Application 
  xmlns:mx="http://www.adobe.com/2006/mxml"
  applicationComplete="init();"
  currentState="start">

<mx:Script>
  private function mainButtonHandler(event:Event):void{
    currentState = "start"
  }

  private function startButtonHandler(event:Event):void {
    currentState = "main";
  }

  public function init():void{
    mainButton.addEventListener(MouseEvent.CLICK, mainButtonHandler);
    startButton.addEventListener(MouseEvent.CLICK, startButtonHandler);
  }
</mx:Script>

<!-- this does not work -->
<mx:states>
  <mx:State name="main"> 
    <mx:AddChild creationPolicy="all">
      <mx:Panel creationPolicy="all">
        <mx:VBox creationPolicy="all">
          <mx:Button id="mainButton" label="Change to Start State"/>
        </mx:VBox>
      </mx:Panel>
    </mx:AddChild>
  </mx:State>

  <mx:State name="start"> 
    <mx:AddChild creationPolicy="all">
      <mx:Panel creationPolicy="all">
        <mx:VBox creationPolicy="all">
          <mx:Button id="startButton" label="Change to Main state"/>
        </mx:VBox>
      </mx:Panel>
    </mx:AddChild>
  </mx:State>
</mx:states>

<!-- this works -->
<!--
<mx:states>
  <mx:State name="main"> 
    <mx:AddChild creationPolicy="all">
      <mx:Button id="mainButton" label="Change to Start State"/>
    </mx:AddChild>
  </mx:State>

  <mx:State name="start"> 
    <mx:AddChild creationPolicy="all">
      <mx:Button id="startButton" label="Change to Main state"/>
    </mx:AddChild>
  </mx:State>
</mx:states>
-->

</mx:Application>

Thanks for any feedback.

A: 
  1. I think that business is in applicationComplete event, use creationComplete instead.
  2. In your code you said that commented code works in addChild, because it doesn't have double containers, as in uncommented code, such as Button Inside VBox inside of Panel.
  3. And finally, my advice is: please left all old flex version, and migrate to Flex 4 Gumbo, because it have great states logic.

Best Regards
Eugene

Eugene
Thanks for the help. I'll read more about Flex 4 states. However, I'm still having the same error whether I use applicationComplete or creationComplete. :-(
mshiltonj
+1  A: 

What if you will make next thing:

 <mx:State name="main"> 
      <mx:Panel creationPolicy="all">
        <mx:VBox creationPolicy="all">
         <mx:AddChild creationPolicy="all">
          <mx:Button id="mainButton" label="Change to Start State"/>
         </mx:AddChild>
        </mx:VBox>
      </mx:Panel>    
  </mx:State>

if you dont like this, switch your Button to custom component Based on Panel with VBox and Button, so you will rewrite it to almost like commented code, but you will use a containers.

And if we will answer to question Why this weird stuff about access comes true:

because you use 3 containers: Panel VBox and Button, and events dispatching all is done to application/creation-Complete after only panel is completed, not its children.

Clear?

Eugene
Eugene, I think I understand. But when can I expect (or force) all the mxml generated objects in a given context, like a state, to be initialized? When is it safe to touch then with actionscript?
mshiltonj
in a short bad way, just add creation Complete event handlers to each component, in other cases I should research a bit more, I'll let you know.
Eugene
A: 

Since the creationPolicy="all" attribute does not work the way I thought it would, then for complex state changes I set up a 'state initialization' method for each state, keyed off of the currentStateChange event. Here's an example of what I did, which also include an update to use Flex 4 states, even though I'm not using spark components in the example.

<s:Application 
  xmlns:fx="http://ns.adobe.com/mxml/2009"
  xmlns:mx="library://ns.adobe.com/flex/mx"
  xmlns:s="library://ns.adobe.com/flex/spark"
  applicationComplete="init();"
  currentStateChange="stateChange(event);"
  creationPolicy="all"
  currentState="bar"
  >

<fx:Script>
  import mx.controls.Alert;
  import mx.events.StateChangeEvent;

  private var InittedStates:Object = new Object();

  private function mainButtonHandler(event:Event):void{
    currentState = "bar"
  }

  private function startButtonHandler(event:Event):void {
    currentState = "foo";
  }

  public function stateChange(event:StateChangeEvent):void{
      if (event.newState === 'bar'){
        initBarState();
      }   
      else if(event.newState === 'foo'){
        initFooState();
      }   
  }

  public function initBarState():void {
    if (InittedStates.bar){ 
      return;
    }   
    startButton.addEventListener(MouseEvent.CLICK, startButtonHandler);
    InittedStates.bar = true;
  }

  public function initFooState():void {
    if (InittedStates.foo){ 
      return;
    }   
    mainButton.addEventListener(MouseEvent.CLICK, mainButtonHandler);
    InittedStates.foo = true;
  }

  public function init():void{
  }
</fx:Script>

<s:states>
  <s:State name="foo"/>
  <s:State name="bar"/>
</s:states>


<mx:Panel includeIn="foo" creationPolicy="all">
  <mx:VBox creationPolicy="all">
    <mx:Button id="mainButton" label="Change to Start State"/>
  </mx:VBox>
</mx:Panel>

<mx:Panel includeIn="bar" creationPolicy="all">
  <mx:VBox creationPolicy="all">
    <mx:Button id="startButton" label="Change to Main state"/>
  </mx:VBox>
</mx:Panel>

</s:Application>
mshiltonj
Even this didn't work. When I added a tab container in one state, objects in a panel in one of the tabs are still not initialized when the state change event is fired. So, I'm still not sure of the best way to (for example) attach event listeners to mxml generated objects. I'm still not grokking when I can expect all the objects to be ready. What am I missing?
mshiltonj