Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



DataGrid et XML

Compatible Flex. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par dermiste (Thomas Pujolle), le 26 avril 2009

L'idée

Dans ce tuto on va apprendre à charger des données externes depuis un XML, pour ensuite les afficher dans une dataGrid. Un petit bonus avec l'utilisation de ce qu'on appelle les ValueObject.

Pour les débutants, voir également le dernier paragraphe qui rappelle la façon la plus simple de faire cela, sans les avantages que peut donner la méthode ici présentée. (lilive - 07/11/2009)

Solution

Mise en place

Commencez par créer un nouveau projet du nom de votre choix. Une fois ceci fait, il va falloir rajouter dans notre arborescence de fichier quelques dossiers.

Dans le dossier bin-debug, créez deux un dossier que vous nommerez xml.

Voila pour l'arborescence :D

HTTPService: Récupérer le contenu de notre XML

Une fois ce dossier crée, ouvrez votre mxml principal, celui qui porte le nom de votre projet. Dans les balises applications créez un nouvel objet HTTPService. Il s'agit la du composant qui sera chargé de charger un xml de données externes. Rajoutez lui un id, “DataService” par exemple.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:HTTPService 
	id="DataService"
	/>
</mx:Application>

De même nous devons lui renseigner deux fonctions, une qui sera appelée si notre xml est correctement chargé, une qui sera appelée si il y a une erreur. On appellera donc respectivement ces deux fonctions onResult, et onFault.

Juste après la première balise application ouvrez une balise script, et créez ces deux fonctions de la manière suivante. On ne va rien y mettre pour l'instant hormis

		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
 
		private function onResult(e:ResultEvent):void {
                    trace('Succès');
 
		}
 
		private function onFault(e:FaultEvent):void {
			trace('Echec');
		}

Il ne reste plus qu'a les relier au diffuseurs d'évènements de notre HTTPService. Pour ça il suffit de rajouter les attributs “fault” et “result”, et d'y lier les fonctions correspondantes, en prenant soin de passer un évènement en argument.

<mx:HTTPService 
id="DataService"
fault="onFault(event)"
result="onResult(event)"
/>

Nous y voila, il ne reste plus qu'a créer un XML pour faire des tests, je vous suggère de prendre celui ci:

<?xml version="1.0" encoding="utf8"?>
<items>
	<item>
		<name>Thomas</name>
		<city>Paris</city>
		<age>21</age>
	</item>
	<item>
		<name>Samantha</name>
		<city>Bordeaux</city>
		<age>21</age>
	</item>
	<item>
		<name>Aurelien</name>
		<city>Limoge</city>
		<age>23</age>
	</item>		
</items>

C'est basique mais efficace :D Placez donc tout ça dans un fichier xml qu'on nommera data.xml, dans notre répertoire XML.

Revenons dans notre HTTPService et rajoutons l'attribut url. Il s'agit là du chemin pour atteindre les données à charger, relatif au swf. On y mettra donc “xml/data.xml”. De même, ajoutons un dernier attribut à ce composant, le format de données reçues, ou le “resultFormat”, que nous spécifieront comme étant e4x. E4X signifie ECMA Script For(4) Xml. Je vous invite à visiter WikiPedia pour plus d'explications. Voici donc l'état de notre composant une fois que vous avez normalement suivi la consigne:

<mx:HTTPService 
id="DataService"
fault="onFault(event)"
result="onResult(event)"
url="xml/data.xml"
resultFormat="e4x"
/>

