Sunday, May 18, 2008    
Home My Books Blog ColdFusion About Me Back    

Calendar
<< May 2008 >>
S M T W T F S
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
             

Search

Categories
 • Adobe (61) [RSS]
 • AdobeMAX06 (45) [RSS]
 • AdobeMAX07 (59) [RSS]
 • AdobeMAX08 (6) [RSS]
 • AIR (96) [RSS]
 • Appearances (105) [RSS]
 • Books (66) [RSS]
 • CFEclipse (14) [RSS]
 • ColdFusion (1081) [RSS]
 • Flash (91) [RSS]
 • Flex (319) [RSS]
 • Jobs (81) [RSS]
 • JRun (12) [RSS]
 • Labs (27) [RSS]
 • LiveCycle (12) [RSS]
 • MAX (141) [RSS]
 • Regular Expressions (12) [RSS]
 • SQL (36) [RSS]
 • Stuff (492) [RSS]
 • Tips (CF Studio) (80) [RSS]
 • Tips (CF) (795) [RSS]
 • Tips (Dreamweaver) (91) [RSS]
 • Tips (Flex Builder) (2) [RSS]
 • Using CF (131) [RSS]
 • Wireless (96) [RSS]

Other BLOGs
 • Ray Camden
 • Tim Buntel
 • Sean Corfield
 • John Dowdell
 • Steven Erat
 • Brandon Purcell
 • Charlie Arehart
 • Full As A Goog

RSS Feeds
 • Feed
 • Subscribe

Join my mailing list and find out about new books and other topics of interest.

Thoughts, ideas, tips, musings, and pontifications (not necessarily in that order) by Ben Forta ...
NOTE: This is my personal blog, and the opinions and statements voiced here are my own.

Viewing By Entry / Main
May 8, 2008

Flex Renderers Can't Rely On creationComplete

Yesterday I wasted an hour or so debugging a Flex itemRenderer that I was using to display an image instead of a value in a DataGrid column. The renderer had to simply pick one of six images based on the column value, and so it contained a single <mx:Image> tag and a function that set the Image source dynamically. And then I called that function on the renderer's creationComplete event.

Simple enough. Except that the wrong images were sometimes being displayed, the column had the right data, but the code used to select the image seemed to sometimes pick the wrong image. And what it picked seem to change each time I scrolled the DataGrid up and down!

I actually ran into a very similar issue with a TileList renderer a few weeks ago, but then I had no time to figure out the cause, and so I hacked a workaround. But this time, having been bitten by the same issue twice, I had to find out what was going on.

And what I discovered (by using traces and alerts) is that the creationComplete event does not get fired as I had expected. Rather, it seemed to fire only occasionally, and not once per DataGrid row, and so my image selection function was not being executed as expected.

Once I had figured out the problem I searched the docs for any info on renderers and creationComplete, and found this page. And sure enough, "Flex might reuse an instance of the item renderer or item editor, a reused instance of an item renderer or item editor does not redispatch the creationComplete event". Well, that explained it.

The right way to do what I wanted is to trap the dataChange event instead of creationComplete, as "Flex dispatches the dataChange event every time the data property changes".

And so I am posting this for my own future reference, just to make sure I don't run into it a third time.

TrackBacks
There are no trackbacks for this entry.

No trackback URL. Trackbacks are only allowed via interactive form.

Comments
You nailed a major problem I had with a project recently! Since we were only displaying 4 items at a time, controlling scrolling with custom arrow components, I ended up making a new model with the next 4 items every time the user wanted to move back or forth. Like you, I didn't have the time to figure out what was wrong. Thanks for posting this!
# Posted By Jack | 5/8/08 9:59 AM
Hey Ben,

I went through the same thing when I first picked up Flex 2...once I thought about it, the idea of recycling item renderers made total sense from a performance standpoint.

Instead of catching dataChange and going through event mechanics, I've often found it easier to deal with this issue by updating the setter for the data property that's on most visual components (I think it's the implementation of IListDataRenderer?):

override public function set data(value:Object):void
{
super.data = value;

// do my stuff
switch (value)
{
case "foo":
myImage.source = "foo.png";
break;
}
}

I'll often go further, adding a bindable private property that is of the type of data being set in, allowing components in the renderer to bind cleanly to it.
# Posted By Joe Rinehart | 5/8/08 10:04 AM
Joe, yep, same here, and that's exactly what I am doing now. And you actually are indeed catching the dataChange event by overriding set data. One comment though, just to be safe, when overriding set data you should dispatch the DATA_CHANGE event when done, so at the end of your function add:

dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));

--- Ben
# Posted By Ben Forta | 5/8/08 10:11 AM
Ben, if you are doing super.data = value you shouldn't be required to dispatch the change event since the super class would to that for you.
# Posted By João Fernandes | 5/8/08 10:36 AM
João, cool, I'll look into that one. Although I wonder why the example on the docs page I mentioned about does both ...

--- Ben
# Posted By Ben Forta | 5/8/08 10:49 AM
In such cases, I believe "binding" would do the job...

Every itemRenderer (inline or custom component) has a "data" property linked to the its parent's dataProvider. So, you can "bind" it to dynamically transfer the corrent row info to the itemRenderer on Flex's display framework.

