Search code examples
apache-flexactionscriptbinding2-way-object-databinding

Flex 4 COLLECTION_CHANGE event not firing


I'm currently trying to achieve 2 way binding of an ArrayCollection object. However, the COLLECTION_CHANGE event is not firing.

App.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" 
               xmlns:components="components.*"
               creationComplete="handleCreationComplete(event)">
    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;

            [Bindable]
            public var booths:ArrayCollection;              

            public function handleCreationComplete(event:Event):void
            {
                // ADD SOME BOOTHS
                booths = new ArrayCollection();
                booths.addItem( "item1" );
                booths.addItem( "item2" );
            }

        ]]>
    </fx:Script>

    <components:FloorplanGrid id="grid" width="400" height="300" booths="{booths}" />
</s:Application>

FloorplanGrid.as

package components
{       
    import mx.collections.ArrayCollection;
    import mx.events.CollectionEvent;

    import spark.components.Group;

    [Event(name="collectionChanged", type="events.CollectionEvent")]

    public class FloorplanGrid extends Group
    {
        [Bindable]
        public var booths:ArrayCollection = new ArrayCollection();

        public function FloorplanGrid()
        {
            booths.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothsChange);
            super();
        }

        private function handleBoothsChange(event:CollectionEvent):void
        {
            trace("HANDLE BOOTHS CHANGE");
            /* draw booths */
        }
    }
}

I'm trying to achieve 2-way binding with the Booths variable. However, the COLLECTION_CHANGE event never fires even when I add 2 new items in the Booths variable in the App.mxml


Solution

  • I'm not sure where to start...

    Binding works on the Flex event system; so when a binding is upated, it is not instant/synchronous. Look at these code segments:

            public function handleCreationComplete(event:Event):void
            {
                booths = new ArrayCollection();
                booths.addItem( "item1" );
                booths.addItem( "item2" );
            }
    
    <components:FloorplanGrid id="grid" width="400" height="300" booths="{booths}" />
    

    So, you create the booths ArrayCollection and add two items to it. This is synchronous code, so those three items happen right after each other. The booths ArrayCollection is bound to the booths property on the FloorplanGrid. This update will happen asynchronously; so there will be a delay.

    So, it is likely that the items are added to the ArrayCollection before the booths property on the FloorplanGrid contains the new value. As such the collection change event is never fired from within the FloorplanGrid.

    Additionally, you are setting the event listener for the collect change event inside the constructor of FloorplanGrid:

        public function FloorplanGrid()
        {
            booths.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothsChange);
            super();
        }
    

    As such you are listening for the event on the 'new ArrayCollection()' object created inside the component. When you change the booths property you have not added an event listener to the new value; as such you have no event listener in play.

    You probably want to change your booths property to a get/set property. Like this:

            private var _booths:ArrayCollection;  
            [Bindable]
            public function get booths():ArrayCollection{
                return _booths;
            }
            public function set booths(value:ArrayCollection):void{
               if(_booths){
               _booths.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothsChange);
               }
               _booths = value;
               if(_booths){
                _booths.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothsChange);
               }
            }
    

    Removing the event listener and re-adding it will help prevent memory leaks in your application. Because you won't have an erroneous listener in your app preventing the old value from being garbage collected.