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

16oct/098

Flex Modules – Utiliser des interfaces ActionScript pour communiquer avec l'application

Les derniers articles sur les modules Flex montraient comment créer une communication module-application et vice-versa. Cependant, ces techniques basées sur les propriétés child de Module Loader ou parentApplication créent une forte dépendance entre vos classes, ce qui empêche la ré-utilisabilité de vos modules.

La solution pour créer des modules peu couplés avec l'application est d'utiliser des interfaces ActionScript. Les modules vont implémenter une interface et votre application va appeler les méthodes ou fixer les propriétés définies dans l'interface. L'interface va ainsi définir les points d'accès vers votre module. Le module implémente une interface connue de l'application ou l'application implémente une interface connue par le module. Cela vous permet d'éviter les dépendances fortes entre module et application.

Dans l'application mère, quand vous souhaitez appeler les méthodes d'un module, vous devrez faire un cast (conversion de type) de la propriété "child" du ModuleLoader" en une instance de votre interface.

L'application suivante vous permet de personnaliser l'apparence du module chargé  en appelant les méthodes définies par l'interface IModuleInterface. L'application appele aussi la méthode getModuleName(). Cette méthode renvoie une valeur du module et fixe un variable locale.

L'application principale

<?xml version="1.0"?>
<!-- modules/interfaceexample/Main.mxml -->
<mx:Application xmlns="*"
                xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:Script>
    <![CDATA[
      import mx.events.ModuleEvent;
      import mx.modules.ModuleManager;

      [Bindable]
      public var selectedItem:Object;

      [Bindable]
      public var currentModuleName:String;

      private function applyModuleSettings(e:Event):void {
// Cast the ModuleLoader's child to the interface.
// This child is an instance of the module.
// You can now call methods on that instance.
        var ichild:* = mod.child as IModuleInterface;
        if (mod.child != null) {
// Call setters in the module to adjust its
// appearance when it loads.
          ichild.setAdjusterID(myId.text);
          ichild.setBackgroundColor(myColor.selectedColor);
        } else {
          trace("Uh oh. The mod.child property is null");
        }
// Set the value of a local variable by calling a method
// on the interface.
        currentModuleName = ichild.getModuleName();
      }

      private function reloadModule():void {
        mod.unloadModule();
        mod.loadModule();
      }
    ]]>
  </mx:Script>
  <mx:Form>
    <mx:FormItem label="Current Module:">
      <mx:Label id="l1"
                text="{currentModuleName}"/>
    </mx:FormItem>
    <mx:FormItem label="Adjuster ID:">
      <mx:TextInput id="myId"
                    text="Enter your ID"/>
    </mx:FormItem>
    <mx:FormItem label="Background Color:">
      <mx:ColorPicker id="myColor"
                      selectedColor="0xFFFFFF"
                      change="reloadModule()"/>
    </mx:FormItem>
  </mx:Form>
  <mx:Label text="Long Shot Insurance"
            fontSize="24"/>
  <mx:ComboBox labelField="label"
               close="selectedItem=ComboBox(event.target).selectedItem">
    <mx:dataProvider>
      <mx:Object label="Select Module"/>
      <mx:Object label="Auto Insurance"
                 module="AutoInsurance.swf"/>
    </mx:dataProvider>
  </mx:ComboBox>
  <mx:Panel width="100%"
            height="100%">
    <mx:ModuleLoader id="mod"
                     width="100%"
                     url="{selectedItem.module}"
                     ready="applyModuleSettings(event)"/>
  </mx:Panel>
  <mx:Button id="b1"
             label="Reload Module"
             click="reloadModule()"/>
</mx:Application>

L'interface implémentée par le module

package {
  import flash.events.IEventDispatcher;

  public interface IModuleInterface extends IEventDispatcher {
    function getModuleName():String;
    function setAdjusterID(s:String):void;
    function setBackgroundColor(n:Number):void;
  }
}

Le module

L'exemple suivant définit le module chargé dans l'exemple précédent et implémente l'interface IModuleInterface.

<?xml version="1.0"?>
<!-- modules/interfaceexample/AutoInsurance.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
           width="100%"
           height="100%"
           implements="IModuleInterface">
  <mx:Panel id="p1"
            title="Auto Insurance"
            width="100%"
            height="100%"
            backgroundColor="{bgcolor}">
    <mx:Label id="myLabel"
              text="ID: {adjuster}"/>
  </mx:Panel>
  <mx:Script>
    <![CDATA[
      [Bindable]
      private var adjuster:String;

      [Bindable]
      private var bgcolor:Number;

      public function setAdjusterID(s:String):void {
        adjuster = s;
      }

      public function setBackgroundColor(n:Number):void {
// Use a bindable property to set values of controls
// in the module. This ensures that the property will be set
// even if Flex applies the property after the module is
// loaded but before it is rendered by the player.
        bgcolor = n;
// Don't do this. The backgroundColor style might not be set
// by the time the ModuleLoader triggers the READY
// event:
// p1.setStyle("backgroundColor", n);
      }

      public function getModuleName():String {
        return "Auto Insurance";
      }
    ]]>
  </mx:Script>
