Apache Adobe Flex TutorialTutoriaux Adobe Flex & AIR en Français

14oct/096

Flex Modules – Communication Application vers Module

Vous pouvez accéder aux méthodes et aux propriétés d'un module depuis l'application parent. Pour cela, vous devez récupérer une instance de la classe du module.

Si vous utilisez la classe ModuleLoader pour charger votre module, vous pouvez appeler des méthodes de module depuis l'application parente en prenant une référence de la propriété "child" du ModuleLoader, et en la castant (conversion de type) en la classe du module (difficile à expliquer avec des mots, l'exemple suivant sera plus parlant).

La propriété "child" est une instance de la classe du module. Dans ce cas, la classe du module est le nom du fichier MXML qui définit le module.

L'exemple suivant appelle la méthode getTitle() du module depuis l'application parente:

Code Application parente

<?xml version="1.0"?>
<!-- modules/ParentApplication.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      [Bindable]
      private var s:String;

      private function getTitle():void {
        s = (m1.child as ChildModule1).getModTitle();
      }
    ]]>
  </mx:Script>
  <mx:Label id="l1"
            text="{s}"/>
  <mx:ModuleLoader url="ChildModule1.swf"
                   id="m1"
                   ready="getTitle()"/>
</mx:Application>

Code Module (ChildModule1.swf)

<?xml version="1.0"?>
<!-- modules/ChildModule1.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
 width="100%"
 height="100%">
 <mx:Script>
 <![CDATA[
 // Defines the method that the application calls.
 public function getModTitle():String {
   return "Child Module 1";
 }
 ]]>
 </mx:Script>
</mx:Module>

Comme vous pouvez le voir, cette approche crée une liaison forte entre l'application et le module, et ne vous permettra pas de réutiliser le même code quand vous chargerez de multiples modules. Une autre technique est d'utiliser une interface qui va définir les méthodes et les propriétés d'un module (ou d'un groupe de modules) que l'application peut charger.

En passant par le ModuleManager

Si vous chargez vos modules en passant par l'API ModuleManager, il y a un peu plus de code à écrire dans l'application mère. Vous devez utiliser la propriété "factory" du ModuleManager pour obtenir une instance de la classe du module. Vous pouvez ensuite appeler la méthode du module sur cette instance.

Le module suivant définit une seule méthode, computeAnswer():

<?xml version="1.0"?>
<!-- modules/mxmlmodules/SimpleModule.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:Script>
 <![CDATA[
  public function computeAnswer(a:Number, b:Number):Number {
   return a + b;
 }
 ]]>
 </mx:Script>
</mx:Module>

L'exemple suivant récupère une instance de la classe SimpleModule en utilisant la propriété "factory" et appelle la méthode create(). On appelle ensuite la méthode computeAnswer() sur cette instance.

<?xml version="1.0"?>
<!-- modules/mxmlmodules/SimpleMXMLApp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                creationComplete="initApp()">
  <mx:Script>
    <![CDATA[
      import mx.modules.IModuleInfo;
      import mx.modules.ModuleManager;

      public var assetModule:IModuleInfo;

      public var sm:Object;

      [Bindable]
      public var answer:Number = 0;

      public function initApp():void {
// Get the IModuleInfo interface for the specified URL.
        assetModule = ModuleManager.getModule("SimpleModule.swf");
        assetModule.addEventListener("ready", getModuleInstance);
        assetModule.load();
      }

      public function getModuleInstance(e:Event):void {
// Get an instance of the module.
        sm = assetModule.factory.create() as SimpleModule;
      }

      public function addNumbers():void {
        var a:Number = Number(ti1.text);
        var b:Number = Number(ti2.text);
// Call a method on the module.
        answer = sm.computeAnswer(a, b).toString();
      }
    ]]>
  </mx:Script>
  <mx:Form>
    <mx:FormHeading label="Enter values to sum."/>
    <mx:FormItem label="First Number">
      <mx:TextInput id="ti1"
                    width="50"/>
    </mx:FormItem>
    <mx:FormItem label="Second Number">
      <mx:TextInput id="ti2"
                    width="50"/>
    </mx:FormItem>
    <mx:FormItem label="Result">
      <mx:Label id="ti3"
                width="100"
                text="{answer}"/>
    </mx:FormItem>
    <mx:Button id="b1"
               label="Compute"
               click="addNumbers()"/>
  </mx:Form>
</mx:Application>

Dans cet exemple, on devrait en fait créer un module qui hérite de la classe ModuleBase en ActionScript plutôt qu'un module MXML qui hérité de la classe Module car on a aucun élément visuel, simplement une méthode qui additionne et retourne la valeur. Un module qui hérite de ModuleBase serait plus léger qu'une classe qui hérite de Module.

