Flex 3.0 simple navigation question

msukow
msukow used Ask the Experts™
on
I had the following code originally which worked:
  <mx:Button id="loginButtonID" click="currentState='state1'"
        x="175" y="5" styleName="loginButton" buttonMode="true" cornerRadius="0" />

I now have changed the button to be a component. The click state doesn't work any more because the path is not valid anymore (I think). I tried the following, but it was no good as well:
  <mx:Button id="loginButtonID" click="currentState='/state1'"
        x="175" y="5" styleName="loginButton" buttonMode="true" cornerRadius="0" />

The application mxml is in the same folder as the component folder, which holds the login.mxml component.

What should the click code be?
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
Where is 'state1' defined? As you've made the button a (presumably MXML) component by itself, you probably want to move the state definition inside the component as well.
I assume state1 is part of the main application ,not the login component.

<mx:Button id="loginButtonID" click="parent.currentState='state1'"
        x="175" y="5" styleName="loginButton" buttonMode="true" cornerRadius="0" />

or

<mx:Button id="loginButtonID" click="Application.application.currentState='state1'"
        x="175" y="5" styleName="loginButton" buttonMode="true" cornerRadius="0" />


The proper way to do this would be to use events:

in your button component code:
<mx:Button id="loginButtonID" click="dispatchEvent( new Event('loginClicked'))"
        x="175" y="5" styleName="loginButton" buttonMode="true" cornerRadius="0" />

and in init function of the module that has the state 1 defined, assuming the login component instance was name 'button1':

button1.addEventListener("loginClicked", doClick);

private function doClick( e:Event):void
{
    currentState = 'state1';
}

Author

Commented:
I updated the code below in the main app, as well as the login component. I know I am close, but I can't quite wrap my head around the process. I now get the following error on line 22 of the main app (loginButtonID.addEventListener("loginClicked", loginClick);)

