Dans ce tutorial nous allons créer une petite application exécutable pour Windows à l'aide du tout nouveau framework MVC "RobotLegs" et du logiciel SWF Studio. J’utiliserai Flex Builder 4 pour la compilation mais il est tout à fait possible d’utiliser le flex sdk 3 en utilisant que des composants mx au lieu de spark.
L'application est un client FTP, qui servira simplement à « uploader » des fichiers vers un FTP de manière très rapide. Pourquoi ce projet ? Eh bien tout simplement pour aborder en douceur la conception d’une application robotlegs mais également parceque j’avais besoin d’un petit widget me permettant d’uploader mes fichiers simplement vers un répertoire juste au drag and drop ! Voici un aperçu de l’application :

La partie haute présente la liste des uploads en cours, les fichiers sont ajoutés au glissé-déposé. Au dessus deux boutons permettent de se connecter au ftp et de lancer l’upload. La partie basse présente une zone de texte qui log tout les évênements.
Vous pouvez télécharger le logiciel, et les sources :
Pour ce tutorial il vous faut :
- Le Flex SDK 3 minimum
- Un compilateur comme Flex Builder / FDT / Ant
- Les librairies RobotLegs et SwiftSuspenders
- SwfStudio et ses librairies SWC
Création du projet
Commençons par créer un nouveau projet Flex appelé « FlexFTPClient ». Installez SwfStudio et linkez les swc coreAS3Definitions.swc et NorthcodeAS3Lib.swc situé dans le répertoire « SWC » du dossier d’installation de swfStudio. Téléchargez RobotLegs puis linkez le swc lib/swiftSuspenders.swc pour permettre l’utilisation des méta [Inject] dans votre projet, puis ajoutez un library-path vers le répertoire des sources de robotLegs. Nous voilà prêt pour commencer !
Démarrage et bootstrap
Pour ce petit projet, nous allons créer plusieurs vues :
- ListingView : représentera la liste des uploads en cours
- StatView : représente la partie basse de l’application, une zone de texte pour logguer tout ce qui se passe.
- ConnexionView : affiche deux boutons pour se connecter au ftp et lancer l’upload.
- DragDropView : est un carré de 100% x 100% chargé de détecter le déposé de fichiers sur l’application.
Ainsi que leurs « médiateurs » chargés d’interagir avec tout le reste de l’application.
Puis nous auront besoin de la partie « model » pour stocker les données. Il s’agit ici de stocker la liste des uploads.
La partie « controller » contiendra les évênements de l’application ainsi que deux commandes. La commande demandant la connexion FTP et celle demandant le démarrage de l’upload.
Enfin la partie « service » contiendra notre service de connexion FTP chargé de se connecter et d’uploader les éléments contenu dans le model.
Commençons donc par créer les répertoires model, view, controller et service, qui contiendront nos classes. Puis nous commençons par remplir notre fichier FlexFTPClient.mxml, point d’entrée de l’application :
<?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/mx" xmlns:view="view.*" creationComplete="init(event)" backgroundColor="0xeeeeee"> <fx:Style source="Plastic.css"/> <fx:Script> <![CDATA[ import mx.events.FlexEvent; private var context:MainContext; protected function init(e:FlexEvent):void { ssCore.init(); context = new MainContext(this); } ]]> </fx:Script> <view:DragDropView /> <view:ConnexionView height="24"/> <mx:VDividedBox top="24" width="100%" height="100%"> <view:ListingView height="50%"/> <view:StatView height="50%" /> </mx:VDividedBox> </s:Application>
Ligne 6 : j’importe toutes mes vues pour les ajouter à l’application lignes 25 à 31. Nous allons voir la création de nos vues juste après.
Ligne 26 : on initialise les composants de swfStudio.
Ligne 27 : on initialise le contexte de l’application. C’est lui qui va servir de jointure entre toutes nos classes pour le MVC.
Jusque ici rien de complexe, voyons maintenant ce fameux contexte :
package { import flash.display.DisplayObjectContainer; import org.robotlegs.mvcs.Context; import view.*; import controller.*; import model.*; import service.*; public class MainContext extends Context { public function MainContext(ContextView:DisplayObjectContainer=null, autoStart:Boolean = true ) { super(ContextView, autoStart); } override public function startup():void { mediatorMap.mapView(ListingView, ListingViewMediator); mediatorMap.mapView(ConnexionView, ConnexionViewMediator); mediatorMap.mapView(StatView, StatViewMediator); mediatorMap.mapView(DragDropView, DragDropViewMediator); injector.mapSingleton(ListingModel); injector.mapSingleton(FTPService); commandMap.mapEvent(ClientEvent.LAUNCH_FTP_CONNEXION, LaunchFTPConnexionCommand); commandMap.mapEvent(ClientEvent.LAUNCH_UPLOAD, LaunchUploadCommand); } } }
Le contexte d’une application RobotLegs lance le démarrage avec la fonction startup qu’il vous faut donc réécrire.
Ligne 17 à 20 : on « map » chaque vue à son médiateur, chargé d’établir la communication entre elle et le reste de l’application.
Ligne 22 et 23 : on spécifie à l’injecteur que le model de donnée et le service doivent être injectés dans toutes les classes où ils seront appelés, en tant que singleton ! Et ceci est très important. A chaque fois que vous verrez dans une classe [Inject] public var monModel :ListingModel ; c’est l’instance du singleton qui y sera automatiquement placée ! Ceci nous évite bien des efforts. Pas besoin de développer le model en singleton ou même d’appeler monModel.getInstance() . Merci swiftSuspenders !
Ligne 25 et 26 : on MAP deux évênements vers nos commandes de connexion et d’upload. Ainsi quelque soit l’endroit où l’on va dispatcher ses deux évênements, les deux commandes seront éxécutées automatiquement.
C’est tout pour notre contexte, créons à présent nos vues et leur médiateurs.
Création des vues
La vue ConnexionView est très simple, nous y plaçons deux boutons, un pour la connexion et un pour lancer l’upload avec le serveur FTP.
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%"> <mx:HBox> <s:Button id="btConnect" label="Connecter !" width="100" height="25" /> <s:Button id="btUpload" label="Uploader !" width="100" height="25" /> </mx:HBox> </mx:Canvas>
Son médiateur est à placer dans le même package view. L’écriture du médiateur est également très concise :
package view { import controller.ClientEvent; import flash.events.MouseEvent; import org.robotlegs.mvcs.Mediator; import service.*; public class ConnexionViewMediator extends Mediator { [Inject] public var connexionView:ConnexionView; override public function onRegister():void { eventMap.mapListener(connexionView.btConnect, MouseEvent.CLICK, connect); eventMap.mapListener(connexionView.btUpload, MouseEvent.CLICK, upload); } private function connect(e:MouseEvent){ dispatch(new ClientEvent(ClientEvent.LAUNCH_FTP_CONNEXION)); } private function upload(e:MouseEvent){ dispatch(new ClientEvent(ClientEvent.LAUNCH_UPLOAD)); } } }
Lignes 11 et 12 : Nous récupérons l’instance de notre vue toujours grâce à l’injection de constructeur.
Ligne 17 : on écrase la function onRegister, appelée une fois que la vue est linkée. Dans cette fonction nous écoutons les évênements de click sur nos deux boutons. Qui servent à lancer la connexion ou l’upload au FTP. Puis on dispatch les fameux évênements chargés de lancer les commandes de connexion ou d’upload.
Passons à la vue « ListingView » chargée d’afficher la liste des uploads stockée dans le model en temps réel. Pour se faire, rien de très compliqué il faut « binder » le model de donnée pour que chaque mise à jour des données soit dispatchée vers notre liste. Nous verrons le model juste après.
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:exoaFlex="flex.*" width="100%" height="100%"> <fx:Script> <![CDATA[ public function updateItem(value){ var renderer:Array = liste.renderers[0]; if(renderer[0] != null) { if(value==100) ListItemRenderer(renderer[0]).progressBar.visible=false; else ListItemRenderer(renderer[0]).progressBar.visible=true; ListItemRenderer(renderer[0]).setProgress(value); } } ]]> </fx:Script> <exoaFlex:ExtendedList id="liste" width="100%" height="100%" itemRenderer="view.ListItemRenderer" /> </mx:Canvas>
Vous pouvez remarquer que je n’utilise pas de composant List mais une List étendue permettant d’accèder facilement à ses itemRenderers et de mettre à jour la barre de progression de l’item en cours d’upload.
Son médiateur :
package view { import controller.ClientEvent; import exoa.server.FTPConnexionEvent; import model.*; import view.ListItemRenderer; import mx.controls.List; import mx.controls.listClasses.ListItemRenderer; import org.robotlegs.mvcs.Mediator; import service.*; public class ListingViewMediator extends Mediator { [Inject] public var listingView:ListingView; [Inject] public var listingModel:ListingModel; [Inject] public var FTP:FTPService; override public function onRegister():void { eventMap.mapListener(eventDispatcher, FTPConnexionEvent.TRANSFER_ITEM_PROGRESS, onTransferItemProgress); listingView.liste.dataProvider=listingModel._listing; } private function onTransferItemProgress(e:FTPConnexionEvent){ listingView.updateItem(e.valeur); } } }
Ligne 26 : on écoute notre service FTP pour récupérer les informations de progression de transfert servant à mettre à jour notre vue.
Ligne 28 : on assigne le model comme source de donnée au dataProvider de notre Liste.
Notre model est constitué comme ceci :
package model { import controller.ClientEvent; import mx.collections.ArrayCollection; import org.robotlegs.mvcs.Actor; [Bindable] public class ListingModel extends Actor { [Bindable] public var _listing:ArrayCollection; public function ListingModel() { _listing = new ArrayCollection([ ]); } public function addToList(files:Array) { for (var i:int = 0; i < files.length;i++){ _listing.addItem({name:files[i].name,url:files[i].url}); } _listing.refresh(); } } }
Une classe bindable, et une ArrayCollection également Bindable permettant de stocker et synchroniser les données avec le reste de l’application.
La fonction addToList() permettra d’ajouter des fichiers à la liste au drag and drop depuis DragDropView.
Enfin notre StatView qui sert dont à logguer les évênements dans un textArea :
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%"> <s:TextArea editable="false" width="100%" height="100%" id="textArea" /> <fx:Script> <![CDATA[ public function update(content:String){ textArea.appendText("\n"+content); } ]]> </fx:Script> </mx:Canvas>
La fonction update() servira à y rajouter du texte depuis notre médiateur :
package view { import org.robotlegs.mvcs.Mediator; import controller.ClientEvent; public class StatViewMediator extends Mediator { [Inject] public var statView:StatView; override public function onRegister():void { eventMap.mapListener(eventDispatcher, ClientEvent.ON_APPENED, onAppend); } public function onAppend(e:ClientEvent) { statView.update(e.retour); } } }
A chaque reception d’un évênement ON_APPENED, provenant de n’importe quelle acteur de notre application, on appelle la function update() de notre StatView.
Cette première partie est déjà bien copieuse, nous verrons la suite au prochain article, à savoir les commandes, le service FTP, et la création du projet SWF Studio !
Lire la suite


Sadlig dit:
Bonjour et merci pour ce tuto,
avr 02, 2010, 14 h 47 minUne question: Un client ftp est-il envisageable en flash dans le navigateur? Cette question peut paraître stupide mais je suis totalement noob en flash/Flex