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

19mar/102

Flex UIComponent – Déclencher l'évènement change lors de la modification du selectedItem en AS3 (ComboBox, List et DataGrid)

Sur les composants Flex de type List (ComboBox, List et DataGrid par exemple), on dispose d'un évènement ListEvent.CHANGE ("change") qui est dispatché quand l'utilisateur fait une sélection dans la liste. Il est légèrement différent de l'évènement "itemClick" qui n'est déclenché qu'au click sur un élément de la liste.

De manière générale, il vaut mieux utiliser "change" que "itemClick" car change va dispatcher un évènement lorsque l'utilisateur fera une sélection au clavier (avec les touches directionnelles).

Le problème

Lorsque l'utilisateur fait une sélection, on a bien un évènement CHANGE qui est dispatché. Cependant, si on fixe le "selectedItem" en ActionScript (code), vous pourrez voir que l'évènement ne sera pas dispatché et pourtant la sélection sera bien faite. L'affichage sera aussi mis à jour mais aucun évènement.

Dans votre code métier, vous voulez sûrement déclencher une action quand un élément est sélectionné, que ce soit par action utilisateur ou par code. L'évènement CHANGE est donc critique pour que votre code reste homogène.

La solution

Puisque le composant Flex ne le fait pas, vous pouvez créer votre propre ComboBox (par héritage) qui va rajouter ce comportement ou tout simplement dispatcher vous-même l'évènement CHANGE pour que votre programme se joue correctement. Voici un extrait de code qui réalise cette action:

...
secondComboBox.selectedItem = item;
secondComboBox.dispatchEvent(new ListEvent(ListEvent.CHANGE));
...

Exemple complet en ligne

Ci-dessous, la démonstration des deux comportements (avec et sans dispatch d'évènement):

21fév/105

Flex Tips – Adapter automatiquement la taille d'une List / DataGrid / ComboBox suivant la donnée

Voilà un petit "tip" que j'utilise très fréquemment pour créer des interfaces plus agréables à utiliser. Ce n'est pas grand chose mais c'est toujours un petit plus. Ce petit truc sert simplement à adapter la taille d'une composant de type "liste" (List / DataGrid / ComboBox) suivant le nombre de données qu'il doit afficher.

En effet, par défaut, ces composants affichent un certain nombre d'éléments de votre dataProvider:

  • 5 pour la ComboBox (le DropDown)
  • 6 pour la DataGrid
  • 7 pour la List

Prenons maintenant l'exemple dans lequel vous avez un dataProvider à 6 éléments pour votre ComboBox. Quand vous allez l'ouvrir, une scrollbar (barre de défilement) va apparaitre pour vous permettre d'accéder au 6ème élément. Pour accéder au 6eme (ou 7e, ...) élément, on doit donc effectuer un click de plus alors que ce ne serait pas vraiment nécessaire, on pourrait afficher un DropDown affichant les 6 éléments par exemple.

Pour définir le nombre d'éléments à afficher dans un composant Flex de type List, il existe une propriété nommée "rowCount". Il suffirait donc de mettre votre rowCount à 6 et le tour est joué. Mais on va être plus malin car on ne connait pas forcement le nombre d'élément à afficher à l'avance (peut-être même qu'il peut varier).

On va ici utiliser le Data Binding pour automatiser la tâche et ne pas se soucier du pourquoi et du comment. On va simplement faire un Binding (liaison dynamique) entre la propriété "rowCount" de notre composant de type "liste" et la propriété "length" de notre dataProvider (en admettant que ce soit un Collection comme une ArrayCollection).

Voici un petit exemple:

<mx:ComboBox dataProvider="{_listOperatorsDP}" rowCount="{_listOperatorsDP.length}"/>

Et magie du Binding, notre affichage sera exactement à la taille souhaitée, pas de scrollbar. Alors vous avez sûrement déjà pensé à un cas qui pose problème, c'est le cas où l'on a beaucoup d'élément. Dans ce cas, notre Binding fera que notre liste sera immense et en plus, vous n'aurez potentiellement pas de scrollbar.