Tout est donc presque prêt. Notre composant sait tout ce qu'il a à faire, mais si vous lancez l'application, il ne se passe rien, la console de sortie est vide(vous avez besoin de la version debug du flash player pour voir les traces dans flex, dispo ici.

Il faut en fait dire à notre composant d'exécuter la requête pour recevoir les données qui nous intéressent. Et comment on fait? Il suffit de le faire au bon moment, une fois que toute l'application est chargée par exemple :D Et justement, il y a un évènement disponible dans le composant application qui nous permet de lancer notre requête au bon moment. Rendez vous dans la balise application, pour rajouter l'attribut creationComplete, et lui donner la valeur: DataService.send(), de cette manière:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="vertical" 
	horizontalAlign="center" 
	creationComplete="DataService.send()">

Au passage rajoutez les attributs layout et horizontalAlign pour que tout se positionne correctement à l'écran.

Et cette fois-ci, vous pouvez normalement observer un “Succès” dans la fenêtre de sortie de flex :D Magique !

Traitement des données

On va devoir maintenant traiter le xml reçu pour l'afficher dans notre dataGrid. Pour ça, il faut peut être savoir ou il se trouve ! En l'occurrence il est présent dans la propriété result de l'évènement, essayez donc de remplacer le trace('Succès'); de la fonction onResult par trace(e.result); et observez le résultat. Votre xml s'affiche normalement en fenêtre de sortie. Cette fois-ci, on voit bien que tout a été chargé correctement.

On va donc devoir parcourir tous les items de notre xml pour extraire à chaque fois les données, et les rajouter dans un ArrayCollection qui sera le fournisseur de données de notre dataGrid, autrement dit, son data provider.

Commençons donc par créer cet ArrayCollection, que nous nommerons ItemProvider, au tout début de notre balise script, de cette manière en prenant soit de préciser que cette variable est Bindable.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="vertical" 
	horizontalAlign="center" 
	creationComplete="DataService.send()">
<mx:Script>
	<![CDATA[
		import mx.collections.ArrayCollection;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
 
		[Bindable]
		private var ItemProvider:ArrayCollection;

Petit rappel: une variable bindable et une variable à laquelle des composants peuvent s'abonner, pour afficher son contenu et le mettre à jour lorsque cette variable est mise à jour par exemple.

Dans onResult, nous pouvons commencer par instancier notre variable, puis passer au traitement de notre XML.

Tout ça va se passer dans une boucle de la manière suivant :

private function onResult(e:ResultEvent):void {
      ItemProvider = new ArrayCollection();
 
      for each(var item:XML in (new XML(e.result).elements())){
 
      }
}

Explications, dans notre boucle, on crée un nouveau XML à partir du résultat de notre requette. C'est le

new XML(e.result)

Ensuite on applique la methode elements() à ce nouveau XML. Autrement dit, notre boucle tournera autant de fois que la methode elements() trouve de noeud identiques dans ce nouveau XML, ici elle trouve 3 noeud “item”.

Et enfin en début de boucle nous avons un var item:XML in … cela signifie que à chaque fois que la methode elements() trouve un nouveau noeud dans notre xml, elle le stocke dans la variable item.

Un petit essai? On va essayer d'afficher chaque noeud trouvé, séparé par un commentaire. Pour ça il suffit d'écrire dans la boucle: trace(item);trace('Voici un noeud'); De la manière suivant:

private function onResult(e:ResultEvent):void {
	ItemProvider = new ArrayCollection();
	for each(var item:XML in new XML(e.result).elements()){
		trace(item);
		trace('Voici un noeud');
	}
}

Encore une fois si tout va bien, vous avez chaque noeud qui s'affiche dans la fenêtre de sortie suivi de “Voici un noeud”.

Maintenant passons à ce qui nous intéresse, récupérer la valeur de chaque balise du noeud, la stocker dans un object et ajouter cet object à notre ArrayCollection.

Sous la déclaration de la variable ItemProvider, rajoutons la variable oneItem de type object. Ce qui nous donne:

[Bindable]
private var ItemProvider:ArrayCollection;
private var oneItem:Object;

Lors de chaque passage de notre boucle, nous devront re instancier notre variable oneItem, la remplir avec les données du noeud stockée dans item, et ajouter oneItem à ItemProvider. Ou plus explicitement:

private function onResult(e:ResultEvent):void {
	ItemProvider = new ArrayCollection();
	for each(var item:XML in new XML(e.result).elements()){
		oneItem = new Object();
		oneItem.age = item.age;
		oneItem.city = item.city;
		oneItem.name = item.name;
		oneItem.img = item.img;
		ItemProvider.addItem(oneItem);
	}
}

Pourquoi la re instancier? Pour la vider de ses précédentes valeurs.

Et pour visualiser ce que fais notre boucle. Imaginez vous en train de lire vous aussi ce XML, vous cherchez le motif(=elements()) qui se répète, dans ce cas c'est la balise <item> qui revient 3 fois, pour chaque motif trouvé, vous prenez un post it(= new Object) et vous notez tout dessus, et pour finir quand vous avez tout noté, vous mettez votre post it dans une boite(= ItemProdiver.addItem(…))

Une petite astuce pour nous assurer que tout va bien, afficher le contenu complet de notre ItemProvider, pour les PHPman, c'est l'équivalent du print_r.

Pour mettre ça en oeuvre il faut taper trace(ObjectUtil.toString(ItemProvider)) en prenant soin d'importer la classe correspondante : import mx.utils.ObjectUtil;

Ce qui nous donne:

private function onResult(e:ResultEvent):void {
	ItemProvider = new ArrayCollection();
	for each(var item:XML in new XML(e.result).elements()){
		oneItem = new Object();
		oneItem.age = item.age;
		oneItem.city = item.city;
		oneItem.name = item.name;
		oneItem.img = item.img;
		ItemProvider.addItem(oneItem);
	}
	trace(ObjectUtil.toString(ItemProvider));
}

Vous pourrez observer dans la fenêtre sortie tout ce que contient notre ItemProvider, et normalement les données du XML. Vous remarquerez également au début du trace, la taille de notre ItemProvider, ici 3 est affiché , à part si vous avez 0, je vous invite donc à tout bien relire pour être sur de ne rien avoir oublié, ou si vous avez rajouté des données dans votre XML.

On y est presque ! Notre ItemProvider est rempli par notre XML correctement chargé par notre HTTPService. Terminons…

Insertion de la DataGrid, et lien avec le data provider

Juste après le HTTPService, créez un nouveau composant DataGrid, et ajoutez lui l'attribut DataProvider, que vous indiquerez par “ItemRenderer”, c'est notre arrayCollection qui contient nos données. Le Bindable prend tout son sens ici, si jamais notre ItemRenderer ne l'était pas, la DataGrid ne pourrait pas “lire” ce qu'il contient et l'afficher.

Vous avez normalement donc ça pour ce qui est du DataGrid:

<mx:DataGrid dataProvider="{ItemProvider}" />

Si vous lancez maintenant votre application, vous pourrez voir que nos données sont affichées dans le DataGrid, missions accomplie !

Pour finir

D'autres tutoriels devraient normalement suivre pour rendre l'affichage et le traitement des données plus riche. Ne manquez pas la suite ;)




En savoir plus

Suite à cette remarque sur le forum, j'ajoute ici la façon la plus basique d'afficher les données d'un XML dans un DataGrid :

<mx:XML id="xml" source="xml/data.xml"/>
<mx:DataGrid dataProvider="{xml.item}"/>