<?xml version="1.0"?>
<!– modules/mxmlmodules/SimpleMXMLApp.mxml –>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initApp()">
<mx:Script>
<![CDATA[
import mx.modules.IModuleInfo;
import mx.modules.ModuleManager;

public var assetModule:IModuleInfo;

public var sm:Object;

[Bindable]
public var answer:Number = 0;

public function initApp():void {
// Get the IModuleInfo interface for the specified URL.
assetModule = ModuleManager.getModule("SimpleModule.swf");
assetModule.addEventListener("ready", getModuleInstance);
assetModule.load();
}

public function getModuleInstance(e:Event):void {
// Get an instance of the module.
sm = assetModule.factory.create() as SimpleModule;
}

public function addNumbers():void {
var a:Number = Number(ti1.text);
var b:Number = Number(ti2.text);
// Call a method on the module.
answer = sm.computeAnswer(a, b).toString();
}
]]>
</mx:Script>
<mx:Form>
<mx:FormHeading label="Enter values to sum."/>
<mx:FormItem label="First Number">
<mx:TextInput id="ti1"
width="50"/>
</mx:FormItem>
<mx:FormItem label="Second Number">
<mx:TextInput id="ti2"
width="50"/>
</mx:FormItem>
<mx:FormItem label="Result">
<mx:Label id="ti3"
width="100"
text="{answer}"/>
</mx:FormItem>
<mx:Button id="b1"
label="Compute"
click="addNumbers()"/>
</mx:Form>
</mx:Application>

Articles similaires

Commentaires (6) Trackbacks (1)
  1. Bonjour,

    l'utilisation d'interface est nécessaire car sinon on brise tout l'intérêt des modules, non ?

    si l'application utilise directement la classe du module, vu comment la compilation Flex/Flash est faite, l'application va embarquer dans son swf la classe du module plus toutes ses dépendances. On va se retrouver avec un swf de module vide et on perd ainsi l'intérêt de charger à la volée un module pour diminuer la taille du swf de l'application.

  2. Effectivement, on couple les deux de manière très forte et on perd ainsi beaucoup en portabilité. Le prochain article parlera de la manière par laquelle on peut utiliser les interfaces pour cela.

    Fabien

  3. Bonjour.
    Tout d'abord un grand merci pour ce tuto, qui est une vrai mine pour un débutant comme moi !
    J'ai avancé sur un projet et je suis en train de me battre avec les modules, et surtout la communication entre un module et son application "mère".
    Dans un premier temps, je voudrais "juste" pouvoir récupérer l'évènement creationComplete quand le module est chargé (pour être sur qu'il soit chargé, car si j'ai bien compris, le fait de récupérer le ModuleEvent.READY n'est pas suffisant).
    Mon premier souci est que je ne vois jamais passer cet évènement, donc forcement, je ne vais pas loin.
    Voici un exemple tout simple :

    MXML:
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    3. width="400" height="300"
    4. creationComplete="doEventcc();"
    5. implements="ModuleTestInterface">
    6. <mx:Script><![CDATA[
    7.  
    8. import mx.controls.Alert;
    9.     // C'est cette methode qui sera appellee depuis le main
    10.     public function runMethodOfModule(prmIn:String):void {
    11.         Alert.show(prmIn);
    12.         /* declencher un evenement */
    13.         doEvent("eventSendFromModule");
    14.     }
    15.  
    16.     private function doEvent(typeOfEvent:String):void{
    17.         var myEvent:ModuleTestEvent = new ModuleTestEvent(typeOfEvent,'string value v2',true);
    18.         dispatchEvent(myEvent);
    19.     }
    20.  
    21.     public function doEventcc():void{
    22.         Alert.show("Creation complete");
    23.         var myEventcc:ModuleTestEvent = new ModuleTestEvent("eventccSendFromModule",'string value',true);
    24.         dispatchEvent(myEventcc);
    25.     }
    26. ]]></mx:Script>
    27. </mx:Module>

    Mon test (runMethodOfModule...) fonctionne bien, par contre je ne vois jamais passer mon Alert.show("Creation complete").
    Il y a certainement quelque chose que je n'ai pas compris, mais quoi ?
    Merci

  4. Si si, tu peux bien utiliser ModuleEvent.READY pour savoir quand ton module est chargé. (tu n'as pas oublié de l'instancier le module?)

    Fabien

  5. Salut et merci pour ta reponse.
    Voici ce que je fais, en gros.
    J'utilise le IModuleInfo :

    Actionscript:
    1. /* declaration (Attention, dans un scope au niveau de la classe !!!)
    2. cf un de tes article qui m'a empéché de devenir fou :
    3. */
    4. var assetModule:IModuleInfo=null;
    5. ...
    6. assetModule = ModuleManager.getModule("ModuleTest.swf");
    7. assetModule.addEventListener(ModuleEvent.READY, moduleReady);
    8. ...
    9. assetModule.load();
    10. ...
    11.  
    12. public function moduleReady(evt:ModuleEvent):void{
    13.             var sm:Object = assetModule.factory.create() as ModuleTestInterface;
    14.             sm.runMethodOfModule("blablabla");
    15. ...
    16.     }

    C'est en testant cela que je ne vois pas mon alert.show("creation complete")

    En fait, j'avais lu à plusieurs endroits qu'il pouvait y avoir un souci, à savoir que le ModuleEvent.READY pouvait être un "faux ami" (qu'il n'était pas déclenché vraiment une fois le module totalement chargé) et qu'il fallait attendre l'évenement creationComplete dispatché par le module pour être totalement sûr qu'on pouvait commencer à l'utiliser.
    C'est pour cela que je faisait mes petits tests sur le creationComplete de mon module.
    Mais bon, à priori, cela fonctionne pas trop mal pour l'instant, donc je ne vais pas faire de zèle !
    Merci

  6. ok, cool ^^

    Fabien


Leave a comment

(required)