Download - Rendering The Fat
Rendering the Fat:How to Keep Your DataGrid Healthy
Ben Schmidtke IIIDigital Primates
[email protected]: stunnedgrowth
Who Am I
• Ben Schmidtke III• Consultant - Digital Primates IT Consulting
Group• Flash Platform Developer for 11 years• Adobe Certified Instructor• Twitter: stunnedgrowth
Agenda• Overview of List Components • DataGrid and AdvancedDataGrid• Why should we take care of them• ItemRenderers• Building Slim ItemRenderers• Tips and Guidelines• Questions
Get To It• Focus will be looking at best practices
for dealing with custom cell formatting
• Data customization in DataGrid and AdvancedDataGrid
• Many of the concepts can be used in the other List components
The Interwebs
• The Internet is full of examples about how to use Flex components.
• Some are great• Some are ok• Others, let’s pretend they don’t exist.
Out Of The Box
• List Controls – List – Tree– DataGrid– TileList– Menu– And More…
• Data Visualization– AdvancedDataGrid– OLAPDataGrid
Flex 3 provides several components for presenting data in a list format:
General Features• Support Multiple Data Types• Drag and Drop• Scrolling - Vertical and Horizontal• Cell Editing• Variable Row Height• labelFunction• And more...
DataGrid: What Is It?• Presents data in a Table format– Rows– Columns
• Can automatically create rows & columns based on the data
• Single Column Sorting• Column Reordering
AdvancedDataGrid
• Supports all the functionality of DataGrid• Provides support for Hierarchical Data
– Understands how to display parent/child relationships.
– XML, XMLList, HierarchicalData, HierarchicalCollectionView
– Custom objects with “parent” and “children” properties
• Advanced Sorting (Multiple Columns)• Column Grouping
In Short They…
• Are a view for the data model• Can render data multiple ways– Flat, Hierarchical Data, Grouped
• Are a control that render other controls– Headers– Renderers– Editors– ScrollBars, Lines & Other UI Stuff
Why should we take care of it?
– Performance– Troubleshooting– Flexibility–Maintainability
It Takes Work• Large quantities of information• Each cell is a component• The more rows and columns
displayed decreases performance
What Could Go Wrong?• Cells appear slow to display• Content placed incorrectly
“sometimes work, sometimes not work”• Content shift a few pixels for no reason• Scrolling is jumpy or lag• Sorting & Column reordering take a long
time to process
• In worst case, it crashes.
It All Boils Down To• Unexpected Results• Poor User Experience• Cranky Client • Your Personal Health: Stress
The Fat : ItemRenderers
What are they?– Purpose:• Read data• Present the data (Text, Icons, Controls, etc.)
• What are they not?– Things that compute data to display–mx.containers.* (HBox, Canvas, etc.)
ItemRenderer Lifecycle
• In general, list components will only create as many ItemRenderers as needed to display
– 1 Column X 10 Visible Rows = 10 Renderers– 100 data objects = 10 Renderers– Plus one or two extra for performance reasons
• ItemRenderers are Recycled
The Default: It’s Text!• DataGrid– DataGridItemRenderer• Extends UITextField > FlexTextField >
TextField
• AdvancedDataGrid– AdvancedDataGridItemRenderer• Extends UITextField > FlexTextField >
TextFIeld
Why Is It Text?• Extremely lightweight• Direct and to the point• Take the least amount of time to
create and display• No container overhead
But Containers Are Easy…
• Containers provide a lot of useful functionality:
– Automatic Layout: Horizontal & Vertical– Absolute Layout: Coordinates X and Y– Scrollbars– Dividers, Tabs, etc.
Too Much Functionality
• Containers are performance heavy
• Everything that makes them easy, slows them down
• 10 Rows + 10 Columns = 100 Containers
Custom ItemRenderersDepending on the project
requirements, it most likely require a custom ItemRenderer
– Different text colors based on the data– Support for icons– Support for other Controls• CheckBox, ComboBox, etc.
Do you need one?
• If you do not need one, do not create one
Custom String Manipulation– Use a label function if possible– Can be done with out creating a
renderer
Typical Label Function
public function myLabelFunction(item:Object, column:DataGridColumn):String
{return item.firstName + " " + item.lastName;
}
<mx:DataGrid dataProvider="{myDataProvider}"><mx:columns>
<mx:DataGridColumn labelFunction=“myLabelFunction"/></mx:columns>
</mx:DataGrid>
That doesn’t cut it
A single question is the determining factor for the approach of a ItemRenderer.
Q: Does the content require something other than
text or a label function can provide?A) No, formatting, custom styling, truncated textB) Yes, I need some icons or checkbox
Just some formatting…
• Extend the Base ItemRenderer Class for the component– AdvancedDataGridItemRenderer– DataGridItemRenderer– Etc.
• They extend UITextField• Override validateProperties();
ValidateProperties()• The part of DataGridItemRender that:– Sets the Text property on the TextField– Sets the Tooltip– Applies WordWrap based on Column
settings
• Apply any necessary formatting based on the data here
Unhealthy Text Renderer
<mx:AdvancedDataGridColumn headerText=“Add User"><mx:itemRenderer>
<mx:Component><mx:HBox width="100%" height="100%">
<mx:Label id=“userLabel” text="{data.name}“ styleName="{((data.name ==
'Jack') ? 0x990000 : 0x000000)}”/><mx:Image source=“{data.imageURL}” />
</mx:HBox></mx:Component>
</mx:itemRenderer></mx:AdvancedDataGridColumn>
What is wrong with it?• Improper use of a Container• Unknown length of the user name• ScrollBars• Inline ItemRenders are unable to be
reused elsewhere in the application
Healthy Text Rendererpublic class ExpiredUserItemRenderer extends AdvancedDataGridItemRenderer{
override public function validateProperties():void{
super.validateProperties();if (data && listData){
text = listData.label;if((data is Person) && (data as Person).isExpired == true){
setStyle("color", "0x990000");toolTip = “Warning! “ + listData.label;
}else {
setStyle("color", "0x000000");toolTip = listData.label;
}}
}}
Why Is It Healthy?• It is still just a TextField• Does the least amount of work to
present the custom text• No additional overhead from
Container classes or UIComponent
Rendering Other Controls
• When rendering images or multiple controls:– Extend UIComponent– Implement Interfaces• IDataRenderer• IListItemRenderer• IDropInListItemRenderer (Optional)
– Build Custom Functionality
Interfaces Part 1• IDataRenderer– data (value:Object)
• Provides the data object for the entire row
Interfaces Part 2• IListItemRenderer– All renderers must implement this
interface– Does not define any new methods– Extends several other Flex Interfaces– UIComponent already has required
properties and methods
Interfaces Part 3• IDropInListItemRenderer (Optional)– listData (value:BaseListData)
• Provides additional information to the renderer: Column Index, Row Index, dataField
• Allows the renderer to be re-used with different data fields and List components
Custom Functionality• Since we are extending
UIComponent, we have the Flex Component Life Cycle
• createChildren()• commitProperties()• measure()• updateDipslayList(…)
Unhealthy Renderer<?xml version="1.0" encoding="utf-8"?><mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
protected function linkbutton_clickHandler(event:MouseEvent):void{
// navigate to data.url}
]]></mx:Script>
<mx:HBox><mx:Label id="city" text="City: {data.city}" /><mx:Label id="state" text="State: {data.state}“ />
</mx:HBox><mx:LinkButton label="More Information“ click="linkbutton_clickHandler(event)"/>
</mx:VBox>
Another Unhealthy Renderer
override protected function commitProperties():void {if(dataChanged) {
// start overremoveChild(pdfIcon);removeChild(labelField);
}}
override protected function updateDisplayList( … ):void {super.updateDisplayList(unscaledWidth, unscaledHeight);
if(data && !pdfIcon) {createPDFIcon();}
if(data && !labelField) {createLabelField();
}// position & sizing logic…
What is wrong with it?• Improper use of the display list
• Using removeChild() & addChild() every time the data changes when unnecessary
• If you know what you need, create it once and update it when the data changes
Healthy Versionoverride protected function commitProperties():void {
if(dataChanged) {// update the text on the labelif(data) {
labelField.text = data.labelField;labelField.visible = true;
} else {labelField.text = “”;labelField.visible = false; }
}}
Healthy Component Renderer
Example of a Text & Icon Renderer
Tips for Healthy ItemRenderers
1. Avoid extending or containing anything in the mx.containers.* package.
2. Using a Repeater3. Using Remote Objects! Don’t Do it!4. Inappropriate use of the display list.
• addChild(), removeChild(), etc.5. Excessive recursion in the UIComponent
lifecycle.6. Directly modifying the data model,
remember it’s a view
Guidelines
When displaying:• Text: Extend base ItemRenderer
component• Images & Controls: Extend UIComponent• Add items to the display list once.• Avoid removing items from the display list
unless necessary
Summary• Know the issues related to a poorly
performing DataGrid• Ask the right question before
creating a custom ItemRenderer• Base ItemRenderer classes,
UITextField and UIComponent are your friends
• Don’t be afraid to get in there
Questions?
Bonus Slides
Performance Note
A majority of all performance issues with AdvancedDataGrid are related to improper use of a custom item renderer.
Even if when updating the dataProvider collection more than necessary, a single poorly made item renderer can decrease performance severely.
The DataProvider• Will accept pretty much anything override public function set dataProvider
(value:Object):void
Behind the scenes if it does not meet certain criteria, it becomes something else.
The DataProviderAll data is or becomes one of two types:
• ICollectionView– ArrayCollection– XMLListCollection
• IHierarchicalCollectionView– HierarchicalCollectionView
Behind The Scenes if (value is Array) { collection = new ArrayCollection(value as Array); } else if (value is XMLList) {
collection = new XMLListCollection(value as XMLList); }
...
else { // convert it to an array containing this one item var tmp:Array = []; if (value != null) tmp.push(value);
collection = new ArrayCollection(tmp); }
CollectionEventcollection.addEventListener(
CollectionEvent.COLLECTION_CHANGE, …);
AdvancedDataGrid watches for CollectionEvent to respond accordingly keeping things up to date.
HierarchicalCollectionView Caches parent/child relationships for quick lookups and respond to the parent & children properties changing.
Issue 1: CollectionEvent
• Every time a collection event is dispatched on the dataProvider, AdvancedDataGrid may perform a considerable amount of work including:
– Rebuilding the data view– Reconstructing Hierarchical Structures &
Updating Cached Collections if necessary– Setting the data on all visible renderers
DataProvider Guidelines
• Manage your data prior setting the dataProvider– ArrayCollection, XMLListCollection or
HierarchicalCollection
• Apply updates to data objects in bulk if possible– Example: When editing a property, set the
value once not one character at a time.