Pages

February 13, 2009

Item Renderer Basics

Posted by Jeremy Mitchell
One characteristic of a well-designed Flex application is the ability to present data in a visually-compelling manner. Outside of the database and within the model layer of a Flex application, data is stored in a flat or hierarchical fashion (i.e. ArrayCollection or XMLListCollection). Although a great format for data storage or transport, flat or hierarchical data structures lack significant meaning to the average user.

Fortunately, Flex provides a handful of UI controls designed to render flat or hierarchical data in an intuitive, meaningful and compelling manner within the UI. These UI controls include those that extend the ListBase class and are commonly referred to as list-based controls. Since a collection of data can also be referred to as a list, the name makes sense.

List-based controls include DataGrid, List, Tree, Menu, TileList and HorizontalList. Each list-based control contains a public dataprovider property. The value of the dataprovider property represents the data collection (or list) that requires the list-based control to render its contents in the UI.

Just as a list has items, so does a data collection. An item is analogous with a row or a record in a flat data set or a node within a hierarchical data set (i.e. XML). Data collections contain one or more items. In turn, each item may contain one or more properties.

The following example illustrates storage of flat data:



By default, list-based controls render the contents (properties) of each item (row or record) as simple text. In many scenarios, a textual rendering is sufficient. In others it is not.

Let's examine a scenario where you've selected a DataGrid control to render data stored in an ArrayCollection. In this scenario, your ArrayCollection houses a "list" of 10 employees. Each employee "record" has 4 properties - firstName, lastName, age, photo. Each employee property with the exception of the photo property fits nicely with the DataGrid's default textual rendering.

To customize the rendering format of an item property (i.e. employee photo), utilize the itemrenderer property. Item Renderers control the display of data for each item property. If you do not specify an Item Renderer, the default is used and the item property is displayed as simple text.

Since simple text is not an acceptable display for the employee photo property in our previous example, a custom Item Renderer is required.

3 types of custom Item Renderers exist:

1. Drop-In Item Renderers
2. Inline Item Renderers
3. External Item Renderers

Each type requires access to the data in which it will render and, therefore, any component acting as an Item Renderer must implement the IDataRenderer interface. The IDataRender interface enforces the implementation of a public getter and setter method for the data property. Without these methods, the list-based control that contains the data (host component) cannot pass item data to the component acting as an Item Renderer.

Fortunately, all Flex containers and many Flex controls implement the IDataRenderer interface and contain the required data property. To be sure, you may want to check the inheritance tree of the component you are considering as an Item Renderer. The inheritance tree can be found for each Flex component at the Flex 3 Language Reference.

Drop-In Item Renderers

Several UI controls within the Flex SDK were designed to serve as Drop-In Item Renderers - Button, CheckBox, DateField, Image, Label, NumericStepper, Text, TextArea, and TextInput. These controls all implement the IDropInListItemRenderer interface.

Drop-In Item Renderers are generic in nature and don't rely on specific data fields to render data. This allows them to be used with a wide range of data sets, hence, the term “drop-in”. Drop-In Item Renderers can be “dropped-in” to any list-based control regardless of the dataprovider’s data properties.

In our previous example, the employee photo property requires use of a custom Item Renderer to render properly in the UI. In this scenario the Image component satisfies our rendering needs out of the box. Implemented as a Drop-In Item Renderer, the Image component takes any data property regardless of name and uses it as the Image component's source property value. Assuming our employee photo property contains a valid image path, the Image Drop-In Item Renderer will work perfectly and resolve the image path as an image in the UI.

<!-- Drop-in Item Renderer: Image control -->
<mx:DataGridColumn dataField="photo" 
                   headerText="Employee Photo" 
                   itemRenderer="mx.controls.Image"/>
Drop-In Item Renderers are simple and easy to use and satisfy specific use cases nicely. However, they provide no flexibility whatsoever. If your needs are not satisfied by a Drop-In Item Renderer, you must create your own Item Renderer as an inline component or an external component.

Inline Item Renderers

Generally used for simple item rendering requiring minimal customization, inline Item Renderers are defined as a component nested within the MXML of your list-based control.

It is important to note that Item Renderers nested within the itemrender property of a list-based control occupy a different scope than the list-based control. Any attempt to reference members (properties or methods) of the parent component from the nested Item Renderer component will result in a compile-time error. However, references to the members of the parent component can be achieved by utilizing the outerDocument object.

<mx:DataGrid id="myGrid" dataProvider="{gridData}">
   <mx:columns>
      <mx:DataGridColumn headerText="Show Relevance">
         <mx:itemRenderer>
            <mx:Component>
               <mx:Image source="{'assets/images/indicator_' + data.showRelevance + '.png'}" 
                         toolTip="{(data.showRelevance == 1) ? 'On' : 'Off'}"
                         click="outerDocument.toggle()" />
            </mx:Component>
         </mx:itemRenderer>
      </mx:DataGridColumn>
   </mx:columns>
</mx:DataGrid>
Remember, rules of encapsulation still apply. Mark all properties or methods public if you want them accessible by your inline Item Renderer. In the previous example, the toggle() method must have a public access modifier to expose itself to the inline Item Renderer.

public function toggle():void
Inline Item Renderers can also be reusable by creating a named component instance outside of the list-based control. This component must have an id property and contain the rendering logic of the Item Renderer. Using data bindings, the component is assigned to the itemrenderer property of one or more data properties of a list-based control.

<!-- Reusable inline Item Renderer -->
<mx:Component id="ImageRenderer">
   <mx:VBox width="100%" height="140"
            horizontalAlign="center" verticalAlign="middle">
      <mx:Image source="{'assets/'+data.image}"/>
      <mx:Label text="{data.image}" />
   </mx:VBox>
</mx:Component>

<!-- Used within a list-based control-->
<mx:DataGridColumn headerText="Image"
                   dataField="image" width="150"
                   itemRenderer="{ImageRenderer}"/>
In the previous example, note that the Item Renderer component contains 2 UI controls – Image and Label. When using multiple controls within an Item Renderer, a layout container is required. In this example, a VBox was used.

External Item Renderers

As the complexity of your Item Renderer increases, you may want to create a custom MXML component or ActionScript class to encapsulate the Item Renderer.

Once built, the Item Renderer component is assigned to the itemrenderer property of the appropriate list-based control property. Note that the path to the Item Renderer component is not relative to the current file but rather is relevant to the MXML file containing your Application tags.

<!-- Custom Component: ImageRender.mxml -->
<mx:VBox width="100%" height="140"
         horizontalAlign="center" verticalAlign="middle">
   <mx:Image source="{'assets/'+data.image}"/>
   <mx:Label text="{data.image}" />
</mx:VBox>

<!-- Used within a list-based control-->
<mx:DataGridColumn headerText="Image"
                   dataField="image" width="150"
                   itemRenderer="components.ImageRenderer"/>
There is very little difference between an external Item Renderer and an inline Item Renderer. However, external Item Renderers are ideal for modularity, reuse and maintainability.

To continue the discussion of Item Renderers and take a much closer look, read Peter Ent's 5 part article entitled Understanding Flex itemRenderers or see an example of an Item Renderer in practice.