ejbelair

I am an IT Professional from southwestern CT. I currently work for Private Client Resources, LLC, in Wilton, CT designing and developing Rich Internet Applications using Flex, ColdFusion, and SQL Server. I hope to share here the little nuggets of knowledge that I find along the way.

Follow me on Twitter

StackOverflow Profile

Friday, June 12, 2009

Flex Accordion - View State vs. Dynamic Child Creation

Since I first started working with Flex (Beta 3), I have had to deal with switching views. I remember spending much time learning how ViewStacks and States worked, and often went with ViewStacks - or their extensions - simply for ease of implementation and code-readability. Today, however, I found a case where States made much more sense, and I completely refactored a component I built months earlier. I have an Accordion component that has different children based on the user's permissions, with one child that is always present. I keep track of the user's permissions using a one-character code (i.e. "F", "B", "A", et al). My original design was an ActionScript Class that extended Accordion, adding the children to the Container dynamically based on the user's permission code:

package
{
  import mx.containers.Accordion;

  public class Filters extends Accordion
  {
      private var _loginCode:String = "";
    
      public function set loginCode(value:String):void
      {
          _loginCode = value;
        
          addChildren();
      }
    
      public function get loginCode():String
      {
          return _loginCode;
      }
    
      public function Filters()
      {
          super();
      }
    
      private function addChildren():void
      {
          removeAllChildren();
        
          var loginCodesToViewSeasons:Array = ["F"];
        
          if (loginCodesToViewSeasons.indexOf(_loginCode) != -1)
              super.addChild(new SeasonsFilter());
        
          var loginCodesToViewEpisodes:Array =
              loginCodesToViewSeasons.concat("B");
        
          if (loginCodesToViewEpisodes.indexOf(_loginCode) != -1)
              super.addChild(new EpisodesFilter());
        
          var loginCodesToViewDates:Array =
              loginCodesToViewEpisodes.concat("A");
        
          if (loginCodesToViewDates.indexOf(_loginCode) != -1)
              super.addChild(new DateFilter());
        
          super.addChild(new CharactersFilter());
      }
  }
}
This seemed ugly to me - building multiple arrays just to check a single character. "States should be able to do this..." kept running through my mind. Well, I was recently tasked with making a "lite" version of the existing application, with some modifications needed for this component - another child component needed to be added as the first child of the Accordion, always to be shown. So, I started trying to do this with States. It turned out to be very easy to do - and easy on the eyes. I started off by creating a new MXML component, so that the States could be laid out and easily readable:

<?xml version="1.0" encoding="utf-8"?>
<mx:Accordion xmlns:mx="http://www.adobe.com/2006/mxml"
 xmlns:local="*" currentState="{loginCode}">
 <mx:Script>
     <![CDATA[
         [Bindable]
         public var loginCode:String = "";
     ]]>
 </mx:Script>

 <local:KeyWordFilter />

 <mx:states>
     <mx:State name="F" basedOn="B">
         <mx:AddChild relativeTo="{episodesChild}"
                position="before">
             <local:SeasonsFilter />
         </mx:AddChild>
     </mx:State>
  
     <mx:State name="B" basedOn="A">
         <mx:AddChild relativeTo="{datesChild}"
                position="before">
             <local:EpisodesFilter id="episodesChild" />
         </mx:AddChild>
     </mx:State>
  
     <mx:State name="A" basedOn="S">
         <mx:AddChild relativeTo="{charactersChild}"
                position="before">
             <local:DateFilter id="datesChild" />
         </mx:AddChild>
     </mx:State>
 </mx:states>

 <local:CharactersFilter id="charactersChild" />
</mx:Accordion>
The key was the State property basedOn and the AddChild properties relativeTo and position. The State property basedOn allows to "extend" another State. That is, when the State named "F" is set as the currentState, it first runs State "B", which in turn first runs State "A". The only problem with this was that it obviously started with "A", then added "B", then added "F" - the opposite order of what I want. That's where relativeTo and position come in. This basically allows you to tell the AddChild command where to place the component in relation to another component. The options for position are "firstChild", "lastChild", "before", and "after", giving you plenty of flexibility in your layout.

Labels: , ,