views:

567

answers:

4

I'm trying componentize one of the pieces of UI in an AIR application that I'm developing in Flex. In this example, I want to display file information on a single line (which has an icon, some text/link and the size).

My code looks like this (component is called FileDisplay):

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"&gt;
    <mx:Script>
     <![CDATA[
      public function set iconType(source:String):void {
       this.ficon.source = source;
      }

      public function set fileName(name:String):void {
       this.fname.htmlText = name;
      }

      public function set fileSize(size:String):void {
       this.fsize.text = size; 
      }
     ]]>
    </mx:Script>
    <mx:Image id="ficon" />
    <mx:Label id="fname" left="20" right="30" text="Filename" />
    <mx:Label id="fsize" right="0" text="0 K" />
</mx:Canvas>

When I'm using this component in my main application, the actionscript looks like:

for each (var file:XML in result.files) {
    var fd:FileDisplay = new FileDisplay();
    fd.fileName = '<a href="blah">'+file.name+'</a>';
    fd.iconType = getFileTypeIcon(file.name);
    fd.fileSize = getFileSizeString(file.size);

    this.file_list.addChild(fd);
}

However, when I do this, I get an error: Error #1009: Cannot access a property or method of a null object reference. This is because the child components of the FileDisplay are null (or at least they show up that way in the debugger).

Does anyone know if there's a way around this? Am I supposed to be waiting for events indicating the child components were created? Is there a more common pattern that solves this problem?

For now I can manually do everything in ActionScript in my main app (create a Canvas and add children to it) but I would appreciate any insight on how to separate the code more cleanly.

+1  A: 

The subcomponents haven't been loaded yet.

Read this: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html#203434.

Then, when like me, you don't understand it (and it's not reliable), listen for the FlexEvent.CREATION_COMPLETE within FileDisplay, and apply your child component properties there.

Or better yet, create the three children programmatically in the "createChildren" function, and apply the settings there.

Both of these methods assume that you're setting filename, icontype, and filesize as local members before applying them to the children components, which you should be doing regardless.

Glenn
Thanks for the insight. The docs on how to use "advanced" custom components are pretty confusing. I get the feeling my case isn't all that uncommon they should probably have more examples like it.
Chris R
+1  A: 

What is the parent component that holds the FileDisplay component? If you're sure that the error is coming from the fact that the child components of FileDisplay aren't being instantiated then you might want to look at the creationPolicy attribute and make sure it's set to ContainerCreationPolicy.ALL on that parent component.

=Ryan

ryanstewart
Doesn't matter. The children of FileDisplay don't have time to be created between the "new FileDisplay()" call, and the assignment of the properties.
Glenn
+3  A: 

Bindable to the rescue:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml"&gt;
    <mx:Script>
        <![CDATA[
                [Bindable]
                public var iconType:String;
                [Bindable]
                public var fileName:String = "Filename";
                [Bindable]
                public var fileSize:String = "0 K";
        ]]>
    </mx:Script>
    <mx:Image id="ficon" source="{iconType}"/>
    <mx:Label id="fname" left="20" right="30" text="{fileName}" />
    <mx:Label id="fsize" right="0" text="{fileSize}" />
</mx:Canvas>

the values will be automatically updated when the components are created.

Amarghosh
A: 

In addition to setting the CreationPolicy to all, you need to add the DisplayObject to the stage via addChild. The children of FileDisplay are not created until you add it is added to the stage. So do:

for each (var file:XML in result.files) {
   var fd:FileDisplay = new FileDisplay();
   this.file_list.addChild(fd);

   fd.fileName = '<a href="blah">'+file.name+'</a>';
   fd.iconType = getFileTypeIcon(file.name);
   fd.fileSize = getFileSizeString(file.size);    
}
Tommy