Spesso, durante lo sviluppo di applicazioni Flex, ci siamo trovati ad utilizzare il component ComboBox per consentire la selezione di un elemento da una lunga lista di oggetti. Quando, però, i dataProvider assegnati alle combo box contengono molti elementi è scomodo utilizzare il component ComboBox e obbligare l'utente a ricercare l'elemento di interesse costringendolo a scorrere tutta la lista.
In sostituzione delle combobox possiamo utilizzare un component molto potente chiamato AutoComplete, che consente di gestire il riempimento di un campo di testo filtrando il dataProvider in stile Facebook.
Una demo completa delle funzionalità è disponibile all'indirizzo http://flex-autocomplete.com/demo/.
Scaricare la libreria
Il sito ufficiale della libreria è http://hillelcoren.com/flex-autocomplete. Qui è possibile trovare la documentazione e alcune dimostrazioni di utilizzo. La libreria è scaricabile, invece, da questa pagina.
Estraiamo il file che abbiamo appena scaricato e al suo interno, oltre alle due cartelle con il codice sorgente per Flex3 e Flex4 e alla cartella contenente la documentazione, troviamo la cartella 'bin' che contiene le librerie compilate. A noi, per lo sviluppo dell'applicazione di esempio, serve la libreria AutoComplete-1.1-Fx4.swc.
Prepariamo il progetto
Per prima cosa, dobbiamo creare un progetto Flex (scegliamo l'sdk 4.1 visto che è la più aggiornata), come mostrato in figura:
Una volta creato il progetto, dobbiamo importare la libreria. Lo possiamo fare o copiando il file SWC nella cartella 'libs' (consigliato):
o dalla preferenze del progetto, selezionando la voce Flex Build Path e cliccando su Add Swc:
A questo punto, la configurazione del progetto è finita e possiamo iniziare ad usare le funzionalità del component AutoComplete. Nel prosieguo dell'articolo analizzeremo prima gli utilizzi di base del component, poi svilupperemo un esempio in cui utilizzeremo il component AdvancedAutoComplete che presenta caratteristiche avanzate.
Il primo esempio
Sviluppiamo una semplice applicazione per testare la funzionalità di base del component, cioè il filtro sulla struttura dati che gli viene assegnata come dataProvider. Definiamo la lista di valori hardcoded all'interno dello statement fx:Declarations
e la utilizziamo assegnandola alla proprietà dataprovider
del component AutoComplete. Di seguito è mostrato il codice:
<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" width="300" height="150" xmlns:components="com.hillelcoren.components.*">
<fx:Declarations>
<s:ArrayCollection id="listValue">
<fx:String>Antonio</fx:String>
<fx:String>Armando</fx:String>
<fx:String>Michele</fx:String>
<fx:String>Nicola</fx:String>
<fx:String>Pippo</fx:String>
<fx:String>Pluto</fx:String>
</s:ArrayCollection>
</fx:Declarations>
<s:Label x="103" y="25" text="Esempio1" fontSize="21"/>
<s:HGroup x="28" y="57" verticalAlign="middle">
<s:Label text="Cerca un nome:"/>
<components:AutoComplete x="107" y="80" dataProvider="{listValue}">
</components:AutoComplete>
</s:HGroup>
</s:Application>
Effettuando un primo test dell'applicazione possiamo notare (ed è mostrato nella figura seguente) come funzioni in maniera automatica il filtro sugli elementi dell'arraycollection definito come dataprovider (figura 4).
Iniziamo, a questo punto, ad esaminare alcune proprietà interessanti del component, proprietà che ci permettono di aumentarne le funzionalità a seconda di ciò che dobbiamo realizzare. Un primo set di proprietà riguarda la visualizzazione delle labels, sia all'interno del campo di ricerca/selezione che all'interno della tendina (dropdown list) che contiene gli elementi filtrati:
- labelField: il campo degli oggetti del dataprovider che verrà visualizzato nel menu a tendina e nel campo di testo (al valore del campo viene applicato il filtro di ricerca);
- labelFunction: funzione che viene invocata per ogni oggetto del dataprovider e deve ritornare una stringa che verrà visualizzata sia nel menu a tendina che all'interno del campo di testo (al valore ritornato viene applicato il filtro di ricerca);
- dropDownLabelFunction: funzione che viene invocata per ogni oggetto del dataprovider e formatta una stringa che verrà visualizzata solo nel menu a tendina;
- dropDownItemRenderer: il component da utilizzare come itemrender per gli elementi visualizzati nel menu a tendina.
L'algoritmo di ricerca può essere modificato modulando la proprietà MatchType
che può avere tre valori:
- beginning: fa il match solo della parte iniziale della stringa;
- word: fa il match della parte iniziale di ogni parola della stringa;
- anyPart: fa il match con ogni parte della stringa.
Sviluppiamo adesso un altro esempio per testare le proprietà di cui abbiamo appena discusso. Per fare ciò, invece di considerare la lista dell'esempio precedente, costruiamo una lista di oggetti più complessi (non consideriamo semplici stringhe): per l'occasione creiamo un oggetto Person
con name
, surname
e gender
come proprietà:
public class Person { private var _name:String; private var _surname:String; private var _gender:String; public function Person() { } public function get name() : String { return _name; } public function get surname() : String { return _surname; } public function get gender() : String { return _gender; } public function set name(n:String) : void { _name = n; } public function set surname(s:String) : void { _surname = s; } public function set gender(g:String) : void { _gender = g; } }
Nell'applicazione dichiariamo una struttura dati ArrayCollection
formata da oggetti di tipo Person
. Per la visualizzazione dei risultati parziali del filtro di ricerca costruiamo un ItemRenderer
che mostra i campi Name
e Surname
dell'oggetto Person
e, in base al campo Gender
(sesso), mostra un simbolo diverso. Questo Item Renderer dovrà essere utilizzato come dropDownItemRenderer
. Di seguito è riportato il codice dell'itemRenderer:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
updateComplete="handleUpdate()"
verticalGap="0" horizontalGap="0"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
verticalAlign="middle"
width="100%" height="100%">
<fx:Script>
<![CDATA[
import mx.controls.List;
import vo.Person;
override public function set data(value:Object):void {
super.data = value;
var p:Person = value as Person;
nameSurname.htmlText = (owner as List).labelFunction(p);
if (p.gender == "M") {
imageGender.source = "assets/male.png";
} else {
imageGender.source = "assets/female.png";
}
}
private function handleUpdate():void{
graphics.clear();
graphics.lineStyle( 1, 0x858585, 0.5 );
graphics.moveTo( -2, height + 2);
graphics.lineTo( width, height + 2 );
}
]]>
</fx:Script>
<mx:Label id="nameSurname" />
<mx:Image id="imageGender" includeInLayout="true" width="25" height="25" />
</mx:HBox>
Mentre di seguito è riportato il codice dell'applicazione:
<?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" width="300" height="250" xmlns:components="com.hillelcoren.components.*" xmlns:vo="vo.*">
<fx:Declarations>
<s:ArrayCollection id="listPerson">
<vo:Person name="Nicola" surname="Strisciuglio" gender="M" />
<vo:Person name="Mario" surname="Rossi" gender="M" />
<vo:Person name="Rosa" surname="Brambilla" gender="F" />
<vo:Person name="Giulio" surname="Bianchi" gender="M" />
<vo:Person name="Anna" surname="Esposito" gender="F" />
<vo:Person name="Michele" surname="Verdi" gender="M" />
<vo:Person name="Armando" surname="Gazzetta" gender="M" />
<vo:Person name="Maria" surname="Rossi" gender="F" />
</s:ArrayCollection>
</fx:Declarations>
<fx:Script>
<![CDATA[
private function makeNameSurnameLabel(item:Object) : String {
var p:Person = item as Person;
return p.name + " " + p.surname;
}
]]>
</fx:Script>
<s:Label x="103" y="25" text="Esempio2" fontSize="21"/>
<s:HGroup x="28" y="57" verticalAlign="middle">
<s:Label text="Cerca un nome:"/>
<components:AutoComplete x="107" y="80"
dataProvider="{listPerson}"
labelFunction="makeNameSurnameLabel"
matchType="word"
showRemoveIcon="true"
dropDownItemRenderer="itemrenderer.PersonRenderer">
</components:AutoComplete>
</s:HGroup>
</s:Application>
Da notare l'utilizzo della proprietà showRemoveIcon
settata a true
: questo permette di visualizzare un pulsantino per rimuovere dal campo di testo l'elemento che viene di volta in volta selezionato. Abbiamo utilizzato “word” come valore della proprietà matchType
in modo da effettuare la ricerca considerando ogni parola della stringa rappresentativa dell'elemento della lista. Inoltre, è definita una labelFunction
che è la funzione che viene invocata per formattare la stringa da visualizzare quando viene selezionato un elemento e il cui valore di ritorno viene utilizzato per il filtro di ricerca del component.
Nota: per utilizzare l'elemento selezionato possiamo utilizzare la proprietà selectedItem
e selectedItems
nel caso ne abbiamo selezionati più di uno.
Utilizzi avanzati
Del component AutoComplete è stata sviluppata una versione avanzata che prende il nome di AdvancedAutoComplete e che, rispetto ad AutoComplete, offre alcune funzionalità aggiuntive.
Prima su tutte, la funzionalità di Browsing della lista di oggetti usata come dataprovider: la funzionalità di browsing permette di visualizzare un popup con un datagrid filtrabile da cui è possibile selezionare gli elementi del dataprovider. Il popup visualizzato è rappresentato in figura:
Per attivare la visualizzazione del popup per il browsing del dataprovider è necessario settare a true
la proprietà showBrowseButton
, che farà comparire alla destra del component AdvancedAutocomplete un pulsante con label “Browse”. In alternativa è possibile utilizzare un menu (definito in XML), più gradevole dal punto di vista estetico e che non comporta la necessità di riservare spazio per il pulsante 'browse', da cui poter aprire il popup.
Il menu XML va definito all'interno dei tag <fx:Declarations>:
<fx:XML format="e4x" id="autoCompleteMenuData">
<root>
<menuitem data="browse" label="Elenco..."/>
</root>
</fx:XML>
Tra le proprietà del component, invece, bisogna definire actionsMenuDataProvider
che deve essere impostata con il riferimento al menu XML. Nel datagrid che sarà visualizzato nel popup di browsing è possibile scegliere i campi da utilizzare come colonne con le relative labels. Per fare ciò utilizziamo la proprietà browserField
a cui passiamo un array di oggetti: per ogni oggetto dobbiamo definire i campi label e field che rappresentano rispettivamente il nome della colonna e il nome del campo degli oggetti del dataprovider che verrà mostrato nelle celle di quella determinata colonna.
Di seguito è mostrato il codice dell'applicazione che utilizza il component autocomplete avanzato, in modo da dare un'idea completa di come il component viene utilizzato:
<?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" width="100%" height="100%"
xmlns:components="com.hillelcoren.components.*"
xmlns:vo="vo.*">
<fx:Declarations>
<s:ArrayCollection id="listPerson">
[…] stesso dataprovider dell'esempio precedente [...]
</s:ArrayCollection>
<fx:XML format="e4x" id="autoCompleteMenuData">
<root>
<menuitem data="browse" label="Elenco..."/>
</root>
</fx:XML>
</fx:Declarations>
<fx:Script>
<![CDATA[
private function makeNameSurnameLabel(item:Object) : String {
var p:Person = item as Person;
return p.name + " " + p.surname;
}
]]>
</fx:Script>
<s:Label x="103" y="25" text="Esempio2" fontSize="21"/>
<s:HGroup x="28" y="57" verticalAlign="middle">
<s:Label text="Cerca un nome:"/>
<components:AdvancedAutoComplete id="advancedAutoComplete" x="107" y="80"
dataProvider="{listPerson}"
labelFunction="makeNameSurnameLabel"
matchType="word"
showRemoveIcon="true"
itemClick="{advancedAutoComplete.showBrowser()}"
actionsMenuDataProvider="{autoCompleteMenuData}"
browserFields="{[{label:'Nome', field:'name'},
{label:'Cognome',field:'surname'},
{label:'Sesso',field:'gender'}]}">
</components:AdvancedAutoComplete>
</s:HGroup>
</s:Application>
Tra le proprietà utilizzate, è importante itemClick
che viene utilizzata quando viene cliccata una voce del menu che abbiamo appena creato. Infatti, in questa proprietà viene definita l'azione da eseguire quando viene cliccato l'elemento browse del menu: nel nostro caso viene mostrato il popup di browse del dataprovider.
Ecco una demo.
È possibile scaricare il codice sorgente utilizzato nell'articolo.