One way to create reusable components is to define them as template components. A template component defines properties with a general data type that lets the component user specify an object of a concrete data type when using the component. By using a general data type to define component properties, you create highly reusable components that can work with many different types of objects.
A standard component defines a property with a concrete data type, such as Number or String. The component user must then pass a value that exactly matches the property's data type or else Flex issues a compiler error.
A template component is a component in which one or more of its properties is defined with a general data type. This property serves as a slot for values that can be of the exact data type of the property, or of a value of a subclass of the data type. For example, to accept any Flex visual component as a property value, you define the data type of the property as UIComponent. To accept only container components, you define the data type of the property as Container.
When you use the template component in an application, the component user sets the property value to be an object with a concrete data type. You can think of the property as a placeholder for information, where it is up to the component user, rather than the component developer, to define the actual data type of the property.
The following example shows an application that uses a template component called MyTemplateComponent:
<?xml version="1.0"?>
<!-- templating/MainTemplateButton.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:MyComp="myComponents.*"
height="700" width="700">
<s:Panel>
<MyComp:MyTemplateComponent id="myTComp1">
<MyComp:topRow>
<s:Label text="top component"/>
</MyComp:topRow>
<MyComp:bottomRow>
<s:Button label="Button 1"/>
<s:Button label="Button 2"/>
<s:Button label="Button 3"/>
</MyComp:bottomRow>
</MyComp:MyTemplateComponent>
</s:Panel>
</s:Application>
The MyTemplateComponent takes two properties:
The topRow property specifies the single Flex component that appears in the top row of the VGroup container.
The bottomRow property specifies one or more Flex components that appear along the bottom row of the VGroup container.
The implementation of the MyTemplateComponent consists of a VGroup container that displays its children in two rows. The following image shows the output of this application:
The implementation of the topRow and bottomRow properties lets you specify any Flex component as a value, as the following example shows:
<?xml version="1.0"?>
<!-- templating/MainTemplateLink.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:MyComp="myComponents.*"
height="700" width="700">
<s:Panel>
<MyComp:MyTemplateComponent id="myTComp2">
<MyComp:topRow>
<s:TextArea text="top component"/>
</MyComp:topRow>
<MyComp:bottomRow>
<mx:LinkButton label="Link 1"/>
<mx:LinkButton label="Link 2"/>
<mx:LinkButton label="Link 3"/>
</MyComp:bottomRow>
</MyComp:MyTemplateComponent>
</s:Panel>
</s:Application>
In this example, the top component is a TextArea control, and the bottom components are two LinkButton controls.
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.
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.
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.
You can define component properties of type IDeferredInstance.
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.
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.
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>
Navigation
Adobe and Adobe Flash Platform are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries and are used by permission from Adobe. No other license to the Adobe trademarks are granted.