views:

316

answers:

3

I'm used to building applications using pure AS3. I always pass dependencies into the constructor of classes I make, but this method seems to not work out well for Flex MXML views.

It seems like I should define setters on the MXML class, which map to attributes in the tag/class instantiation. But using this method I cannot specify which properties are required and in what order I expect them etc.

What is the preferred method to give a Flex view it's dependencies?

A: 

A pattern I've used a couple times was to define a public init() method in the MXML which takes the argument that would normally have gone in the constructor. Then, whatever instantiates that MXML component is responsible for calling init() before using it.

Another way would be to create setters for the properties like you mentioned. In those setters store the values that are passed, then call invalidateProperties(). Then override the commitProperties() method in the MXML, and the first time that's called do your initialization (and maybe throw an exception if the needed properties weren't provided). As long as the user of your class sets all the properties before adding the component to the display list then it will work fine (I don't believe commitProperties() is called until after a component is added to the display list, either by being declared in MXML or by passing it to an addChild() call).

I haven't ever tried that second method (only just thought of it now), but it should work.

Herms
A: 

You can't force people to use parameters in the constructor, but you can force then to set properties before adding the item to the stage.

How's this:

<mx:HBox
         added="{checkProps()}">

    <mx:Script>
        <![CDATA[
            public var prop1:String;
            public var prop2:String;

            private function checkProps():void
            {
                if( !( prop1 && prop2 ) )
                {
                    throw new Error( "Prop1 and prop2 must be set before "+
                                     "adding this to the stage" );
                }
            }
        ]]>
    </mx:Script>

</mx:HBox>

Realistically, if you're interested in forcing people to do something before adding it to the display list, then you're going to have to do something like this anyway.

Christopher W. Allen-Poole
A: 

There are a few things in Flex that you can override or listen to that are really important.

  1. FlexEvent.CREATION_COMPLETE - set an eventListener for this (I usually do it in the constructor but you could do it in MXML as creationComplete attribute) and it acts like your constructor. Use getters and setters to pass through references to your dependencies as MXML attributes and store them locally then inside this handler you will apply them.
  2. override protected function createChildren - this is called when it is time to add display list items to the component. You shouldn't do that during the constructor or creationComplete handlers. It is always tempting to addChild outside of this function but Adobe best practice is only to do so directly in this function.
  3. override protected function updateDisplayList - this is where your drawing logic should happen (if there is any) or positioning/alpha/rotation/etc of children. This will get called if a CSS property changes, a child changes size or position or anything else that the Flex framework thinks may cause you to want to redraw the screen. You can force an updateDisplayList to get called by calling invalidateDisplayList
  4. override protected function commitProperties - this is called when the dataProvider for a class is changed. Any time data within the component means you want to update internal data structures this should be called. You can force this to be called using invalidateProperties.
  5. FlexEvent.ADDED_TO_STAGE - If you need to know when the component is actually added to the stage you can listen for this. In practice I can't remember ever actually using it ...

Always remember to call the super equivalents -- forgetting to do so will often cause the component to fail to appear at all (this happens to me at least 4 or 5 times a project). Also be aware that if you first invalidateProperties and then commitProperties and then invalidateDisplayList and then updateDisplayList you may see some jerkyness ... that is, invalidateDisplayList as soon as you know you'll want a redraw to avoid delay.

Also don't get too invested in Flex 3 since Flex 4 is just around the corner and it is quite a bit different. I have a feeling that much of this will no longer apply in the new component framework (names Spark).


edit a typical class stub:

package
{
import mx.containers.Canvas;
import mx.events.FlexEvent;

public class TestComponent extends Canvas
{
 public function TestComponent()
 {
  super();

  addEventListener(FlexEvent.CREATION_COMPLETE, init);
 }

 // acts as constructor
 private function init(event:FlexEvent):void
 {
  // might as well be clean
  removeEventListener(FlexEvent.CREATION_COMPLETE, init);

  // do init stuff here
 }

 override protected function createChildren():void
 {
  super.createChildren();
  // do any addChilds here that are necessary
 }

 override protected function commitProperties():void
 {
  super.commitProperties();
  // update internal state when data changes
 }

 override protected function updateDisplayList(w:Number, h:Number):void
 {
  super.updateDisplayList(w, h);
  // do any drawing, positioning, rotation etc.
 }
}

}
James Fassett