</mx:Module>

L'application en ligne

Flex Source Code Download: Télécharger le code source complet de l'application

This movie requires Flash Player 11

De manière générale, si vous voulez fixer des propriétés dans un composant dans un module en utilisant des valeurs externes, vous devriez créer des variables Bindable. Vous pouvez ensuite fixer les valeurs de ces variables dans la méthode définie dans l'interface. Si vous essayez de fixer des propriétés sur un composant directement en utilisant des valeurs externes, le composants pourrait ne pas être instancié au moment où le module est chargé et vous pourriez avoir une RTE (RunTime Error).

Articles similaires

Commentaires (8) Trackbacks (0)
  1. Merci pour cette excellente explication! Ce site est réellement très utile. Il m'a permis de gagner beaucoup de temps à de nombreuses reprises. Merci encore!

  2. Salut,

    Dans l'exemple présent, le module MXML implémente une (seule) interface (en l'occurrence "IModuleInterface").

    Savez-vous s'il est possible de déclarer plusieurs interfaces?

    Note: Je cherche à exprimer, en MXML, l'équivalent du message suivant (ActionScript)

    Actionscript:
    1. public class TimeChooser extends HBox implements IGhostInterface, ISelectorInterface

    La casse "TimeChooser" implémente deux interfaces (IGhostInterface et ISelectorInterface).

    J'ai effectué des recherches mais sans succès.

    Merci,

    Denis

  3. Salut,
    il faut passer par la propriété "implements" du tag MXML. Pour de multiples interfaces, elles doivent être séparées par des virgules.
    Par exemple:

    Fabien

  4. Salut,

    Je te remercie pour l'information. J'avais essayé, mais j'avais certainement fait une erreur de syntaxe.

    En tous les cas, ça fonctionne.

    MXML:
    1. <?xml version="1.0" encoding="utf-8"?>
    2.  
    3. <mx:Module    xmlns:mx="http://www.adobe.com/2006/mxml"
    4.             implements="interfaces.IGhostInterface, interfaces.ISelectorInterface" xmlns:local="*">
    5.             <!-- show="init()"> -->
    6.  
    7.     <mx:Script>
    8.         <![CDATA[
    9.             import components.UIComponents.TimeChooser;
    10.        
    11.             public var __picker:TimeChooser;
    12.        
    13.             public function destroy():void                   { __picker.destroy(); }
    14.             public function getSelectedValues():Object       { return new Object(); }
    15.             public function setValues(inValues:Object):void  { }
    16.        
    17.             private function init():void
    18.             {
    19.                 __picker = new TimeChooser();
    20.                 this.addChild(__picker);
    21.                
    22.                 trace ("SWC exécuté");
    23.             }
    24.         ]]>
    25.     </mx:Script>
    26.  
    27. </mx:Module>

    Merci,

    Denis

  5. Merci pour cette illustration c'est très utile, j'ai réussi à faire la connexion entre l'application racine et un module comme il est décrit sur le tuto par contre malheureusement j'ai pas réussi à l'établir entre 2 module , pour information le chargement d'un module dans un autre est faisable mais lorsque je teste l'objet "ichild" il retourne null ... une indice stp ;) Merci.

  6. Salut,
    tu fais le chargement d'un module depuis un autre module? Tu passes bien dans l'évenement READY de ce module pour être sûr que son chargement est terminé?
    Ah oui et sinon, assure toi qu'il implémente bien IModuleInterface si tu utilises le code de cet exemple, sinon le cast avec "as" va renvoyer null.
    Si malgré tout, cela ne fonctionne pas, passe un coup en debug pour voir ou tu perds ton module

    Fabien

  7. Merci pour votre réponse fabien, mais bon jusqu'à maintenant je sais pas d'où vient le problème, J'ai trouvé un nouveau moyen pour faire communiquer mes deux modules c'est à travers parentApplication.chargeurModule.child.findByKeys(...) le module fait appel à l'application principale ,puis l'id du chargeur ensuite child et finalement la méthode voulue.

    Merci Sami.

  8. Effectivement. La méthode suivante est tout de même à préferer (plus clean):
    http://www.flex-tutorial.fr/2009/10/15/flex-modules-communication-module-vers-module/

    Fabien


Leave a comment

(required)

Aucun trackbacks pour l'instant