17 Replies Latest reply on Jan 17, 2007 1:54 AM by Camus Miu

    Best method for passing data between nested components

    jsevlie Level 1
      I have a fairly good sized Flex application (if it was stuffed all into one file--which it used to be--it would be about 3-4k lines of code). I have since started breaking it up into components and abstracting logic to make it easier to write, manage, and develop.

      The biggest thing that I'm running into is figuring out a way to pass data between components. Now, I know how to write and use custom events, so that you dispatch events up the chain of components, but it seems like that only works one way (bottom-up). I also know how to make public variables/functions inside the component and then the caller can just assign that variable or call that function.

      Let's say that I have the following chain of components:

      Component A
      --Component B
      -- -- Component C
      -- -- -- Component D

      What is the best way to pass data between A and D (in both directions)?

      If I use an event to pass from D to A, it seems as though I have to write event code in each of the components and do the bubbling up manually. What I'm really stuck on though, is how to get data from A to D.

      I have a remote object in Component A that goes out and gets some data from the server, and most all of the other components all rely on whatever was returned -- so what is the best way to be able to "share" data between all components? I don't want to have to pass a variable through B and C just so that D can get it, but I also don't want to make D go and request the information itself. B and C might not need the data, so it seems stupid to have to make it be aware of it.

      Any ideas? I hope that my explanation is clear enough...

      Thanks.

      -Jake

        • 1. Re: Best method for passing data between nested components
          peterent Level 2
          Using events from D to A is the way to go, but you can make it a bit easier. Let's say your custom event is DEvent.as and the event type "testData". When you want to tell A about this event in D, do this:

          In D:
          var event:DEvent = new DEvent("testData",true); // you want it to bubble:
          dispatchEvent(event);

          B,C, and D should have metadata: [Event(name="testData",type="DEvent")]

          And that's all you need. From A you can do: <B testData="handleDEvent(event)" /> you can even put the metadata on A if you want. Bubbling events means that each component up the chain has to be able to handle the event, but you just need the metadata in the minimum.

          As for A communicating with D. I solve this problem by placing my RemoteObject calls into a separate class and use it via static methods (its called a 'singleton' since you want only 1 to exist).

          [Event(name="dataEvent1",type="dataEvent1Type")]
          // more events, one per RemoteObject method result

          public class DataServices extends EventDispatcher {
          public static function getInstance() : DataServices { if( _self == null ) _self = new DataServices(); return _self' }
          private static var _self:DataServices;
          /* functions which initialize the RemoteObject (id="srv") and define the methods and result handlers
          */
          static function requestData( param:Object ) : void { getInstance().srv.requestData(param); }
          }

          Then from D I would do:
          DataServices.addEventListener( "dataEvent1", handleDataEvent );

          Then in A I can do:
          DataServices.requestData( param );

          Since D has registered to receive the dataEvent1, when the result handler in DataServices receives the data, DataServices dispatches a dataEvent1 event which D picks up.

          This way you can also have A listen for the event if it needs the information, too.

          This may seem a bit complex, but once you separate things out you have this nice distributed system and that allows you to make RemoteObject calls from anywhere in your code and have the results used anywhere else. For example, may be you are calling the RemoteObject method in A, but you really want to call it from B. Now you can call it from B and have D receive the result. Later you might have a RemoteObject method that C needs to invoke but A needs to receive its result.

          The trick is to be organized about it .
          • 2. Re: Best method for passing data between nested components
            jsevlie Level 1
            Thank you! This is exactly the kind of help that I was looking for (being able to make calls from anywhere and have the results used anywhere else). I will try out what you've mentioned and respond back if I run into anything strange.

            I really appreciate the response... This will help me get to the next step.

            Thanks!
            • 3. Re: Best method for passing data between nested components
              jsevlie Level 1
              quote:

              Originally posted by: peterent
              /* functions which initialize the RemoteObject (id="srv") and define the methods and result handlers
              */
              static function requestData( param:Object ) : void { getInstance().srv.requestData(param); }



              Peter, can you elaborate on this part of the class? Where does the "srv" come from? Does it get defined from within the DataServices class? I think I understand why the method is there (it's wrapping the call to the remote method so that it can be sucked up and dispatches), but am a little unclear on the "srv" part -- which I'm assuming is a RemoteObject object. Where does that get instantiated?

              Thanks.
              • 4. Re: Best method for passing data between nested components
                ntsiii Level 3
                "srv" is the id of the RemoteObject:
                <mx:RemoteObject id="srv" .../>

                Tracy
                • 5. Re: Best method for passing data between nested components
                  peterent Level 2
                  Tracy's right - it is the id of the RemoteObject in my example; you can make it anything you wish.

                  You can either make DataServices.mxml and use <mx:RemoteObject id="srv" ... > or make DataServices.as and private var srv:RemoteObject - whichever you are comfortable with.

                  And of course, "DataServices" was just my example name. If you want to go the ActionScript route, then you need to instantiate the RemoteObject, srv, instance. Something like this:

                  srv = new RemoteObject( destination here);
                  srv .addEventListener(FaultEvent.FAULT, handleFault);
                  srv . method_name_here.addEventListener(ResultEvent.RESULT, someResultHandler);

                  HTH,
                  Peter
                  • 6. Re: Best method for passing data between nested components
                    jsevlie Level 1
                    So let's say that I create a class called "RemoteMethods". It is contained within a file called "RemoteMethods.as".

                    This is what the class looks like:

                    package com
                    {
                    import flash.events.EventDispatcher;
                    import mx.rpc.remoting.RemoteObject;

                    public class RemoteMethods extends EventDispatcher
                    {
                    private static var _self:RemoteMethods;
                    private static var srv:RemoteObject;

                    // Follows a singleton pattern.
                    public static function getInstance():RemoteMethods {
                    if( _self == null ) {
                    _self = new RemoteMethods();
                    }
                    return _self;
                    }

                    // One static function per remote method that we want to call.
                    static function getUsers(brandid:Number):void {
                    getInstance().srv.getUsers(brandid);
                    }
                    static function getDocuments(brandid:Number):void {
                    getInstance().srv.getDocuments(brandid);
                    }
                    }
                    }

                    Now, if I put the following code:

                    srv = new RemoteObject(destination)
                    .. etc..

                    into the getInstance function, I get an error that says:

                    "#1119: Access of possibly undefined property srv through a reference with static type com:RemoteMethods"

                    If I try to instantiate it inside either getUsers or getDocuments, I get the same error.

                    Am I completely missing something? To me this is the hardest part about Flex -- knowing your object design methodologies well enough to get around.

                    Again, I completely appreciate the help, I've already gotten a lot farther than I would have on my own.

                    Thanks,
                    Jake
                    • 7. Re: Best method for passing data between nested components
                      peterent Level 2
                      srv is not a static variable so it is not addressable in the static function, getInstance().

                      You should create the RemoteObject in the RemoteMethods constructor:

                      public function RemoteMethods() {
                      srv = new RemoteObject(destination);
                      // etc.
                      }

                      Now when getInstance() is called the first time, it will instantiate an instance of RemoteMethods which automatically calls its constructor, creating the RemoteObject, srv.

                      I should add that this is not a Flex-specific design method; it is a standard software design pattern known as "Singleton". You can get any good book on software design using object-oriented principles and it will easily apply to Flex.
                      • 8. Re: Best method for passing data between nested components
                        jsevlie Level 1
                        Yah, man, I had forgotten all about the constructor function. I took an entire year of object design (including covering most of the Gang of Four book) in grad school, and here I am flailing around, completely clueless. You'd think I'd know better.

                        Ok, I got it working now... I have a constructor function that initializes the RemoteObject and all is good. Now I'll just go off and get the whole event dispatching going.

                        I appreciate the help.
                        • 9. Re: Best method for passing data between nested components
                          jsevlie Level 1
                          Has anybody ever written a fully-described explanation of his this works? I'm getting farther along but must be missing something.

                          Now all I'm having trouble with is trying to figure out where all of the event declaration stuff should go. I have verified that the remote object call is working (I can see the request hit my CFC on the server), but I'm not sure how to capture the result.

                          Here's how I have it set up right now:

                          ----------------
                          RemoteMethods.as
                          ----------------
                          same code as displayed earlier in this thread, except now "srv" is NOT a static and the getInstance() function has this code:

                          srv= new RemoteObject("ColdFusion");
                          srv.source = "report.ddv3.datahome";


                          ----------------
                          main.mxml
                          ----------------
                          import com.RemoteMethods;
                          RemoteMethods.getUsers(BRAND_ID);



                          Just by doing this, I can see the getUsers command hit the server. Ok, so then the question is how do I capture the result?

                          Question 1: Where exactly do you declare the event meta data? Inside the class file? Like this:

                          [Event(name="getUsers", type="flash.events.Event")]
                          [Event(name="getDocuments", type="flash.events.Event")]

                          public class RemoteMethods extends EventDispatcher
                          {
                          ...
                          }

                          Or does it go somewhere else?


                          Question 2: Where do you declare the event listeners? I tried doing the following things:my

                          In main.mxml:
                          RemoteMethods.addEventListener("getUsers", handleUserRequest);

                          But get an error, "1061: Call to a possibly undefined method addEventListener through a reference with static type Class"


                          Then, instead of that, I tried this in main.mxml:
                          var rm:RemoteMethods = new RemoteMethods;
                          rm.srv.getUsers.addEventListener("getUsers", handleUserRequest);

                          And it compiles ok, but the "handleUserRequest" function never gets invoked.



                          Question 3: Where do you dispatch the events? Or is this handled for you automatically somehow?


                          After this is all over and I've got it all figured out I'm going to write up a detailed example of what we've worked through here and share it so that someone else can just cut/paste all of this code!

                          I am definitely humbled that this is such a complex process, although I know that once I get it working I'll think it's as easy as pie.

                          Thx.


                          • 10. Re: Best method for passing data between nested components
                            peterent Level 2
                            Question #1: You have that correct. [Event] metadata above the class declaration within the package. Lets the compiler know the class may be dispatching those events.

                            Question #2: addEventListener is not a static function of RemoteMethods, so you want to get the instance:
                            RemoteMethods.getInstance().addEventListener(...);

                            Question #3: When you define the remote object, also declare event listeners for the results of each method:

                            srv= new RemoteObject("ColdFusion");
                            srv.source = "report.ddv3.datahome";
                            srv.getUsers.addEventListener( ResultEvent.RESULT, getUsers_result );

                            Then in RemoteMethods class, define the getUsers_result function and it will dispatch the event:

                            private function getUsers_result( event:ResultEvent ) : void
                            {
                            // store the event.result someplace in RemoteMethods
                            dispatchEvent( new Event("getUsers") ) ;
                            }

                            You will probably want to define your own custom event class to place the result from the remote method call into the event when you dispatch it.
                            • 11. Re: Best method for passing data between nested components
                              jsevlie Level 1
                              YES!!!!!!!

                              Peter, thank you very very much for that last peice of advice... Now I'm getting the result back like I had expected.

                              WHEW! Yes, there are a lot of peices to get set up. I will respond back to this post after I'm done creating my sample app so that everyone else can benefit.

                              Wow this feels good to have it working. This will make my life much much easier.

                              -Jacob
                              • 12. Best method for passing data between nested components
                                jsevlie Level 1
                                Peter (or anyone else)...

                                To take this example to the next (albeit parallel) level, how would you go about creating a class that will let you just capture/dispatch local data changes? Following along my original example (Components A-D),let's say that we have this component architecture:

                                Component A
                                --Component B
                                -- -- Component C
                                -- -- -- Component D
                                -- -- Component E
                                -- -- Comonnent F

                                How would we go about creating a dispatch scheme for getting data between Component C and E/F? Maybe in Component C the user picks a username from a combo box. That selection will drive some changes in Component E (like triggering a new screen to appear based on the user). There are no remote methods at play with this example, just a simple update of a username that's all contained within the Flex app.

                                I tried mimicking the technique that we used for the RemoteObject methods, but things are a bit different this time around because we're not making a trip to the server. I just want to be able to register Component E to listen for an event that would indicate that some data has changed.

                                Now, once again, I know that I can bubble that information up to A and then back down to E, but that's sloppy... There has to be a similar approach to broadcasting events across the entire application, right?

                                Here's what I started to come up with so far:

                                ---------------------------------------------------
                                [Event(name="selectUsername", type="CustomEvent")]
                                public class LocalData extends EventDispatcher
                                {
                                private static var _self:LocalData;

                                // Constructor
                                public function LocalData() {
                                // ?? does anything go here ??
                                }

                                // Returns the singleton instance of this class.
                                public static function getInstance():LocalData {
                                if( _self == null ) {
                                _self = new LocalData();
                                }
                                return _self;
                                }

                                // public method that can be called to dispatch the event.
                                public static function selectUsername(userObj:Object):void {
                                dispatchEvent(new CustomEvent(userObj, "selectUsername"));
                                }
                                }
                                ---------------------------------------------------


                                Then, in the component that wants to dispatch the event, we do this:
                                ---------------------------------------------------
                                LocalData.selectUsername([some object]);
                                ---------------------------------------------------


                                And in the component that wants to listen for the event:
                                ---------------------------------------------------
                                LocalData.getInstance().addEventListener("selectUsername", selectUsername_Result);

                                public function selectUsername_Result(e:CustomEvent):void {
                                // handle results here
                                }
                                ---------------------------------------------------


                                The problem with this is that when I go to compile it, it doesn't like my use of "dispatchEvent" inside that public static method. Tells me, "Call to possibly undefined method "dispatchEvent". Huh? Why would it be undefined?

                                Does it make sense with where I'm going?

                                Any help is greatly appreciated.

                                Thanks!

                                -Jacob


                                • 13. Re: Best method for passing data between nested components
                                  peterent Level 2
                                  Give this type of set up:
                                  Component A
                                  --Component B
                                  -- -- Component C
                                  -- -- -- Component D
                                  -- -- Component E
                                  -- -- Comonnent F
                                  Since components C, E, and F are siblings, it would be easiest to do something like:

                                  <C id="c" someEvent="e.someFn(event); f.someFn(event)" ... />

                                  Where you have public functions in E and F which accept an event. If the event has a single item of data, say a "User" then maybe:

                                  <C id="c" someEvent="e.user=event.user; f.user=event.user" ... />

                                  Both E and F could have user setter functions.
                                  • 14. Best method for passing data between nested components
                                    jsevlie Level 1
                                    Peter,

                                    Following the thread of this conversation, I'm now having an issue with SSL in Internet Explorer for my shared objects. In IE, on our production environments, I just get errors when trying to connect up to my remote objects. However, they work fine in Firefox. Another person and I have been posting about this in another thread:

                                    http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?catid=585&threadid=1208935

                                    So I did some searching around and found that you had a post relating to this almost 2 years ago:
                                    http://weblogs.macromedia.com/pent/archives/2005/02/operating_in_pa.cfm

                                    I also found this article:
                                    http://weblogs.macromedia.com/lin/archives/2006/08/flex_app_works.cfm

                                    However, I am using this whole singleton pattern for my remote objects that we've discussed in this thread at length. That means that I only ever have 1 RemoteObject instantiated at once across my entire app, and then listeners attached to wherever I want to get data results back.

                                    So, my question is, is there way to get around this SSL/IE issue with the way that you and I have set up this singleton-patterned RemoteObject setup? I can't really change the endpoint on-the-fly because Flex is telling me things like "Channel disconnected before an acknowledgement received".

                                    Any thoughts/ideas?

                                    Oh, and also for reference, we have full SSL certificates purchased from Network Solutions (they are not self-signed).


                                    UPDATE: Nevermind, I found the solution:

                                    Placing this in my channel-definition in services-config.xml:

                                    <properties>
                                    <add-no-cache-headers>false</add-no-cache-headers>
                                    </properties>

                                    I wasn't doing the outer properties tag. But putting that in there fixed it.

                                    • 15. Best method for passing data between nested components
                                      Camus Miu
                                      This is marked as my favarite topics on the forum. Really helps much.

                                      However, I still have some questions.

                                      Give this type of set up:
                                      Component A
                                      --Component B
                                      -- -- Component C
                                      -- -- -- Component D
                                      -- -- Component E
                                      -- -- Comonnent F
                                      -- -- -- Component G

                                      if D is going to communicate with G. Isn't Singleton Design Pattern the only way to make it work efficiently?
                                      Seems it's always stupid to bubbling the event to A and B, then down to F , G, right?
                                      • 16. Re: Best method for passing data between nested components
                                        peterent Level 2
                                        I think that would be the most straightforward way. G could register with the singleton to listen for an event. D would ask the singleton to dispatch the event.

                                        It is nice to use the event flow system and to architect your app around that, but if you find yourself in a situation like this, especially after having to re-write or re-factor your code, the singleton pattern can make quick work of it.
                                        • 17. Re: Best method for passing data between nested components
                                          Camus Miu Level 1
                                          Thanks a lot for the help.

                                          and thanks Jake for starting this case =)