1120: Access of undefined property loginButtonID.


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:login="components.*"
                layout="absolute" height="612" width="792"
                backgroundColor="#FFFFFF" backgroundAlpha="0" currentState="initialState">
	
	<mx:Script>
	  <![CDATA[
	    public function captureLoginEvent (event:Event):void
	    {
	      trace("logged in");
	      //click="currentState='productGroupingState'"
	    }
	    
	    private function loginClick( e:Event):void
      {
        currentState = 'state1';
      }
      //
      private function initMSS():void
      {
        loginButtonID.addEventListener("loginClicked", loginClick);
      }
	    
	  ]]>
	  
	  
	</mx:Script><mx:states>
		<mx:State name="initialState">
			<mx:AddChild position="lastChild">
        <login:initialScreen x="0" y="245" width="792"/>
      </mx:AddChild>
      <mx:AddChild position="lastChild">
	      <login:LoginComp x="541" y="10" />
	      
	    </mx:AddChild>
      <mx:AddChild position="lastChild">
        <mx:Image x="150" y="155" source="images/mdiCreativeLogo.swf"/>
      </mx:AddChild>
      <mx:RemoveChild target="{mdiCreativeLogoMainID}"/>
      <mx:RemoveChild target="{monthNavID}"/>
      <mx:RemoveChild target="{cartButtonID}"/>
      
		</mx:State>
		<mx:State name="productGroupingState"/>
      
	</mx:states>
	
	<mx:Style source="supportFiles/Button.css" />
  
  <mx:Style>
      @font-face {
    	src:url("components/supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
  		}
  		@font-face {
    	src:url("components/supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
    	fontStyle: bold;
  		}
  			
	</mx:Style>
	
	<mx:Image id="mdiCreativeLogoMainID" source="images/mdiCreativeLogo.swf"
		        x="28" y="71" height="42.559" width="318.338"/>
 
  <login:monthNavComp  id="monthNavID" y="144" width="792"/>
 
  <mx:Button x="641" y="67.7" styleName="cartButton" id="cartButtonID" width="65.8" height="55.3" click="currentState='cart'" buttonMode="true"/>
</mx:Application>
 
 
---------------------------------------------------------------
 
 
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" borderThickness="0" borderColor="0x000000">
  <mx:Style source="supportFiles/Button.css" />
  
  <mx:Style>
      @font-face {
    	src:url("supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
  		}
  		@font-face {
    	src:url("supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
    	fontStyle: bold;
  		}
  			
	</mx:Style>
	
    <mx:Script>
                <![CDATA[
                        import mx.controls.Alert;
                        
                        private function checkLogin():void {
                          if(usernameText.text == "user" && passwordText.text == "pass"){
                                Alert.show("Success!");
                          }else{
                                Alert.show("Fail!");
                          }
                        }
                ]]>
        </mx:Script>
        
        <mx:Form id="loginForm" x="0" y="0" verticalGap="3" paddingTop="0" paddingBottom="0" paddingLeft="0" paddingRight="0">  
                <mx:FormItem id="usernameFormItem" label="Username:">
                        <mx:TextInput id="usernameText"/>
                </mx:FormItem>
                <mx:FormItem id="passwordFormItem" label="Password:">
                        <mx:TextInput id="passwordText"/>
                </mx:FormItem>
                <mx:Button id="loginButtonID" click="dispatchEvent( new Event('loginClicked'))"
	      styleName="loginButton" buttonMode="true" cornerRadius="0"/>
                <!--<mx:Button id="loginButtonID" label="Login" click="checkLogin()" styleName="loginButton" buttonMode="true" cornerRadius="0"/>-->
        </mx:Form>
 
</mx:Canvas>

Open in new window

Success in ‘20 With a Profitable Pricing Strategy

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Commented:
Seems hobbit72 guessed correctly what you were trying to do with states.

OK assuming your loginButtonID control is within your monthNavComp control:

monthNavComp.loginButtonID.addEventListener("loginClicked", loginClick);

OR

monthNavComp.addEventListener("loginClicked", loginClick);
but the click action in your subcontrol needs to be: click="dispatchEvent( new Event('loginClicked', true))" to allow event bubbling from loginButtonID up to monthNavComp.

Author

Commented:
It is actually located in the LoginComp component. I updated the script on the main app to be:

      private function initProgram():void
      {
        LoginComp.loginButtonID.addEventListener("loginClicked", loginClick);
      }

But I don't know how to trigger "initProgram" and I get the following error now:
1119: Access of possibly undefined property loginButtonID through a reference with static type Class.      mdiCreative/src      mdiCreative.mxml      line 22

Author

Commented:
Where should "click="dispatchEvent( new Event('loginClicked', true))" go? On the LoginComp.mxml component?

Commented:
Difficult to make step by step suggestions when the landscape is changing...

Can you post the code as you now have it?
I'm online for the next 30 mins if you want a quick answer.

Author

Commented:
Main app:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:login="components.*"
                layout="absolute" height="612" width="792"
                backgroundColor="#FFFFFF" backgroundAlpha="0" currentState="initialState">
      
      <mx:Script>
        <![CDATA[
          public function captureLoginEvent (event:Event):void
          {
            trace("logged in");
            //click="currentState='productGroupingState'"
          }
          
          private function loginClick( e:Event):void
      {
        currentState = 'state1';
      }
      //
      private function initProgram():void
      {
        LoginComp.loginButtonID.addEventListener("loginClicked", loginClick);
      }
          
        ]]>
        
        
      </mx:Script><mx:states>
            <mx:State name="initialState">
                  <mx:AddChild position="lastChild">
        <login:initialScreen x="0" y="245" width="792"/>
      </mx:AddChild>
      <mx:AddChild position="lastChild">
            <login:LoginComp x="541" y="10" />
            
          </mx:AddChild>
      <mx:AddChild position="lastChild">
        <mx:Image x="150" y="155" source="images/mdiCreativeLogo.swf"/>
      </mx:AddChild>
      <mx:RemoveChild target="{mdiCreativeLogoMainID}"/>
      <mx:RemoveChild target="{monthNavID}"/>
      <mx:RemoveChild target="{cartButtonID}"/>
     
            </mx:State>
            <mx:State name="productGroupingState"/>
     
      </mx:states>
      
      <mx:Style source="supportFiles/Button.css" />
 
  <mx:Style>
      @font-face {
          src:url("components/supportFiles/fonts.swf");
          fontFamily: "Helvetica";
              }
              @font-face {
          src:url("components/supportFiles/fonts.swf");
          fontFamily: "Helvetica";
          fontStyle: bold;
              }
                    
      </mx:Style>
      
      <mx:Image id="mdiCreativeLogoMainID" source="images/mdiCreativeLogo.swf"
                    x="28" y="71" height="42.559" width="318.338"/>

  <login:monthNavComp  id="monthNavID" y="144" width="792"/>

  <mx:Button x="641" y="67.7" styleName="cartButton" id="cartButtonID" width="65.8" height="55.3" click="currentState='cart'" buttonMode="true"/>

</mx:Application>



-------------------------------------------------------

Login component below
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" borderThickness="0" borderColor="0x000000">
  <mx:Style source="supportFiles/Button.css" />
  
  <mx:Style>
      @font-face {
    	src:url("supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
  		}
  		@font-face {
    	src:url("supportFiles/fonts.swf");
    	fontFamily: "Helvetica";
    	fontStyle: bold;
  		}
  			
	</mx:Style>
	
    <mx:Script>
                <![CDATA[
                        import mx.controls.Alert;
                        
                        private function checkLogin():void {
                          if(usernameText.text == "user" && passwordText.text == "pass"){
                                Alert.show("Success!");
                          }else{
                                Alert.show("Fail!");
                          }
                        }
                ]]>
        </mx:Script>
        
        <mx:Form id="loginForm" x="0" y="0" verticalGap="3" paddingTop="0" paddingBottom="0" paddingLeft="0" paddingRight="0">  
                <mx:FormItem id="usernameFormItem" label="Username:">
                        <mx:TextInput id="usernameText"/>
                </mx:FormItem>
                <mx:FormItem id="passwordFormItem" label="Password:">
                        <mx:TextInput id="passwordText"/>
                </mx:FormItem>
                
                <mx:Button id="loginButtonID" click="dispatchEvent( new Event('loginClicked', true))"
	      styleName="loginButton" buttonMode="true" cornerRadius="0"/>
                <!--<mx:Button id="loginButtonID" label="Login" click="checkLogin()" styleName="loginButton" buttonMode="true" cornerRadius="0"/>-->
        </mx:Form>
 
</mx:Canvas>

Open in new window

Commented:
click="dispatchEvent( new Event('loginClicked', true))"

goes on the loginButtonID button.

You had before:

 <mx:Button id="loginButtonID" click="dispatchEvent( new Event('loginClicked'))"
              styleName="loginButton" buttonMode="true" cornerRadius="0"/>

You would make that:

 <mx:Button id="loginButtonID" click="dispatchEvent( new Event('loginClicked', true))"
              styleName="loginButton" buttonMode="true" cornerRadius="0"/>

to make the event 'bubble'.

Then in your containing control (the application root in this case) you could use:

private function initMSS():void
      {
        monthNavID.addEventListener("loginClicked", loginClick); // this will be 'heard' from the monthNavID control because the event bubbled up from loginButtonID control to it's parent, monthNavID.
      }


However you've changed all the code on us now so the names of your controls have changed, but hopefully you can decipher where we're up to from this.

You don't have to use event bubbling.
Right from the example you posted you could have used:

private function initMSS():void
      {
        monthNavID.loginButtonID.addEventListener("loginClicked", loginClick); // this will be 'heard' from the monthNavID control because the event bubbled up from loginButtonID control to it's parent, monthNavID.
      }

so without bubbling, you would have to listen to the control sending the event directly.

To make initMSS (or now initProgram?) fire, put a 'creationComplete' event on your login control, e.g.

<login:monthNavComp  id="monthNavID" y="144" width="792" creationComplete="initProgram();"/>

Commented:
1.

You main login component seems to be:
<login:monthNavComp  id="monthNavID" y="144" width="792"/>

This is type 'monthNavComp' - it's id is 'monthNavID'.
'loginButtonID' is the id of a button control within your 'monthNavComp' control

2.
Your initProgram function should be:

private function initProgram():void
      {
        monthNavID.addEventListener("loginClicked", loginClick); // because you allowed event bubbling [click="dispatchEvent( new Event('loginClicked', true))]
      }

3.
For simplicity for now, to get initProgram called, do this:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:login="components.*"
                layout="absolute" height="612" width="792"
                backgroundColor="#FFFFFF" backgroundAlpha="0" currentState="initialState"
                creationComplete="initProgram();">

Author

Commented:
Hmmm... LoginComp should handle the login only.

And monthNavComp should just be a component that handles 6 buttons, which display the different months. They should not affect each other. It is obvious I have something wrong with my logic. I tried to send the archived flex files, but it didn't let me. Can I email you the files somehow?
Commented:
Oh man, I've just seen it.

I'll put that down as tiredness, just didn't see you creating the LoginComp component in your states, I'd assumed it was monthNavComp (however strange that seemed to me!!)

OK, starting again.

I see you create your login component here within an mx:State declaration for 'initialState':
   <mx:AddChild position="lastChild">
      <login:LoginComp x="541" y="10" />
   </mx:AddChild>

I've either created components statically through MXML or dynamically through AS, but never through states. This just feels unintuitive.
Gut feeling tells me this isn't a great idea as it's going to be hard to tell when they're created/destroyed and how to handle event listeners.

However, a few things come to mind.
a) the LoginComp component instance does not have an id, therefore you have no way of referencing it from code once created.
Try this:
   <mx:AddChild position="lastChild">
      <login:LoginComp id='loginComp' x="541" y="10" />
   </mx:AddChild>

b) Currently you don't necessarily know when the LoginComp control will be created, but when it is you want to listen for the loginButtonID click event, and when it is destroyed you don't - so you're going to need to know both when it is created and destroyed, and on each event create and destroy event listeners for the login button click.

You could try:
   <mx:AddChild position="lastChild">
      <login:LoginComp id='loginComp' x="541" y="10" creationComplete='initProgram();" />
   </mx:AddChild>

c) initProgram would therefore be: (probably better to rename as initLogin)
private function initProgram():void
      { // we can refer to loginComp as we gave your login control an id 'loginComp'
        loginComp.addEventListener("loginClicked", loginClick); // because you allowed event bubbling
      }

Remembering that you're allowing event bubbling from your loginButtonID control [click="dispatchEvent( new Event('loginClicked', true))"]

d) so when loginComp is created, initProgram is called, which adds an event listener to loginComp, listening for an event 'loginClicked' bubbled up from loginButtonID, so when loginButtonID is clicked, the 'loginClicked' event fires and loginClick is called, which then sets the state to 'state1'??? At this point I don't see 'state1' defined, so what on earth happens to your controls now? If your new state removes the loginComp component, you should remove the eventlistener on it...

I would consider rapidly moving away from MXML defined states for control creation/destruction and look into AS dynamic creation - that should give you much more control of what's going on here.

Author

Commented:
Thanks for the hard work - I will look into this tomorrow morning.

Commented:
Update?

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial