DataFilterLib – Pagination de données filtrées (ArrayCollection avec filterFunction)
On m'a posé une question très intéressante sur la DataFilterLib:
Cedric
Bonjour moi j'aurais une petite question :
Comment intégrer les fonctionnalités de filtrage sur une dataGrid dont les données sont paginées (à l'aide du paginateur disponible sur ce site)?
En effet, la DataFilterLib permet de filtrer des données côté client selon plusieurs critères, de manière déclarative (MXML) ou dynamique (AS). Elle permet, pour simplifier, de donner plusieurs filterFunction à un ArrayCollection.
Et pour la pagination (côté client), le plus simple et le plus effectif est d'utiliser aussi une filterFunction sur une ICollectionView, un ArrayCollection par exemple. La question est alors, comment combiner les deux?
Application de test
Voilà le code utilisé pour tester l'application. Le dataProvider se trouve dans un fichier à part pour faciliter la lecture:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%"
paddingRight="20" xmlns:filter="com.fnicollet.datafilter.filter.*"
xmlns:pagination="pagination.*">
<mx:Script>
<![CDATA[
import pagination.event.PaginateEvent;
import com.fnicollet.datafilter.filter.DataFilterType;
import com.fnicollet.datafilter.filter.DataFilterSingleValueOperator;
[Bindable]
private var _startIdx:int = 0;
[Bindable]
private var _endIdx:int = 999999999;
private function pageChangeHandler(e:PaginateEvent):void {
var page:int = e.index;
_startIdx = (page * e.itemsPerPage) + 1;
_endIdx = Math.min((_startIdx + e.itemsPerPage - 1), e.itemsTotal);
//txt.text = "Results: " + String(_startIdx) + " - " + String(_endIdx) + " of " + e.itemsTotal;
}
]]>
</mx:Script>
<mx:Script source="/data/StateData.as"/>
<filter:DataFilterSet id="filterSet" data="{statesData}">
<filter:dataFilterParameters>
<filter:DataFilterParameters id="simpleParam" filterType="{DataFilterType.SINGLE_VALUE}"
filterKeys="state"
filterOperator="{DataFilterSingleValueOperator.LIKE}"
filterValues="{stateInput.text}"/>
</filter:dataFilterParameters>
</filter:DataFilterSet>
<mx:Label text="Filter By State Name (Contains)" fontSize="14" fontWeight="bold"/>
<mx:TextInput id="stateInput"/>
<mx:Label text="Pagination over the filtered elements" fontSize="14" fontWeight="bold"/>
<pagination:PageSelector id="paginator" itemsPerPage="6" rangeCount="6"
itemsTotal="{statesData.length}" selectedIndex="0"
pageChange="pageChangeHandler(event)"/>
<mx:Label text="Filtered Data" fontSize="14" fontWeight="bold"/>
<mx:DataGrid rowCount="7" dataProvider="{statesData}" width="100%">
<mx:columns>
<mx:DataGridColumn dataField="state" headerText="State Name"/>
<mx:DataGridColumn dataField="sales" headerText="Sales"/>
<mx:DataGridColumn dataField="employees" headerText="Number of Employees"/>
<mx:DataGridColumn dataField="population" headerText="Population"/>
</mx:columns>
</mx:DataGrid>
<mx:Label text="Unfiltered Data" fontSize="14" fontWeight="bold"/>
<mx:DataGrid rowCount="8" dataProvider="{statesData.source}" width="100%">
<mx:columns>
<mx:DataGridColumn dataField="state" headerText="State Name"/>
<mx:DataGridColumn dataField="sales" headerText="Sales"/>
<mx:DataGridColumn dataField="employees" headerText="Number of Employees"/>
<mx:DataGridColumn dataField="population" headerText="Population"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
Un composant générique de pagination est intégré et met seulement à jour 2 variables Bindable. Il n'effectue encore aucun filtrage:
Composant Flex – Interface de Pagination générique
Premier test: Ajouter un nouveau filtre pour la pagination
Dans un premier temps, la première idée que j'ai eu fut de simplement ajouter un nouveau filtre, se comportant comme un filtre de type Interval, dont l'intervalle irait entre l'index de mon premier élément de page et le dernier élément.
Pour créer ce nouveau filtre, qui n'existe pas par défaut, il suffit de créer une classe qui hérite de "DataFilterInterval": DataFilterIntervalPagination. Pour l'ajouter en tant que filtre, il suffit d'utiliser la technique que j'expose dans cet article:
DataFilterLib – Utilisation de filtres personnalisés
Pour une utilisation dans une application, il suffit ensuite de donner:
<filter:DataFilterParameters filterClass="{DataFilterIntervalPagination}"
id="intervalPagination" filterValues="{[_startIdx, _endIdx]}"/>
Et déjà, là, premier obstacle, la méthode de filtrage de base. Pour simplifier, la DataFilterLib boucle sur la méthode "apply" de chaque filtre dont la signature est la suiavnte:
public function apply(item:Object):Boolean {
On prend ensuite chaque résultat et si au moins un d'entre eux est false, l'objet est filtré. Pour les filtres sur valeur et sur intervalle, on cherche à comparer une certaine propriété de l'item (donnée par le paramètre "filterKeys"). En pseudo-code, on a :
public function apply(item:Object):Boolean {
var value:String = item[filterKey];
if (value == filterValue){
return true;
}
return false;
}
On fait ici la distinction par rapport à une propriété de l'objet. Seulement dans notre cas, on ne veut pas comparer une des propriétés de l'objet mais bien la position de l'objet dans le dataProvider. Si son index est dans la page courante on garde l'objet, sinon on le filtre.
AdvancedDataGrid, Charting et OLAPDataGrid (datavisualization.swc) enfin GRATUIT!
En voilà une excellente nouvelle de la part d'Adobe (non, pas la sortie de Flash Player 10.1
), la mise à disposition du SWC datavisualization.swc dans le SDK Open Source et sans restriction de licence.
Il y a 9 mois, je vous annonçais l'open-sourcing du Flex Data Visualization mais ici on parle bien de l'aspect gratuit de cette librairie. En effet, Open source ne veut pas forcement dire gratuit.
La nouvelle responsable produit sur Flex (Deepa Subramaniam) en a fait l'annonce lors du 360|Flex:
Flex advanced data visualization components now part of the FREE open source Flex SDK!
Pour résumer, vous n'aurez plus besoin d'une licence Flex Builder Professionnal pour utiliser les composants de la librairie datavisualization.swc. Parmi eux, on peut trouver:
- AdvancedDataGrid: Composant qui va bien au delà de la DataGrid, avec de nouvelle possibilités d'itemRenderer, de bilan et de groupement (et bien d'autres)
- Charting: Tous les graphiques possibles avec Flex (histogramme, bulles, etc.)
- OLAPDataGrid: Peut servir pour des besoins spécifiques d'agrégation de données.
Je viens de vérifier sur le dernier "nightly build" de Flex 4, le swc est bien là dans le SDK Open Source:
Télécharger le dernier build du Flex 4 SDK
Voila donc une excellente nouvelle pour la communauté Flex, puisque l'on peut maintenant réellement créer l'ensemble des applications entreprise que l'on souhaite sans débourser un sou (pour rappel, le SDK est gratuit, seul Flex Builder est payant). Le prix d'entrée pour un développeur se trouve du coup largement réduit. Pour rappel, voici la grille des prix Adobe:
- Flex Builder 3 standard: 249$
- Flex Builder 3 professional: 699$
Bon maintenant, on peut se demander à quoi va servir une licence Flex Builder Pro par rapport à une licence Flex standard. Pour l'instant je ne vois que le profiler et peut-être les wizards (même pas sûr et puis qui utilise les wizards ?^^). En regardant les derniers build Flex 3 SDK, on dirait que ce n'est que pour le SDK Flex 4. Mais bon, on peut compiler des applications Flex 3 avec le Flex 4 SDK donc peu importe.
Bref, vivement que Flex 4 et Flex Builder 4 sortent, il se font vraiment attendre maintenant
Composant Flex – Interface de Pagination générique (exemple)
Il y a quelques semaines, je présentais sur Adobe Flex Tutorial un composant Flex nommé Paginator qui était une interface de pagination générique pour vos applications. Pour rappel, la pagination sert à présenter la donnée par bouts (pages) afin de ne pas noyer l'utilisateur. Dans un commentaire, CapoeiraDance demandait à voir un exemple d'application de ce composant. Voici donc un exemple que j'ai concocté moi-même.
Dans cet exemple, on a une donnée sur les états américains avec quelques valeurs. J'ai choisi de n'afficher que 8 éléments pas page pour montrer le fonctionnement de l'interface de pagination. La vraie pagination (au niveau de la data) se fait par une "filterFunction" sur le dataProvider (ArrayCollection). Ainsi, on va regarder l'index de l'item dans la donnée pour savoir s'il est dans la page courante. Le code n'est sûrement pas optimal mais cela donne une idée.
Attention, pour indiquer le nombre total d'éléments au paginator, on utilise bien la propriété "length" de la "source" de l'ArrayCollection et pas la "length" de l'ArrayCollection. En effet, si on utilise directement statesData.length, les résultats seront erronés car la fonction de filtre va modifier cette longueur.
J'aurai pu être chauvin et utiliser la DataFilterLib pour créer cette pagination mais gardons les choses simples pour l'instant
.
Un peu de code pour commencer
[Bindable]
private var statesData:ArrayCollection = new ArrayCollection([...]);
...
// variables utilisées par la fonction de filtre
private var _startIdx:int = 0;
private var _endIdx:int = 1000;
private function init():void{
// on donne une fonction de filtre à la donnée pour n'afficher que les résultats courants
statesData.filterFunction = paginateFilterFunction;
statesData.refresh();
}
private function pageChangeHandler(e:PaginateEvent):void{
// affichage du nombre de résultats courant
var page:int = e.index;
var startIndex:int = (page * e.itemsPerPage) + 1;
var endIndex:int = Math.min((startIndex + e.itemsPerPage -1), e.itemsTotal);
_startIdx = startIndex - 1;
_endIdx = endIndex;
txt.text = "Results: " + String(startIndex) + " - " + String(endIndex) + " of " + e.itemsTotal;
// filtrage de la donnée par page
statesData.refresh();
}
private function paginateFilterFunction(item:Object):Boolean{
var itemIdx:int = statesData.getItemIndex(item);
return (itemIdx >= _startIdx && itemIdx < _endIdx) || itemIdx == statesData.source.length;
}
...
<local:PageSelector id="paginator"
itemsPerPage="{8}"
rangeCount="6"
itemsTotal="{statesData.source.length}"
selectedIndex="0"
pageChange="pageChangeHandler(event)" />
L'application en ligne
Flex Source Code Download: Télécharger le code source complet de l'application