Samples (with sources):
- inline:
http://www.vpmjr.com.br/downloads/benforta/itemRen...

- external component:
http://www.vpmjr.com.br/downloads/benforta/itemRen...
# Posted By Vicente Junior | 5/8/08 11:04 AM
This killed me when I first got into Flex. I spend two weeks trying to figure out the exact same problem. Luckily for me, Alex Harui was very helpful in explaining the idea of itemRenderer recycling to me. I'm glad to see I wasn't the only one that ran into this problem.
# Posted By Bryan Bartow | 5/8/08 11:56 AM
I am trying to use an external itemrenderer to edit a row of data in a datagrid. The itemrenderer displays a button and when the button is pressed a popup appears with the data. The user updates the data and presses "Save" button in the popup, which triggers the data to be saved in the database and closes the popup.

It is at this point I am stuck. The datagrid needs to know the popup updated the data. I am not sure how to communicate this. Because of the layers of components I am having trouble communicating back to the datagrid. I tried adding dataChange to the datagrid, but it did not recognize the updated data.

I can email you the test code as it uses AdventureWorks database if you'd like to see it.

Thanks,
Mary
# Posted By Mary McDonald | 5/8/08 12:47 PM
@Ben and Joao - If you're extending a container like HBox or VBox, Joao is correct - you don't need to dispatch FlexEvent.DATA_CHANGE. The implementation of set data() in Container itself does this for you. If you're extending UIComponent (or even DisplayObject), you _do_ need to dispatch it yourself (in fact, there's no super.data at all in this case).

@Vicente - Binding does do the job, but by binding to just "data" you lose compile-time checking. Great for fast and loose work, or where you don't know what type of data you'll be getting, but a common "best practice" is to set to a known type within the renderer.

@Mary - It sounds like the "save" functionality in the popup needs to update the data (AS3 objects, XML, etc.) to which your datagrid is bound in addition to sending it to the server.
# Posted By Joe Rinehart | 5/8/08 12:59 PM
Joe,
Could I possible send you my code to take a look at?
# Posted By Mary McDonald | 5/8/08 1:08 PM
Mary,

When the item renderer is also a value editor, you'll need to set "rendererIsEditor='true'" and set what field on the component modifies the "dataField" property using column's "editorDataField" property.

So, in this case, you can make use of custom events too to make the opened window dispatch an event that holds in its properties the new value, so you can listen to it inside your component and set the new value to the property at the component you set up to be the "editorDataField".

Without looking to what you're doing, it's that I can tell you.
# Posted By Vicente Junior | 5/8/08 1:11 PM
Joe, interesting... thank you...
I didn't practice it yet (I'll do some labs...) but I believe using getter/setter in the component to interface the access to the "data" property, would workaround it. So I just change the bind inside the component to it.
# Posted By Vicente Junior | 5/8/08 1:31 PM
getter/setter solutions falls on the need to use components "dataChange" event (or another implementation like that), so don't worths the effort...
# Posted By Vicente Junior | 5/8/08 2:15 PM
@Vicente - there's no need to use the dataChange event. Here's what I do, and what I've seen others do:

<mx:Script><![CDATA[
import model.Contact;

[Bindable]
private var contact:Contact;

override public function set data(value:Object):void
{
super.data = value;

this.contact = value as Contact;
}

]]></mx:Script>

<!-- components may now bind to this.contact and use compile-time checking, and you get code hinting -->
<mx:Label text="{this.contact.name}" />
# Posted By Joe Rinehart | 5/8/08 4:39 PM
Generally we would take care of this by overriding commitProperties().

When the data prop or listData prop is changed on an itemRenderer it should invalidate its properties by invoking invalidateProperties(), which in turn will invoke commitProperties where you would commit the properties that had changed (i.e. assign the data to you Image).

Firstly we'd check to see if the data or listData were different (i.e. the value hadn't just been set again) before invalidating the properties.

ItemRenderers are re-used in Flex to keep the number of instances created down (i.e. you create enough to display on the screen and re-use them).
# Posted By Tink | 5/8/08 6:38 PM
If you're doing anything at all with Flex and item renderers, you literally need to stop what you're doing and head over to Alex Harui's blog and reading his series on working with item renderers. I'm not kidding -- do not pass go, do not collect $200.

http://blogs.adobe.com/aharui/2007/03/thinking_abo...

It's a phenomenal set of entries and his tips are still 100% relevant even with Flex 3 out the door.
# Posted By Dave Carabetta | 5/8/08 8:03 PM
This is a very common gotcha I see. It can have some very "interesting" effects :)
# Posted By David Stockton | 5/9/08 8:20 AM
hey

Really nice tips!

will try to keep it in my mind

thanks!
# Posted By flex developers | 5/9/08 8:47 AM
Ben, why couldn't you have posted this Monday! I spent about 4 hours Tuesday on the same problem.

Oh well, not anymore!

Kevin
# Posted By Kevin Schmidt | 5/9/08 10:38 AM
Why wouldnt you override the updateDisplaylist function instead?
# Posted By Rick | 5/12/08 2:18 PM

  © Copyright 1997-2008 Ben Forta, All Rights Reserved