Implementing a template component

The section About template components shows an example of a template component named MyTemplateComponent. Flex provides you with two primary ways to create template components:

  • Create properties with general data types, such as UIComponent or Container.

  • Create properties with the type IDeferredInstance.

Using general data types in a template component

One way to implement the component MyTemplateComponent (see About template components) is to define the properties topRow and bottomRow as type UIComponent. Users of the component can specify any object to these properties that is an instance of the UIComponent class, or an instance of a subclass of UIComponent.

The following code shows the implementation of MyTemplateComponent:

<?xml version="1.0"?>
<!-- templating/myComponents/MyTemplateComponent.mxml -->
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    initialize="init();">

    <fx:Script>
        <![CDATA[
        
            import spark.components.HGroup;
            import mx.core.UIComponent;
                
            // Define a property for the top component.
            public var topRow:UIComponent;

            // Define an Array of properties for a row of components.
            // Restrict the type of the Array elements 
            // to mx.core.UIComponent.          
            [ArrayElementType("mx.core.UIComponent")]
            public var bottomRow:Array;
    
            private function init():void {            
                // Add the top component to the VGroup container.
                addElement(topRow);

                // Create an HGroup container. This container 
                // is the parent container of the bottom row of components.
                var controlHGroup:HGroup = new HGroup();

                // Add the bottom row of components 
                // to the HGroup container.              
                for (var i:int = 0; i < bottomRow.length; i++)
                    controlHGroup.addElement(bottomRow[i]);

                // Add the HGroup container to the VGroup container.
                addElement(controlHGroup);
            }
        ]]>
    </fx:Script>
</s:VGroup>

For the bottomRow property, you define it as an Array and include the [ArrayElementType] metadata tag to specify to the compiler that the data type of the Array elements is also UIComponent. For more information on the [ArrayElementType] metadata tag, see Metadata tags in custom components.

Using IDeferredInstance in a template component

Deferred creation is a feature of Flex where Flex containers create only the controls that initially appear to the user. Flex then creates the container’s other descendants if the user navigates to them. For more information, see Improving startup performance.

You can create a template component that also takes advantage of deferred creation. Rather than having Flex create your component and its properties when the application loads, you can define a component that creates its properties only when a user navigates to the area of the application that uses the component. This is especially useful for large components that may have many child components. Flex view states make use of this feature.

The following example shows an alternative implementation for the MyTemplateComponent component shown in the section About template components, named MyTemplateComponentDeferred.mxml, by defining the topRow and bottomRow properties to be of type IDeferredInstance:

<?xml version="1.0"?>
<!-- templating/myComponents/MyTemplateComponentDeferred.mxml -->
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    initialize="init();">

    <fx:Script>
        <![CDATA[
        
            import spark.components.HGroup;
            import mx.core.UIComponent;
                
            // Define a deferred property for the top component.
            public var topRow:IDeferredInstance;

            // Define an Array of deferred properties 
            // for a row of components.
            [ArrayElementType("mx.core.IDeferredInstance")]
            public var bottomRow:Array;
    
            private function init():void {  
                // Add the top component to the VGroup container.
                // Cast the IDeferredInstance object to UIComponent
                // so that you can add it to the parent container.      
                addElement(UIComponent(topRow.getInstance()));

                // Create an HGroup container. This container 
                // is the parent container of the bottom row of components.
                var controlHGroup:HGroup = new HGroup();
                
                // Add the bottom row of components 
                // to the HGroup container.              
                for (var i:int = 0; i < bottomRow.length; i++)
            controlHGroup.addElement(UIComponent(bottomRow[i].getInstance()));

                // Add the HBox container to the VGroup container.
                addElement(controlHGroup);
            }
        ]]>
    </fx:Script>
</s:VGroup>

The IDeferredInstance interface defines a single method, getInstance(). Flex calls the getInstance() method to initialize a property when it creates an instance of the component. A subsequent call to the getInstance() method returns a reference to the property value.

In MXML, when the compiler encounters a value declaration for a property of type IDeferredInstance, instead of generating code to construct and assign the value to the property, the compiler generates code to construct and assign an IDeferredInstance implementation object, which then produces the value at run time.

You can pass any data type to a property of type IDeferredInstance. In the example in the section About template components, you pass a Label control to the topRow property, and three Button controls to the bottomRow property.

Notice in the example that the addElement() methods that take topRow and bottomRow as arguments cast them to UIComponent. This cast is necessary because the addElement() method can only add an object that implements the IUIComponent interface to a container, and the DeferredInstance.getInstance() method returns a value of type Object.

Defining properties using the IDeferredInstance interface

You can define component properties of type IDeferredInstance.

Defining a generic property

To define a generic property, one with no associated data type, you define its type as IDeferredInstance, as the following example shows:

// Define a deferred property for the top component. 
public var topRow:IDeferredInstance;

The user of the component can then specify an object of any type to the property. It is your responsibility in the component implementation to verify that the value passed by the user is of the correct data type.

Restricting the data type of a property

You use the [InstanceType] metadata tag to specify the allowed data type of a property of type IDeferredInstance, as the following example shows:

// Define a deferred property for the top component. 
[InstanceType("spark.components.Label")] 
public var topRow:IDeferredInstance;

The Flex compiler validates that users only assign values of the specified type to the property. In this example, if the component user sets the topRow property to a value of a type other than spark.components.Label, the compiler issues an error message.

Defining an array of template properties

You can define an Array of template properties, as the following example shows:

// Define an Array of deferred properties for a row of components. 
// Do not restrict the type of the component. 
[ArrayElementType("mx.core.IDeferredInstance")] 
public var bottomRow:Array; 
 
// Define an Array of deferred properties for a row of components. 
// Restrict the type of the component to mx.controls.Button. 
[InstanceType("mx.controls.Button")] 
[ArrayElementType("mx.core.IDeferredInstance")] 
public var bottomRow:Array;

In the first example, you can assign a value of any data type to the bottomRow property. Each array element's getInstance() method is not called until the element is used.

In the second example, you can only assign values of type spark.components.Button to it. Each Array element is created when the application loads. The following template component shows an alternative implementation of the MyTemplateComponent that restricts the type of components to be of type mx.controls.Button:

<?xml version="1.0"?>
<!-- templating/myComponents/MyTemplateComponentDeferredSpecific.mxml -->
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    initialize="init();">
    
    <fx:Script>
        <![CDATA[
        
            import spark.components.HGroup;
            import mx.core.UIComponent;
                
            [InstanceType("spark.components.Label")]
            public var topRow:IDeferredInstance;

            // Define an Array of deferred properties 
            // for a row of components.
            // Restrict the type of the component 
            // to mx.controls.Button.
            [InstanceType("spark.components.Button")]
            [ArrayElementType("mx.core.IDeferredInstance")]
            public var bottomRow:Array;
    
            private function init():void {            
                addElement(UIComponent(topRow.getInstance()));

                var controlHGroup:HGroup = new HGroup();
                for (var i:int = 0; i < bottomRow.length; i++)
            controlHGroup.addElement(UIComponent(bottomRow[i].getInstance()));

                addElement(controlHGroup);
            }
        ]]>
    </fx:Script>
</s:VGroup>