Search
Feeds
Aug 14 2008

Flex: Calling Methods of the Parent From a Custom Component

Posted by Jeff Anderson at 5:00 PM
1 comments
- Categories: Technical Articles | Adobe Flex

The other day I was working on an event management application for a client. It's a very basic app: a few different "views" to handle the administration of the application. Each view was built using a custom component.

I was refactoring code and adding enhancements when I noticed something interesting in all of them. Every component had a reference to the parent application and was calling methods directly. This immediately fired off some big warnings in my mind. What happens if we want to use the component in a different application or the parent application changes? Then we would need to go in and modify every component to be able to work with the parent application. With a larger application this can be time-consuming and expensive to do.

Here's a very simplistic of example of what was happening:

Main.mxml

<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:sanative="net.sanative.ui.*">
   <mx:script>
      <!--[CDATA[
         import mx.controls.Alert;

         public function isLoggedIn(isValidUser:Boolean):void {
            if (isValidUser) {
               Alert.show("logged in!");
            } else {
               Alert.show("not logged in!");
            }
         }
      ]]-->

   </mx:script>
   <sanative:login id="login">
</sanative:login>
</mx:application>

Login.mxml

<mx:panel xmlns:mx="http://www.adobe.com/2006/mxml" width="333" height="168" layout="absolute">
   <mx:script>
   <!--[CDATA[
      import mx.core.Application;
         
      private var ParentApp:* = Application.application;
         
      private function doLogin(e:MouseEvent):void {
         //assume login is good for the purposes of the example
         var isValidUser:Boolean = true;
         //call parent app method
         ParentApp.isLoggedIn(isValidUser);
      }
   ]]-->

   </mx:script>
   <mx:form x="10" y="10" id="frmLogin" label="Login">
      <mx:formitem label="User Name:" id="login_itm" required="true">
         <mx:textinput id="login">
      </mx:textinput>
      <mx:formitem label="Password:" id="password_itm" required="true">
         <mx:textinput id="password" displayaspassword="true">
      </mx:textinput>
   </mx:formitem>
   <mx:button id="btn_login" x="222" y="96" label="Login" width="65" click="doLogin(event);">
</mx:button>
</mx:formitem></mx:form></mx:panel>

As you can see it's a very simple login panel. Once the user clicks the Login button, the component calls the isLoggedIn() method of the ParentApplication which then shows an Alert to the user. Mind you this is correct, functioning code. (I left out the authentication logic in these examples because it is not the purpose of this article.)

"So how do you call a method in the parent app of a custom component?" you ask. The simple answer is you shouldn't. "But I need a way to do some other things in the parent app after I'm done in the custom component! There has to be a way!" One of the great things about Flex is that there are many ways to solve one problem. Just some are better than others. In this case you would dispatch an event from the custom component that bubbles up into the parent application which has a listener waiting to act on that event.

Here's an example:

Main2.mxml

<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:sanative="net.sanative.ui.*" creationcomplete="init();">
   <mx:script>
      <!--[CDATA[
         import mx.controls.Alert;
         import net.sanative.event.CustomEvent;
         import net.sanative.ui.Login2;
         
         private function init():void {
            this.addEventListener(Login2.LOGIN_SUCCESS,isLoggedIn);
         }
         
         public function isLoggedIn(e:CustomEvent):void {
            var name:String = e.params.name as String;
            Alert.show("Welcome back " + name + "!");
         }
      ]]-->

   </mx:script>
   <sanative:login2 id="login">
</sanative:login2>
</mx:application>

Login2.mxml

<mx:panel xmlns:mx="http://www.adobe.com/2006/mxml" width="333" height="168" layout="absolute">
   <mx:script>
      <!--[CDATA[
         import net.sanative.event.CustomEvent;
         
         public static const LOGIN_SUCCESS:String = "loginSuccess";
         
         private function doLogin(e:MouseEvent):void {
            var params:Object = new Object();
            params.name = login.text;
            var loginEvent:CustomEvent = new CustomEvent(LOGIN_SUCCESS, params, true);
            dispatchEvent(loginEvent);
         }
      ]]-->

   </mx:script>
   <mx:form x="10" y="10" id="frmLogin" label="Login">
      <mx:formitem label="User Name:" id="login_itm" required="true">
         <mx:textinput id="login">
      </mx:textinput>
      <mx:formitem label="Password:" id="password_itm" required="true">
         <mx:textinput id="password" displayaspassword="true">
      </mx:textinput>
   </mx:formitem>
   <mx:button id="btn_login" x="222" y="96" label="Login" width="65" click="doLogin(event);">
</mx:button>
</mx:formitem></mx:form></mx:panel>

CustomEvent.as

package net.sanative.event
{
   import flash.events.Event;

   public class CustomEvent extends Event
   {

      public var params:Object;

      public function CustomEvent(type:String, params:Object=null, bubbles:Boolean=false, cancelable:Boolean=false)
      {
         super(type, bubbles, cancelable);
         this.params = params;
      }

      public override function clone():Event
      {
         return new CustomEvent(type, this.params, bubbles, cancelable);
      }

      public override function toString():String
      {
         return formatToString("CustomEvent", "params", "type", "bubbles", "cancelable");
      }
   }
}

In the above example, I am using the CustomEvent class instead of the normal Event class because I need to pass some data back to the parent application from the Login2 component. In the Login2.mxml code, when the user clicks the login button a CustomEvent is dispatched. It is able to "bubble" because of its properties that were set, and the parent application can capture that event and handle it accordingly. The act of bubbling simply means that the event can be heard by components ABOVE the hierarchy of the component firing the event. This allows us to use this generic login component in any app and not worry about how the parent application uses the data that is passed from it.

Learning and sticking to this principal will enable your components to be easily moved from one project into another without changing the code.

Files: callingParentMethodFromComponent.zip

Comments

Khalil M. Shams

Khalil M. Shams wrote on 08/15/08 10:19 AM

Jeff, this is a great tip ! You really have solved a problem that kept bugging me for long. Keep it up.

Write your comment



(it will not be displayed)