Tuesday, February 9, 2010

Flex ItemRenderers ought to be home-schooled

ItemRenderers ought to be considered private children of their parent control and get their data from their parent only.  Of course sometimes itemRenderers need data beyond what's supplied to them via IDataRenderer.  Imagine that we've a List-based control and want our itemRenderer children to know when some bit of data has changed perhaps isTheWorldEnding:Boolean.  The itemRenderers can then prepare from the upcoming conflagration by changing their states accordingly.  I've seen lots of bad solutions to this problem, two common ones are:
  • Injecting a Presentation-Model into the itemRenderers (bad)
  • referencing an awful Singleton with the desired data (real bad)
Here's a better way. Sub-class the List control (make a MyList control) and add a publicly accessible property of isTheWorldEnding.  In MyList when isTheWorldEnding is set to a new value, call invalidateList() to refresh the renderers. Meanwhile, implement IDropInListItemRenderer in the renderers and get isTheWorldEnding by casting _listData.owner as MyList.  Here's the code to be put in MyList:


and the code to be placed in our renderer:
Edit: Joel pointed out that it would be nice to have a convenient method to cast the list, here it is:

4 comments:

Matt said...

Nice! Might be good to recap why injecting a PM is bad though. It's one of those things everyone kinda knows, but may not entirely know why or what perils exist...

Max said...

Well the Principle of Least Knowledge (only talk to your immediate friends) is one thing violated by the presentation-model injection approach. Specifically the reason this violation is bad is that our itemRenderers will have a dependency coupling them to a class a few layers removed in the application. A change to the presentation-model can now break our renderers, and that's hardly obvious when you're editing the presentation-model. Also, when a bug is filed against our renderer, we'll have to check the parent List control *and* the presentation-model sifting through those two dependecies to find the data and/or state. I guess I could have summarized this as "it is less maintainable" but what fun would that be? :)

Tim Milgram said...

in order for this to work, don't forget to add the following to the renderer:

1:
implements="mx.controls.listClasses.IDropInListItemRenderer" to the renderer
2:
public function get listData():BaseListData
{return _listData;}

These might seem trivial but easy to forget if you're just copying and pasting this example! Hope it helps.

Max said...

Was "Meanwhile, implement IDropInListItemRenderer in the renderers..." not clear enough? :)