15 Replies Latest reply on Nov 24, 2009 10:40 AM by olegkon

    From ArrayCollection to Array in DataBinding

    olegkon

      Hi,

       

      I am trying to use somebody else's visual component (have src code of that mxml),

      which uses Array items as a DataSource.

       

      In my program I use ArrayCollection to hold my data (from the data feed - changes in real time).

      I need to pass some data from my ArrayCollection into that component inside the binding,

      something like:

      <view:Component1  items="{dataArrayCollection}"?? />

       

      currently have in Component1.mxml:

      public function set items(a:Array):void {...}

       

      Since that component is pretty complicated, I would not want to break its logic,

      so I am thinking of adding some data adapter, public method to Component1 like:

       

      public function set dataProvider(ac:ArrayCollection):void {

      Array tempArr = new Array();

      // copy some data from ac -> tempArr

      items(tempArr);

      }             

      and do: <view:Component1  dataProvider="{dataArrayCollection}" />

       

      Is there a more efficient way of doing that (I guess, "set dataProvider" will be called a lot, on each update)?

      I don't think I can just straight convert ArrayCollection -> Array, need just a small subset of data.

       

      Please advise.

       

      TIA,

      Oleg.

        • 1. Re: From ArrayCollection to Array in DataBinding
          olegkon Level 1

          I tried that approach and see that "set dataProvider" is called only once,

          so it didn't work well for me.

           

          So what should I do, try to use a function with that property passed, like:

          <view:Component1  dataProvider="{update(dataArrayCollection)}" />  ?

           

          Or try to call that (ArrayCollection->Array adapter) function

          from event handler function like onChange()  ?

           

           

          Please help !

           

           

          TIA,

          Oleg.

          • 2. Re: From ArrayCollection to Array in DataBinding
            Flex harUI Adobe Employee

            I'm not quite sure what you're asking.

             

            You could subclass AC and add collectionChange to its list of bindable events.

             

            Alex Harui

            Flex SDK Developer

            Adobe Systems Inc.

            Blog: http://blogs.adobe.com/aharui

            • 3. Re: From ArrayCollection to Array in DataBinding
              olegkon Level 1

              Alex,

               

              To summarize:

              1) how to pass the data from AC to Array on each AC update (e.g. need custom event?  {update(AC)}? ...) ?

              2) idea how to do that effectively, since it's on each update ?

              3) maybe there is a better solution altogether, some advanced Flex feature or trick ?

               

              Thank you,

              Oleg.

              • 4. Re: From ArrayCollection to Array in DataBinding
                Flex harUI Adobe Employee

                AC.source is the modified array.

                 

                Alex Harui

                Flex SDK Developer

                Adobe Systems Inc.

                Blog: http://blogs.adobe.com/aharui

                • 5. Re: From ArrayCollection to Array in DataBinding
                  olegkon Level 1

                  Yes, but I need to copy some data from AC->Array [and their data is different] on each AC update.

                   

                  The good news is that I discovered BindingUtils.bindSetter function.

                   

                  So my code now looks like:

                   

                  main.xml:

                  ...

                  [Bindable]

                  private var myArrayCollection : ArrayCollection ...

                  ...

                  <view:Component1 dataProvider="{myArrayCollection}" />

                   

                   

                  Component1.mxml:

                  ...

                  private var _dataProvider:ArrayCollection;

                  private var _items:Array;

                   

                  private function onCreationComplete():void {

                  ... BindingUtils.bindSetter(onDataProviderUpdate, this, "dataProvider");

                  }

                   

                  public function set dataProvider(ac:ArrayCollection):void {
                      _dataProvider = ac;
                  }
                             
                  public function get dataProvider():ArrayCollection {
                        return _dataProvider;
                  }

                             
                  public function onDataProviderUpdate(ac:ArrayCollection):void {   
                      var aca:Array = ac.toArray();

                      var tempArr:Array = new Array();

                  .....  //copy some data from ACA->tempArray

                      items = tempArr;

                  }

                   

                  public function set items(a:Array):void {

                     display data in component

                  }

                   

                  Unfortunately,   my onDataProviderUpdate() is still executed only once, not on each AC update.

                  What am I missing ?

                   

                  I also tried to pass the instance of the main class to the Component1 like

                  <view:Component1 myMain="this" /> , but compiler complained and would not let me. How can I do that ?

                   

                  Can I pass "Bindable" AC var like that from class to class in dataProvider="{myArrayCollection}"

                  and hope that bindSetter to work?

                   

                  Please help !

                  • 6. Re: From ArrayCollection to Array in DataBinding
                    Flex harUI Adobe Employee

                    BindSetter just watches for an event associated with the setter to fire which it won't if the collection changes.  One person caught collectionevents and faked a dispatch of the setter event and had success.

                     

                    It is not possible to describe references in MXML foo="this" has to be a simple type.  You can use foo="" in many cases.

                     

                    Alex Harui

                    Flex SDK Developer

                    Adobe Systems Inc.

                    Blog: http://blogs.adobe.com/aharui

                    • 7. Re: From ArrayCollection to Array in DataBinding
                      olegkon Level 1

                      Alex,

                       

                      >BindSetter just watches for an event associated with the setter to fire which it won't if the collection changes.

                      Why are you saying that changing collection will not trigger the event, just because it is a Collection ?

                      The collection change I am trying to catch is adding an item to it. I think, it should trigger it ?

                       

                      If not, what would you recommend, something like:

                      [Bindable (event="acChanged")] ArrayCollection ac=...       ?

                       

                      Please advise,

                      Oleg

                      • 8. Re: From ArrayCollection to Array in DataBinding
                        Flex harUI Adobe Employee

                        BindSetter only watches for that property to be re-assigned.  Changing a sub-property of an object or content of an array or collection doesn't re-assign, and the event that gets dispatched by the reassignment doesn't fire.  Also, Binding watches the object that owns the property, not the object referenced by the property.

                         

                        I guess one way to do this would be something like:

                         

                        private var _ac:ArrayCollection;

                         

                        [Bindable("acChanged")]

                        public function get ac():ArrayCollection

                        {

                             return _ac;

                        }

                        public function set ac(value:ArrayCollection):void

                        {

                             if (value != _ac)

                             {

                                  if (_ac)

                                       _ac.removeEventListener("collectionChange", acChangeHandler);

                         

                                  _ac = value;

                                  _ac.addEventLisener("collectionChange", acChangeHandler);

                                  dispatchEvent(new Event("acChanged"));

                             }

                        }

                        private function acChangeHandler(event:Event):void

                        {

                             dispatchEvent("acChanged");

                        }

                         

                         

                        The idea being to fake re-assignment when the collection changes.  The downside will be that anything bound to will be re-assigned every time the collection changes.  If you have a list bound to it will reset selection and scroll position.

                         

                        The smarter play would simply be to us acChangeHandler to update whatever cares about the array at ac.source.

                         

                        Alex Harui

                        Flex SDK Developer

                        Adobe Systems Inc.

                        Blog: http://blogs.adobe.com/aharui

                        • 9. Re: From ArrayCollection to Array in DataBinding
                          olegkon Level 1

                          Alex,

                           

                          Thank you for your posts.

                           

                          I actually tried to implement something similar,

                          but getting runtime error "cannot access a property or method of a null object reference"

                          from     addEventListener("acChanged", onDataProviderUpdate);

                          where      public function onDataProviderUpdate(e:Event):void { .my AC->Array code..}

                          I checked generated code, there is no   acChanged event there.  How is it supposed to work ?

                           

                          Also regarding the code you put there, a few questions:

                          1) you put Bindable on getter, why?  I think, only setter should have one, correct ?

                           

                          2) there is no listener to acChanged event in your code, why ?

                           

                          3) could you please explain why you have both  "collectionChange"  &  "acChamged" events ?

                           

                          Some comment would be useful.

                           

                           

                          TIA,

                          Oleg.

                          • 10. Re: From ArrayCollection to Array in DataBinding
                            Flex harUI Adobe Employee

                            If there is both a getter and setter, our convention is to put the getter first and give it the metadata, but it shouldn't really matter either way.

                             

                            There is no listener in the code I posted because some other code is going to listen to and that will set up the listener

                             

                            The code listens for collectionChange in order to know that the contents of the collection have changed and then fakes an acChanged so binding thinks the entire collection got re-assigned.

                             

                            I'm not sure why you're getting an error.

                             

                            Alex Harui

                            Flex SDK Developer

                            Adobe Systems Inc.

                            Blog: http://blogs.adobe.com/aharui

                            1 person found this helpful
                            • 11. Re: From ArrayCollection to Array in DataBinding
                              olegkon Level 1

                              Alex,

                               

                              A friend suggested me another solution, which I tried and it seems to work and be more straightforward:

                              main.xml:

                              [Bindable]
                                private var ac : ArrayCollection;

                               

                              <component dataProvider="{ac}" />

                               

                               

                              Component.xml:

                              [Bindable]       
                              public var dataProvider:ArrayCollection;

                               

                              private function onCreationComplete():void {

                              ...

                                 dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderUpdate);

                              }

                               

                              public function onDataProviderUpdate(e:*=null):void {
                                              if (dataProvider == null) {                  
                                                  return;
                                              }   
                                             
                                              var aca:Array = dataProvider.source;                                
                                              var dev1:Object = aca[0];    // process only 1 element of that AC                   
                                             
                                              var dev:Device = new Device();
                                              dev.cpu = dev1["cpu"];

                                              ....

                                              // pass to items array
                                              if (_items == null) {    // if new                    // _items - component's data array              
                                                  _items = new Array();
                                                  _items.push(dev);                                                
                                              } else {                   
                                                  _items.push(dev);    
                                                  updateData();                   
                                              }   
                                              updateView();                          
                                          }

                              }

                               

                              Do you like that solution ?

                               

                              The only problem I see is that when the feed runs, the size of the browser memory grows fast,

                              so by element 60 it slows down, by 95 memory consumption grows to 1.4GB and browser crashes.

                              I do not think it is related to that data binding, but not positive.

                              Can it be array.push() ?

                               

                              Any way to force garbage collection in Flex ?

                               

                              Please advise,

                              Oleg.

                              • 12. Re: From ArrayCollection to Array in DataBinding
                                Flex harUI Adobe Employee

                                It's never been clear to me what your final goal was.  You seemed to want the component's dataProvider to be re-set on every change.  Having the component itself listen for collectionChange is the recommended practice.

                                 

                                But the code you show keeps pushing more Device objects onto the items array and if they never get cleaned up or you create lots of them very quickly you will have memory issues.  Use the profiler to see if there are an increasing number of Device objects and figure out how to re-use unused ones or come up with a different approach to updating your items.

                                 

                                Alex Harui

                                Flex SDK Developer

                                Adobe Systems Inc.

                                Blog: http://blogs.adobe.com/aharui

                                • 13. Re: From ArrayCollection to Array in DataBinding
                                  olegkon Level 1

                                  >You seemed to want the component's dataProvider to be re-set on every change.

                                  Not exactly. I introduced dataProvider arraycollection to pass AC from Main to Component.

                                  Component had only private array called _items [to hold data], which I need to update on every change of AC in Main.

                                  Also, in original program that items property was never passed explicitly in MXML and has only setter (no getter).

                                  Some Cairngorm magic I haven't dug into yet.

                                  I do realize that it is a strange design [I would change that Array into ArrayCollection, make it Bindable and better build it in Main],

                                  but do not want to break that fragile and complex component [so I ended up building AC->Array Adapter  in Component].

                                   

                                  >But the code you show keeps pushing more Device objects onto the items array

                                  >and if they never get cleaned up or you create lots of them very quickly you will have memory issues.

                                  This function is supposed to be called on each AC update (when feed creates one more element from JMS message).

                                  Device objects are fairly small (10 small Strings, less than 10 chars each),

                                  so if I add <100 of them, that should not justify anything close to 1.5GB of browser/FlashVM memory usage.

                                  In real life I might pop out and nullify these objects, since only one (latest) state is shown in the visual Component.

                                   

                                  Forgot to mention: I do use a Debug version of Flash VM.

                                  Can it seriously contribute to bloated memory ?

                                   

                                  Yes, I will try to use profiler to see what's going on.

                                  Never tried that before in Flex  ;-)

                                   

                                   

                                  Thank you,

                                  Oleg.

                                  • 14. Re: From ArrayCollection to Array in DataBinding
                                    olegkon Level 1

                                    Alex,

                                     

                                    I was able to fix multiple object leaks which I could see in Profiler.

                                     

                                    Now I am trying to optimize my data feed ArrayCollection - need to keep it small - has to run for days) by doing:
                                    ac.addItemAt( {data...} , 0);
                                    if (ac.length > 1)
                                         ac.removeItemAt(ac.length-1);

                                    That works fine for that Component we worked on, but doesn't work in:
                                    <mx:LineChart dataProvider={ac} ...>

                                    adding ac.dispatchEvent (new CollectionEvent(CollectionEvent.COLLECTION_CHANGE));

                                    to add/remove item did not help.

                                    Any idea ?

                                    I do realize that I should just change data in one single element of AC
                                    instead of Add/Delete items on each tick - just a first cut.

                                     

                                    TIA,

                                    Oleg.

                                    • 15. Re: From ArrayCollection to Array in DataBinding
                                      olegkon Level 1

                                      Actually, the problem with LineChart (nothing gets displayed with small ArrayCollection)

                                      has nothing to do with Data Binding: it happens if there is less than 2 items,

                                      which of course make sense - you can't dray a Line with only 1 item !

                                       

                                      I should have thought of that.

                                       

                                      Thank you,

                                      Oleg.