Detecting Item Clicks on a Spark List

On August 18, 2010, in Flex, by Anuj Gakhar

As I am using the Spark List component extensively in my current project, I have come to understand some of the differences between a Spark List and MX List. I thought it would make sense to post my experience with this.

With MX components, you have an itemClick Event on your List component. So you can capture clicks on Individual items. Something like this :-

    <fx:Script>
        <![CDATA[
            import mx.events.ListEvent;
            protected function myList_itemClickHandler(event:ListEvent):void
            {
                trace(event.currentTarget.selectedItem.label);
            }

        ]]>
    </fx:Script>

    <mx:List width="200"
        id="myList"
        itemClick="myList_itemClickHandler(event)"
        dataProvider="{ds}" />

So, this is cool. However, in Spark, there is no such event. In a Spark List, you have to listen to the change event which fires off an IndexChangeEvent every time the List Index changes – which makes sense. So, your code will look like this in this case:-

<fx:Script>
        <![CDATA[
            import spark.events.IndexChangeEvent
            protected function sparkList_changeHandler(event:IndexChangeEvent):void
            {
                trace("Spark List:: - " + event.currentTarget.selectedItem.label);
            }
        ]]>
</fx:Script>
<s:List change="sparkList_changeHandler(event)"
      id="sparkList"
      width="200"
      x="500"
      dataProvider="{ds}" />

This works pretty much the same as the MX itemClick handler but there is one problem. The IndexChangeEvent won’t fire if you click on an Item that is already selected in the List. So, if you select e.g. the third item in the List – the IndexChange event would fire the first time but it wont fire off the next time you click on it because the Index hasn’t changed. Which makes sense. But this can be a requirement in some cases.

So, there are 2 things you can do here

1) Add Event Listeners for the Mouse Click on the rendererAdd and rendererRemove events of the Spark List. A Spark List uses spark.skins.spark.DefaultItemRenderer by default – which has a Label called “labelDisplay”. So, we can use that information to capture the click events like this :-

    <fx:Script>
        <![CDATA[
            import mx.core.IVisualElement;
            import spark.events.RendererExistenceEvent;

            protected function sparkList_rendererAddHandler(event:RendererExistenceEvent):void
            {
                var item:IVisualElement = event.renderer;
                item.addEventListener(MouseEvent.CLICK, sparkList_clickHandler);
            }

            private function sparkList_clickHandler(event:MouseEvent):void
            {
                trace("Spark List:: - " + event.currentTarget.labelDisplay.text);
            }

            protected function sparkList_rendererRemoveHandler(event:RendererExistenceEvent):void
            {
                var item:IVisualElement = event.renderer;
                item.removeEventListener(MouseEvent.CLICK,sparkList_clickHandler);
            }
        ]]>
    </fx:Script>

    <s:List
        rendererAdd="sparkList_rendererAddHandler(event)"
        rendererRemove="sparkList_rendererRemoveHandler(event)"
        id="sparkList"
        width="200"
        x="500"
        dataProvider="{ds}" />

In the above example – we are capturing the rendererAdd and rendererRemove events of the Spark List. And in those events, we attach/remove Click EventHandlers for the entire ItemRenderer. And now, whether or not an item is selected or not, the click events will always work. Unlike the change event where it would only work if the Item index changes.

2) The other way, we can achieve the same functionality is by creating a Custom ItemRenderer and dispatching an ItemClick event from within the ItemRenderer. Here is the code to do so :-

<fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
            import mx.events.ItemClickEvent;

            protected function sparkList_initializeHandler(event:FlexEvent):void
            {
                sparkList.addEventListener(ItemClickEvent.ITEM_CLICK, sparkList_itemClickHandler);
            }

            private function sparkList_itemClickHandler(event:ItemClickEvent):void
            {
                trace("Spark List:: - " + event.currentTarget.selectedItem.label);
            }

        ]]>
    </fx:Script>

    <s:List
        initialize="sparkList_initializeHandler(event)"
        itemRenderer="CustomListItemRenderer"
        id="sparkList"
        width="200"
        x="500"
        dataProvider="{ds}" />

And here is the code for the CustomListItemRenderer

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    xmlns:mx="library://ns.adobe.com/flex/mx"
    autoDrawBackground="true"
    click="itemrenderer1_clickHandler(event)">

    <fx:Script>
        <![CDATA[
            import mx.events.ItemClickEvent;
            protected function itemrenderer1_clickHandler(event:MouseEvent):void
            {
                var e:ItemClickEvent = new ItemClickEvent(ItemClickEvent.ITEM_CLICK, true);
                e.item = data;
                e.index = itemIndex;
                dispatchEvent(e);
            }
        ]]>
    </fx:Script>

    <s:Label id="labelDisplay" text="{data.label}"/>
</s:ItemRenderer>

In the above example – we are adding an Event Listener for ItemClick Event to the Spark List and our Custom ItemRenderer is dispatching that event.

So, as you can see – there are ways to detect Item Clicks but they are not as easy as they were in MX. I am not sure why the “itemClick” event was removed in Spark. Maybe someone from the Flex SDK team can enlighten us?

Apparently, there is a bug reported for this as well.

 

6 Responses to Detecting Item Clicks on a Spark List

  1. Nick says:

    Cool examples to understand the differences. I have one thing to mention though for the second example where you dispatch the event from inside the custom renderer. Wouldn’t it be better and perhaps safer to dispatch the event this way?

    this.parent.dispatchEvent(e)
    This way you avoid issues with event bubbling. I’ve seen this mentioned by a lot of people so I’ve taken this approach in my current project.

  2. Anuj Gakhar says:

    Nick, we could do that. However, I like things as decoupled as possible and therefore, avoid things like this.parent and this.outerDocument. But – to answer your question, we can probably do it that way as well.

  3. Nick says:

    I know what you mean, referencing the parent is not the best approach to that.

  4. dinko says:

    Hello Anuj,
    Thanks for the hint. It saved me time. Item click is convenient event, I will vote for the bug.

    Best,
    Dinko

  5. Raghu says:

    Great post. I wanted to know how to dispatch a custom event instead of ItemClickEvent. For example:

    event is called List_drilldown
    itemrender is called CustomListItemRenderer
    in the application I want to write the following code:

  6. Raghu says:

    Sorry got cut off :
    event is called List_drilldown
    itemrender is called CustomListItemRenderer
    in the application I want to write the following code:

    list List_drilldown=sparkList_drilldown(event)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 445 other subscribers

© 2011 Anuj Gakhar
%d bloggers like this: