Question

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

Était-ce utile?

La 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.

Autres conseils

I don't think the problem is that the event is not firing. Instead, I think what is happening is that you are listening to the original ArrayCollection that you set up in the variable declaration, rather than to the one passed in by the binding in the Application.

Use a getter/setter pair:

protectect var _booths:ArrayCollection;

[Bindable]
public function get booths():ArrayCollection {
     return _booths;
}

public function set booths(value:ArrayCollection):void {
    if (value != _booths) {
        if (_booths != null) {
            _booths.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothChange);
        }
        _booths=value;
        if (_booths != null) {
            _booths.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleBoothChange);
        }
        handleBoothChange(null);
    }
}

Note that you probably don't need to make this bindable if you're handling the collection change manually.

If you're using FB 4.5, you may want to consider setting this up as a template--I use this type of logic enough that I have my own template for it. The make getter/setter pair is ok, but it doesn't have the check for removing the listener off the old instance and adding the new onw.

The problem is you've subscribed on collectionChange event of this collection:

public var booths:ArrayCollection = new ArrayCollection();

But then:

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

you've passed other collection which event isn't subscribed.

You may change the code to be the following:

<?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;

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

        ]]>
    </fx:Script>

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

Or create getter with subscribe/unsubscribe logic on a FloorplanGrid's side.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top