Ajout et traitement de Metadata personnalisés
Tutorial Flex écrit par Laurent Wroblewski (http://flex-ria.fr/)
Publiez vous aussi sur flex-tutorial!
Tout flexeur connaît les metadatas, ou tout du moins la plus connue, j'ai nommé [Bindable]. Une métadonnée va servir à marquer une propriété d'un objet pour lui permettre d'adopter un comportement spécifique. Par exemple, le binding va permettre de lier une source (la propriété bindée) et toutes ses cibles (les objets la référençant). Mais il existe également d'autres tags pour les metadatas, certes moins utilisés.
La liste des metadatas est décrite ici :
http://livedocs.adobe.com/flex/3/html/help.html?content=metadata_3.html
Mais qu'en est-il si vous souhaitez ajouter vos propres marqueurs à vos objets, avec vos propres traitements liés? C'est le but de cet article.
La création de metadatas personnalisées se divise en trois étapes. Elle est décrite ci-dessous à travers l'exemple d'implémentation suivant : un tag permettant de définir une fonction d'un composant comme listener d'événement pour un composant et un type d'événement donnés. Par exemple, on définira que la fonction btnTest_ClickHandler sera appelée sur le clic (événement MouseEvent.CLICK) sur le bouton btnTest.
1. Ajout des metadatas.
La première chose à faire est d'ajouter un tag aux propriétés que vous souhaitez marquer.
[Listener (obj = "btnTest", event = "click")]
public function btnTest_ClickHandler(evt:MouseEvent):void{
Alert.show("Clic sur le bouton btnTest.");
}
Ici, nous ajoutons donc notre propre tag (appelé Listener) à la fonction btnTest_ClickHandler. A noter que celui-ci comporte deux propriétés :
- obj : l'identifiant du composant auquel nous allons ajouter un écouteur.
- event : le type de l'événement à écouter (ici, click pour MouseEvent.CLICK).
A noter que les propriétés d'un tag sont toutes des chaînes de caractères. N'espérez donc pas passer des objets complexes (en même temps, les metadatas étant gérées à la compilation de votre application, cela s'avère logique)!
2. Traitement de vos metadatas.
Après avoir marqué vos objets, il reste maintenant à définir le traitement à effectuer sur ces objets. Pour ce faire, la méthode describeType de Flex va vous permettre de récupérer un XML énumérant les variables, accesseurs, méthodes, etc… de l'objet passé en paramètre. Cette méthode est très utile pour faire de l'introspection d'objets, car elle va vous permettre de connaître une foule d'informations sur un objet donné.
Attention cependant, les propriétés private ou protected ne sont pas récupérées.
Grâce à cette fonction, nous allons donc pouvoir lister les méthodes dotées de notre tag Listener, et récupérer ainsi l'objet et l'événement associés. A partir de là, il ne reste plus qu'à créer l'écouteur d'événement souhaité.
public function manageListeners(pCompo:UIComponent):void
{
//Récupération des infos de la classe.
var classInfo:XML = describeType(pCompo);
var obj:IEventDispatcher;
var event:String;
//On boucle sur les méthodes de l'objet.
for each (var method:XML in classInfo..method)
{
//Si la méthode a un tag Listener...
if (method.metadata && method.metadata.@name == "Listener")
{
//...On récupère les infos associées...
obj = pCompo[method.metadata.arg.(@key == "obj").@value];
event = method.metadata.arg.(@key == "event").@value;
if (obj && event)
{
//...Et on crée l'écouteur.
obj.addEventListener(event, pCompo[method.@name] as Function, false, 0, true);
}
}
}
}
3. Configuration du compilateur Flex
Vous avez peut-être lancé votre application, et curieusement, aucune trace de votre metadata… C'est tout à fait normal, car le compilateur Flex a besoin d'être informé de la présence de votre nouveau tag pour l'exploiter à la compilation.
Pour ce faire, rajoutez l'option de compilation suivante (clic droit sur votre projet > Flex Compiler > Additional compiler arguments) :
-keep-as3-metadata+=Listener
N'oubliez pas le + du +=, sinon vous écraserez la liste des metadatas Flex!
Ceci n'est qu'un exemple d'utilisation des metadatas. Certains frameworks de Flex, comme Swiz (http://swizframework.org/), utilisent également des metadatas customs.
Flex Tips – Le Metatag Event pour déclarer la propagation d'un évènement par un composant AS / MXML
Après le Metatag DefaultProperty, très utile pour les composants instanciés en MXML, voici une autre astuce que vous devez absolument connaître si vous développez des composants Flex personnalisés. Ce metatag permet d'indiquer au compilateur que votre classe va propager (dispatch) un évènement. Ce n'est pas une étape obligatoire, si vous faîtes un dispatchEvent() dans votre composant, votre évènement sera toujours propagé.
Le principal avantage qu'il apport est un confort de développement, puisque Flex Builder va faire de l'auto-complétion sur votre code (en MXML et en AS). Vous avez donc moins de chances de vous trompez et vous pouvez ainsi réaliser une vraie API.
Ajouter le Metatag Event à sa classe
Notez que pour pouvoir dispatcher un Event depuis votre classe / composant, votre classe doit hériter de EventDispatcher (ou passer par un objet qui en hérite). Vous aurez ainsi accès aux méthodes dispatchEvent() et addEventListener(). Pour ce métatag, 2 attributs sont à préciser au minimum:
- name: Le type d'Event (sous forme de String) ainsi que le type d'objet évènement.
- type: Le type d'objet évènement, il faut donner le "qualified name" de la classe, c'est à dire le nom de la classe avec les noms de packages. Si vous propagez un simple event,il faut par exemple donner: flash.event.Event.
Pour une classe ActionScript
Dans le cas d'une classe ActionScript, il suffit de placer le Metatag juste avant la déclaration de classe:
package comp {
import flash.events.Event;
import flash.events.EventDispatcher;
[Event(name="enableChanged", type="myeventpackage.CustomEvent")]
public class MonComposant extends EventDispatcher {
private var _enabled:Boolean=false;
public function MonComposant() {
}
public function set enabled(value:Boolean):void {
dispatchEvent(new CustomEvent("enableChanged"));
}
}
}
// fichier event custom
package myeventpackage
{
import flash.events.Event;
public class CustomEvent extends Event
{
public static const ENABLED_CHANGED:String = "enabledChanged";
public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}
Dans un composant MXML
Pour un composant MXML, il faut utiliser le tag <mx:Metadata> directement en tant qu'enfant du nœud principal:
Flex Tips – Le metatag [DefaultProperty] pour la création de composants MXML Flex
Voici un petit "tip" pour les développeurs Flex qui créent leurs propres composants Flex. Vous le savez, la plupart les classes de Flex peuvent être créées de manière déclarative en MXML. Il en va de même pour les classes AS ou MXML que vous créez vous-même.
Prenons un petit exemple. On crée une classe MenuItem.as qui nous servir à peupler une MenuBar:
package model
{
import mx.collections.ArrayCollection;
public class MenuItem
{
public var label:String = null;
public var children:ArrayCollection = null;
public function MenuItem()
{
}
}
}
La classe est très très simple, on a simplement 2 propriétés, label et children, qui sont des valeurs clé pour Flex quand il va chercher une hiérarchie. En effet, si on donne comme dataProvider, un ArrayCollection contenant des MenuItem, il va chercher la propriété "label" pas défaut pour définir le nom à afficher si on ne lui donne pas de "labelField". Idem pour la propriété "children", c'est dans cette propriété qu'il va chercher des éléments fils.
Pour construire un simple menu tout en MXML, on aura donc:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*" xmlns:model="model.*">
<mx:MenuBar id="menuComponent">
<mx:dataProvider>
<model:MenuItem label="Menu1" >
<model:children>
<mx:ArrayCollection>
<mx:Array>
<model:MenuItem label="SubMenu1"/>
<model:MenuItem label="SubMenu2">
<model:children>
<mx:ArrayCollection>
<mx:Array>
<model:MenuItem label="SubSubMenu1"/>
<model:MenuItem label="SubSubMenu2"/>
</mx:Array>
</mx:ArrayCollection>
</model:children>
</model:MenuItem>
</mx:Array>
</mx:ArrayCollection>
</model:children>
</model:MenuItem>
<model:MenuItem label="Menu2"/>
</mx:dataProvider>
</mx:MenuBar>
</mx:Application>
Cela fonctionne très bien, notre menu s'affiche comme on le voulait. On remarque cependant que c'est plutôt verbeux pour la création d'un menu à plusieurs étages. En effet, quand on veut ajouter des éléments fils, on doit déclarer la propriété "model:children" (ma classe se trouve dans le package "model", puis un mx:ArrayCollection puis un mx:Array.
Mais vous pouvez contourner cela pour alléger votre code en précisant une propriété par défaut (DefaultProperty). Cette propriété est déclarée à l'aide d'un Metatag sur votre classe comme ceci:
package model
{
import mx.collections.ArrayCollection;
[DefaultProperty("children")]
public class MenuItem(){
....
Alors à quoi sert cette annotation? Et bien cette annotation sert à indiquer à Flex quel sera la propriété à utiliser par défaut quand on va ajouter un noeud MXML comme enfant à notre classe/composant. Ici, on précise que l'on souhaite utiliser "children" par défaut. Il n'est donc plus besoin de créer un tag model:children, Flex sait déjà que quand on ajoute des fils, on cherche à modifier la propriété "children".