La solution est donc de simuler un "rowCountMax" (qui n'existe pas) à l'aide d'une mini-expression de test (test ternaire en ligne):

<mx:ComboBox dataProvider="{_listOperatorsDP}" rowCount="{_listOperatorsDP.length > 15 ? 15 : _listOperatorsDP.length}"/>

Ici, on va s'arrêter à 15 éléments affichés au maximum. A vous d'appliquer ce "tip" dans vos applications pour les rendre plus agréables à utiliser.

Un petit exemple exposant la différence:

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

This movie requires Flash Player 11

4nov/094

DataFilterLib – Utilisation des jokers /wildcards sur les filtres (v1.0.2)

La DataFilterLib voit aujourd'hui l'arrivée des jokers (wildcards) pour le paramétrage des filtres. Après avoir voulu utiliser la librairie que j'ai crée dans une véritable application professionnelle, j'ai vite compris que cette fonctionnalité était indispensable afin de pouvoir correctement filtrer sa donnée à l'aide d'un composant de choix ComboBox.

Prenons l'exemple d'un choix de type d'équipement. Dans notre donnée, on pourrait avoir différents types:

  • "home"
  • "auto"
  • "computers"
  • "electro"

On stocke ces éléments dans un ArrayCollection que l'on affiche dans une DataGrid. Pour une meilleure expérience utilisateur, vous laissez votre utilisateur filtrer la donnée affichée suivant le type. Ainsi, s'il choisit "electro" dans une liste de choix, il n'aura que les équipements électriques. Vous pouvez facilement réaliser cela avec les filtres de base qui étaient présents dans la DataFilterLib (voir les exemples).

Seulement, une fois que votre une fois que votre utilisateur a choisi un élément dans la ComboBox, il ne peut plus revenir à la vue initiale avec tous les éléments. L'idée est donc de rajouter un élément à votre liste appelé "Tous les types", que l'utilisateur pourra sélectionner pour ne plus filtrer selon le type. On aura donc dans notre liste:

  • "Tous les types"
  • "home"
  • "auto"
  • "computers"
  • "electro"

Vous êtes maintenant face à un problème. Si l'utilisateur choisit "Tous les types", le Binding fait que on va filtrer tous les éléments dont le type est "Tous les types", ce qui ne retournera aucun élément. Dans ce cas, la valeur "Tous les types" est notre joker (wildcard).

Dans la version 1.0.2, vous verrez donc l'arrivée d'une propriété "filterJokers" sur la classe "DataFilterParameters". Celle-ci prend soit une valeur, soit un tableau de valeurs. Le principe est celui exposé au dessus. Si on essaie de filtrer selon une valeur "joker", on ne filtre pas l'élément.

L'implémentation des filterJokers dans le code

<filter:DataFilterSet id="filterSet" data="{productData}">
	<filter:dataFilterParameters>
	  <filter:DataFilterParameters id="tstMultiValueParameter"
				filterType="{DataFilterType.SINGLE_VALUE}"
				filterKeys="productType"
				filterOperator="{DataFilterSingleValueOperator.EQUALS_TO}"
				filterValues="{typeChooserCB.selectedItem}"
				filterJokers="Any Type"/>
	</filter:dataFilterParameters>
</filter:DataFilterSet>

...

<mx:ComboBox id="typeChooserCB">
  <mx:dataProvider>
	<mx:Array>
	  <mx:String>Any Type</mx:String>
	  <mx:String>home</mx:String>
	  <mx:String>computers</mx:String>
	  <mx:String>auto</mx:String>
	  <mx:String>electro</mx:String>
	</mx:Array>
  </mx:dataProvider>
</mx:ComboBox>

Application en ligne (onglet Using Jokers)

Voici un petit exemple d'application. Si l'onglet Using Jokers n'est pas présent dans l'application, videz votre cache navigateur ;)

This movie requires Flash Player 11

30oct/0910

Flex Modules – TypeError: Error #1034: Echec de la contrainte de type : conversion de mx.managers::PopUpManagerImpl@18658d01 en mx.managers.IPopUpManager impossible [Résolu]

Voici un bug Flex que vous allez sûrement rencontrer si votre application comporte plusieurs modules à la fois. Ce n'est pas vraiment un bug en soi, c'est simplement un comportement du Framework Flex qu'il faut connaître pour éviter les surprises.

Le bug en question

Ce bug se produit de nombreuses manières différentes, mais toujours quand vous allez utiliser plusieurs modules dans une même application. Après des actions qui peuvent pourtant sembler banales, vous allez obtenir une RTE (RunTime Error) assez étrange du style:

TypeError: Error #1034: Echec de la contrainte de type : conversion de mx.managers::PopUpManagerImpl@18658d01 en mx.managers.IPopUpManager impossible.
 at mx.managers::PopUpManager$/get impl()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\PopUpManager.as:68]
 at mx.managers::PopUpManager$/addPopUp()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\managers\PopUpManager.as:169]
 at mx.controls::ComboBox/getDropdown()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1459]
 at mx.controls::ComboBox/displayDropdown()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1552]
 at mx.controls::ComboBox/downArrowButton_buttonDownHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBox.as:1801]
 at flash.events::EventDispatcher/dispatchEventFunction()
 at flash.events::EventDispatcher/dispatchEvent()
 at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
 at mx.controls::Button/http://www.adobe.com/2006/flex/mx/internal::buttonPressed()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\Button.as:2504]
 at mx.controls::Button/mouseDownHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\Button.as:2750]
 at flash.events::EventDispatcher/dispatchEventFunction()
 at flash.events::EventDispatcher/dispatchEvent()
 at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
 at mx.controls::ComboBase/textInput_mouseEventHandler()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\controls\ComboBase.as:1388]

Cette erreur de conversion (Coercion) se reproduit facilement sur le PopUpManager ou sur le DragManager mais peut aussi se produire avec vos propres classes. Vous obtiendrez des messages qui pourront vous sembler étranges. Par exemple:

TypeError: Error #1034: Type Coercion failed: cannot convert MyClass@5d73ce1 to MyClass.

Une conversion de type entre un type MyClass et le même type MyClass qui ne fonctionne pas, il y a de quoi se poser des questions. Ce bug a été maintes fois reporté sur la Bug Base d'Adobe (SDK-16474, SDK-14384, ...) mais peut facilement être corrigé.

Un exemple d'application qui pose problème

21avr/093

Flex Localization – Chargement de Resource Modules à la volée

Pour charger un Resource Module à l'exécution, vous allez utiliser la méthode loadResourceModule() du ResourceManager. Une fois que le Resource Module a fini son chargement, vous pouvez fixer la valeur de la propriété localeChain à une nouvelle valeur correspondant à la locale que vous venez de charger.

La méthode loadResourceModule() de ResourceManager est une méthode asynchrone (pouvant être comparée à la méthode loadStyleDeclarations() du StyleManager qui charge des themes Flex).

La méthode loadResourceModule() prend plusieurs paramètres. Le premier paramètre est l'URL du fichier SWF  du Module. C'est le seul paramètre obligatoire. Le second paramètre est "update". Vous pouvez le mettre à true ou false, suivant si vous voulez que l'application soit mise à jour tout de suite après le chargement du RM. Les deux paramètres suivants sont applicationDomain et securityDomain. Ces paramètres spécifient les domaines dans lesquels les Resource Modules sont chargés. Ces derniers ne sont nécessaires que si vous essayez de charger de modules venant d'autres domaines que celui où est hébergé l'application.

La méthode loadResourceModule() retourne une instance de la classe IEventDispatcher. Vous pouvez utiliser cet objet pour dispatcher des évènements de type ResourceEvent, qui vont indiquer le succès ou l'échec du chargement du module. Vous aurez accès aux évènements ResourceEvent.PROGRESS, ResourceEvent.COMPLETE et ResourceEvent.ERROR pendant le chargement.