6 Replies Latest reply on Oct 15, 2009 4:50 PM by 80sRelic

    Pass Parameter to Module Loaded with ModuleManager

    80sRelic

      Is it possible to pass a parameter to a module loaded with ModuleManager?

        • 1. Re: Pass Parameter to Module Loaded with ModuleManager
          mewk Level 3

          80sRelic,

           

          First, to answer a lingering question from your last post, the reason simIndex was 3 for each module (as opposed to 1,2,3) has to do with timing. When you ran getDataFromParent() within the module you probably did so after an event like the creationComplete event. The for loop (with simIndex++) executed almost instantly while the asynchronous events for each module kicked in a few ms later, so '3' across the board.

           

          Also, as far as the IModuleInfo::factory.create() method goes, yes you can pass a slew of parameters in here, but this is NOT the way to pass data to the module. I don't know what this parameter is all about, I thought it might pass variables to the module constructor (it does not), the documentation says something about 'let building factories change what they create,' but I never figured it out. Sorry. All I know is that whatever this is, it is not the way.

           

          BTW, the general topic we're dealing with is 'passing data' between module and application and more info can be found here and in the flex 3 cookbook.

           

          But, I'm guessing you already know this. (Just in case!!! )

           

          Of all the ways to pass data, gettingDataFromTheParent() is probably the worst method, or so they say. In general, passing data between module and application couples the two objects together and so they are no longer are they black boxes to each other. And I'm guessing the reason gettingDataFromParent() is the least preferred method probably has to do with change, where changeOverTimeOfApp > changeOverTimeOfModule.

           

          I wrote a small app, which might help demonstrate a nice way for you to pass data in your project. Please note that the app, as it stands doesn't make much sense. Modules should only be used when there's a really good reason to do so, for instance if the module is prohibitively large and only a fraction of the users will actually ever need to employ the module. In my particular example, using a custom component would be simpler and make much much more sense.

           

          Well, here it is (with .fxp appended at the bottom). Hope this helps,

           

          - e

           

          <?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/halo" >
          
            <fx:Script>
              <![CDATA[
                import modules.ColoredRectangle;
          
                import mx.collections.ArrayCollection;
                import mx.events.ModuleEvent;
                import mx.modules.IModuleInfo;
                import mx.modules.ModuleManager;
                private var myModInfo:IModuleInfo;
                private var myColor:uint;
          
                private var colorCollection:ArrayCollection = new ArrayCollection(
                     [{colorName:"Red",    hex:"0xFF0000"},
                      {colorName:"Orange", hex:"0xFFA500"},
                      {colorName:"Yellow", hex:"0xFFFF00"},
                      {colorName:"Green",  hex:"0x008000"},
                      {colorName:"Blue",   hex:"0x0000FF"},
                      {colorName:"Indigo", hex:"0x4B0082"},
                      {colorName:"Violet", hex:"0xEE82EE"}] );
          
                protected function fetchModules():void     {
                  myModInfo = ModuleManager.getModule("modules/ColoredRectangle.swf");
                  myModInfo.addEventListener(ModuleEvent.READY, moduleReadyHandler);
                  myModInfo.load();
                }
          
                private function moduleReadyHandler(event:ModuleEvent):void {
                  for each(var colorObj:Object in colorCollection) {
                     var myModuleInstance:ColoredRectangle = myModInfo.factory.create() as ColoredRectangle;
                     myModuleInstance.myColor = uint(colorObj.hex);
                     tileBox.addElement(myModuleInstance);
                  }
                }
          
              ]]>
            </fx:Script>
          
            <s:HGroup verticalCenter="0" horizontalCenter="0" 
                      width="750" height="400" gap="50">
          
               <s:Border width="100%" height="400">
                  <s:layout>
                     <s:VerticalLayout />
                  </s:layout>
          
                  <s:Button id="seeModulesButton1" label="fetch modules!"
                         click="fetchModules()" width="100%"/>
          
                  <s:Scroller width="100%" height="100%">
                     <s:Group  id="tileBox" height="100%" width="100%">
                        <s:layout>
                           <s:TileLayout columnAlign="justifyUsingGap" />
                        </s:layout>
                     </s:Group>
                  </s:Scroller>
          
               </s:Border>
          
             </s:HGroup>
          
          </s:Application>
          

          • 2. Re: Pass Parameter to Module Loaded with ModuleManager
            80sRelic Level 1

            This is super helpful, thank you so much. I made some progress on this but I see some things you have here that are great - getting a handle of the module instance is the key. I'll post up what I've done after I factor in your input, thanks again.

            • 3. Re: Pass Parameter to Module Loaded with ModuleManager
              Flex harUI Adobe Employee

              FWIW:

               

              1) A module is loaded once per url.  The url can have parameters and the loaderInfo.parameters can be used to get at those parameters

              2) A module is a factory for classes.  You can create more than one instance of the module's main class or any classes in the module.

              3) The factory.create() method takes zero or one parameter (it can take more, but they are ignored by Flex).  If zero parameters, create() creates an instance of the modules main class.  If you pass in the fully qualified name of a class, the module's factory.create() creates an instance of that class if applicationDomain topologies allow

              4) There is no general way to parameterize calls to constructors.  You can't use function.apply or function.call

               

               

              Alex Harui

              Flex SDK Developer

              Adobe Systems Inc.

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

              1 person found this helpful
              • 4. Re: Pass Parameter to Module Loaded with ModuleManager
                80sRelic Level 1

                Thanks Flex harUI.

                 

                I've managed to get this going. Below is the relavant code of what I am doing:

                 

                private function onModuleReady (e:ModuleEvent):void {
                                
                                 var i:int;
                                
                                 for (i = 0; i < mySimCount; i++)
                                 {
                                    
                                     //put the mac addresses into an array
                                     mySimMac.push(getSimsResult.lastResult[i].mac);
                                                        
                                     // cast the currentTarget
                                     var moduleInfo:IModuleInfo = e.currentTarget as IModuleInfo;
                                    
                                     // Add an instance of the module's class to the
                                     // display list.
                                    
                                     var myModuleInstance:simPanel = moduleInfo.factory.create() as simPanel;
                                    
                                     myTile.addChild(myModuleInstance);
                                    
                                     //set the panel id
                                     myModuleInstance.id = "pnl"+ simIndex.toString();
                                     myModuleInstance.panelInit(mySimMac[i]);
                                    
                                     simIndex++;
                                 }
                             }

                 

                Works well. Now I need to update the data in each panel at some interval. I have it setup now so that each panel creates it's own timer and updates - works fine. But what if I wanted the parent app to control the updates with a single timer? I assume I can interate through the instances of the modules and then call a method in the module to update the data. Somethig like this:

                 

                for (i = 0; i < mySimCount; i++)
                                 {

                 

                                    //get the handle of an instance

                 

                                    myModuleInstance.panelUpdate(mySimMac[i]);

                 

                 

                                 }

                 

                How can I iterate through all of the instances of the module?

                • 5. Re: Pass Parameter to Module Loaded with ModuleManager
                  mewk Level 3

                  @80sRelic, Nice to see some of your code up and running . It looks like you are loading several different modules with each module creating multiple instances. I have one concern, and it's really an open-ended question, which is about recycling IModuleInfo. If you have two test cases:

                  // test case 1:
                  var myModInfo:IModuleInfo;
                  
                  for (var i:int = 0; i<3; i++) {
                       myModInfo = ModuleManager.getModule("modules/SomeModule.swf");
                       myModInfo.load();
                  }
                  

                   

                  // test case 2:
                  
                  myModInfo = ModuleManager.getModule("modules/SomeModule.swf");
                  myModInfo.load();
                  
                  myModInfo = ModuleManager.getModule("modules/AnotherModule.swf");
                  myModInfo.load();
                  
                  myModInfo = ModuleManager.getModule("modules/SomeModule.swf");
                  myModInfo.load();
                  
                  
                  
                  

                   

                  then I believe in testCase1, swf data is only transfered once, but in testCase2 data is loaded three separate times? So be careful for that (possibly this is entirely irrelevant for your code).

                   

                  Now to help with your last question regarding the parent app updating all modules on some timer. You could write an interface for all the modules, create an array of all the added instances and then update the collection as needed, something like this:

                   

                  <s:Application>
                       <fx:Script>
                            public var myModuleCollection:ArrayCollection = new ArrayCollection();
                  
                            public function updateAllModules():void {
                                 for each( var tempInstance:ISimPanel in myModuleCollection ) {
                                      tempInstance.panelUpdate();
                                 }
                            }
                  
                            private function onModuleReady (e:ModuleEvent):void {
                                 for (var i:int = 0; i < mySimCount; i++) {
                                      mySimMac.push(getSimsResult.lastResult[i].mac);
                                      var moduleInfo:IModuleInfo = e.currentTarget as IModuleInfo;
                                      var myModuleInstance:simPanel = moduleInfo.factory.create() as ISimPanel;
                                      myModuleInstance.id = "pnl"+ simIndex.toString();
                                      myModuleInstance.panelInit(mySimMac[i]);
                                      myTile.addChild(myModuleInstance);
                                      myModuleCollection.addItem(myModuleInstance);
                                      simIndex++;
                                 }
                            }
                      
                       </fx:Script>
                  </s:Application>
                  

                   

                   

                  package
                  {
                      public interface ISimPanel {
                          function panelInit():void;
                          function panelUpdate():void;
                      }
                  }
                  

                   

                   

                  <mx:Module xmlns:fx="http://ns.adobe.com/mxml/2009" 
                       xmlns:s="library://ns.adobe.com/flex/spark" 
                       xmlns:mx="library://ns.adobe.com/flex/halo" 
                       implements="ISimPanel">
                  

                   

                   

                  @Alex, Well you could pass data through the url string, but I'm unable to come up with a good strategy to handle this test case. To create multiple instances from a module and pass data in the url string you would need to loop through several variations of

                  myModInfo = ModuleManager.getModule("modules/someModule.swf?data=differentDataStringHereOnEachLoop");
                  

                  and in order to create an instance you would need to wait for the ModuleEvent.READY for each loop cycle, which seems like a very inefficient loop. (this is my thinking anyway!!!).

                   

                  Also, thank you very much for the extra info, I didn't realize the module was THE factory (i.e. myModInfo.factory = someModule.swf). I went spelunking in the SDK but I'm still a bit (ok very!) confused about the create() method. Does it matter if the module was written in AS3 vs mxml? If I gave you a module like:

                   

                  <mx:Module namespaces...>
                       <components.blueCircle />
                       <components.redSquare />
                  </mx:Module>
                  

                   

                  are you saying that the I could have the module/factory create, say, just the redSquare?? How do I do this? ... need to see code!!

                   

                  tanks,

                   

                  - e

                  • 6. Re: Pass Parameter to Module Loaded with ModuleManager
                    80sRelic Level 1

                    Good tip on putting the instances into an array. I'll give that a try in the morning.

                     

                    My app so far consists of a main application and 1 module. The module is a collection of components - labels, buttons, data grid. When the main app loads it makes a service call to get the number of modules it needs to load. Then it creates the module. Then it calls the function in my last post that creates the needed number of instances of the module.

                     

                     

                    protected function init():void {
                                   
                                    //get number of panles needed for this center
                                    getSimsResult.token = fLASH.getSims(myCenter);
                                   
                                    //create the panel module - loaded later
                                    _moduleInfo = ModuleManager.getModule("simPanel.swf");

                     

                                    // add some listeners
                                    _moduleInfo.addEventListener(ModuleEvent.READY, onModuleReady);
                                    _moduleInfo.addEventListener(ModuleEvent.SETUP, onModuleSetup);
                                    _moduleInfo.addEventListener(ModuleEvent.UNLOAD, onModuleUnload);
                                    _moduleInfo.addEventListener(ModuleEvent.PROGRESS, onModuleProgress);
                                   
                                    //initialize panels
                                    loadModule();
                                   
                    }

                     

                    The module instances makes service calls using the uniqie MAC addresses that were passed to it during loading.So far, it seems to work.

                     

                    Do you see any issue with this?