Rich Internet Application with Adobe Flex 3
Agenda - 1 Sviluppare applicazioni con MXML
• Una semplice applicazione
Creare Flex Components
• Creare MXML Components • XML Namespace personalizzati
Forms, FormItems e Validators
• Gestire le Forms e i FormItem • Validare le forms con i Validator
Event Handling
Data Binding
• Gestire gli Eventi • Capture, targeting, bubbling • Listener, Dispatching, sottoclassi MouseEvent KeyBoardEvent
• • • •
Tipi di Binding Monitorare il Data Binding: mx.binding.utils.ChangeWatcher Usare il metadata tag [Bindable] Binding a funzioni collegate ad eventi
Agenda - 2
FileReference
• Caricare un file sul server con FileReference
Dataproviders e Collections
• Dataproviders e Dataobjects • ArrayCollection, XMLList, XMLListCollection • Sort e Filter
Datagrids, AdvancedDatagrids, TreeView Remote Procedure Call: le classi HTTPService e WebService
• Usare la DataGrid e AdvancedDatagrids • Usare le Treeview • Estendere le Datagrids: ItemRenderer e ItemEditor
• URLRequest e HTTPService • WebService • RemoteObject e RemoteClass
ActionScript 3 •I tag MXML servono per dichiarare: •Containers •Controls •Effects •Formatters •Validators •Web services • ecc ecc necessari al funzionamento dell’applicazione •MXML non è appropriato per descrivere il flusso applicativo alla pressione di un tasto da parte dell’utente •ActionScript è un linguaggio procedurale ad oggetti •ActionScript può essere mescolato a MXML in molti modi (es. event listener, tag <mx:Script>, file esterni)
Creare Flex Components
Creare MXML Component •Si definiscono sotto forma di file MXML e si utilizzano come custom tag in altri file •Incapsulano ed estendono i componenti già esistenti •Esempio: combobox custom pre-popolata <!-- mxml/containers/boxes/MyComboBox.mxml --> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:ComboBox > <mx:dataProvider> <mx:String>Dogs</mx:String> <mx:String>Cats</mx:String> <mx:String>Mice</mx:String> </mx:dataProvider> </mx:ComboBox> </mx:VBox>
<!-- mxml/CustomMXMLComponent.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComps="containers.boxes.*"> <mx:Panel title="My Application" paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10" > <MyComps:MyComboBox/> </mx:Panel> </mx:Application>
XML Namespace personalizzati •Permettono di utilizzare custom tags non definiti nel namespace MXML <?xml version="1.0"?> <!-- mxml/XMLNamespaces.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:MyComps="containers.boxes.*" > Può essere una subdirectory <mx:Panel title="My Application" dell’applicazione oppure una paddingTop="10" paddingBottom="10" subdirectory di quelle indicate nel file paddingLeft="10" flex-config.xml, con precedenza a paddingRight="10" quella dell’applicazione > <MyComps:CustomBox/> </mx:Panel> </mx:Application>
Forms, FormItems e Validators
Forms e FormItems • Gli oggetti Forms permettono di gestire comodamente una serie di campi label\input • L’oggetto Form contiene n oggetti FormItems che a loro volta contengono l’elemento di input specializzato dall’attributo label del FormItem <mx:Form id="form" x="10" y="10" width="319" height="90%"> <mx:FormItem label="Name:" id="lblName" required="true"> <mx:TextInput id="txtName"/> </mx:FormItem> <mx:FormItem label="Description:"> <mx:TextArea id="txtDescription"/> </mx:FormItem> <mx:FormItem label="Phone Number:" required="true"> <mx:TextInput id="txtNumber"/> </mx:FormItem> <mx:FormItem label="Date:"> <mx:DateChooser id="ctlCalendar" showToday="true"/> </mx:FormItem> <mx:FormItem> <mx:Button label="Save" id="btnSave" click="save()"/> </mx:FormItem> </mx:Form>
Validators • L’oggetto Validator permette di validare velocemente gli elementi di input della Application • La classe Validator è la base class per tutti le sottoclassi del package mx.validators • Esistono vari tipi di validators già pronti in Flex: es. CurrencyValidator, DateValidator, CreditCardValidator, EmailValidator • Si possono creare nuovi custom Validators estendendo la classe Validator <mx:Validator id="reqValidator" source="{txtName}" property="text" required="true" requiredFieldError="Questo campo è obbligatorio" /> <mx:PhoneNumberValidator id="phValidator" source="{txtNumber}" property="text" required="true" requiredFieldError="Il numero di telefono è sbagliato" />
Data Binding
Data Binding in Flex -1 3 tipi •MXML con la sintassi { }
Posso utilizzare funzioni ActionScript
<?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:TextInput id="myTI" text="Enter text here"/> <mx:Text id="myText" text="{myTI.text.toUpperCase()}"/> </mx:Application>
•MXML con il tag <mx:Binding> <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:TextInput id="myTI"/> <mx:Text id="myText"/> <mx:Text id="myText2"/> <mx:Binding source="myTI.text" destination="myText.text"/> <mx:Binding source="myTI.text" destination="myText2.text"/> </mx:Application>
•Separa la view dalla source •Possibilità di binding multiplo dalla stessa source
Data Binding in Flex - 2 â&#x20AC;˘ ActionScript con il metodo BindingUtils <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import mx.binding.utils.*; public function initBindingHandler():void { BindingUtils.bindProperty (myText, "text", myTI, "text"); } ]]> </mx:Script> <mx:TextInput id="myTI"/> <mx:Text id="myText" preinitialize="initBindingHandler();"/> </mx:Application>
Quando viene eseguito il Data Binding?
1. Quando la sorgente dati viene modificata
2. Sempre all’avvio dell’applicazione quando il controllo sorgente lancia l’evento initialize
Monitorare il Data Binding: mx.binding.utils.ChangeWatcher <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initWatcher();"> <mx:Script> <![CDATA[ import mx.binding.utils.*; import mx.events.FlexEvent; import mx.events.PropertyChangeEvent; public var myWatcher:ChangeWatcher; public function initWatcher():void { ChangeWatcher.watch(textarea, "text", watcherListener); } public function watcherListener(event:Event):void { myTA1.text="binding occurred"; } ]]> </mx:Script> <mx:TextInput id="textinput" text=â&#x20AC;&#x153;Textbox sorgente dati"/> <mx:TextArea id="textarea" text="{textinput.text}"/> <mx:TextArea id="myTA1"/> </mx:Application>
myWatcher.unwatch() rimuove il watcher
Usare il metadata tag [Bindable] Serve per rendere sorgente dati una property. Due sintassi: 1. [Bindable] 2. [Bindable(event="eventname")] Nel primo caso Flex crea automaticamente un evento propertyChange che verrĂ generato ogni qualvolta la property cambia valore per notificare i controlli bounded dellâ&#x20AC;&#x2122;avvenuto cambiamento. <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ [Bindable] public var maxFontSize:Number = 15; ]]> </mx:Script> <mx:Text text="{maxFontSize}"/> <mx:Button click="maxFontSize=20;"/> </mx:Application>
Binding a funzioni collegate ad eventi <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script> <![CDATA[ import flash.events.Event; [Bindable(event="myFlagChanged")] private function isEnabled():String { if (myFlag) return 'true'; else return 'false'; } private var _myFlag:Boolean = false; public function set myFlag(value:Boolean):void { _myFlag = value; dispatchEvent(new Event("myFlagChanged")); } public function get myFlag():Boolean { return _myFlag; } ]]> </mx:Script> <mx:TextArea id="myTA" text="{isEnabled()}"/> <mx:Button label="Clear MyFlag" click="myFlag=false;"/> <mx:Button label="Set MyFlag" click="myFlag=true;"/> </mx:Application>
Upload Files
Caricare un file sul server con FileReference • Per effettuare un upload di un file tramite il cliente Flex si usa la classe FileReference o FileReferenceList • Lato server deve essere presente un endpoint che gestisca la scrittura sul file system remoto • Il file da caricare viene inviato alla pagina lato server in post in una form multipart e l’upload viene gestito come un normale upload manuale Var fileRef:FileReference = new FileReference(); fileRef.addEventListener(Event.SELECT, fileSelected); fileRef.addEventListener(ProgressEvent.PROGRESS, notifyProgress); fileRef.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadComplete); var urlReq:URLRequest = new URLRequest(uploadURL); fileRef.upload(urlReq, uploadDataFieldName, false);
Event handling
Gestire gli eventi in Flex •Gli eventi di Flex sono basati sul W3 - DOM Level 3 •Interfacce base di DOM lev.3 •Event •CustomEvent •EventTarget •EventListener •EventException •EventExceptionCode
•Event types di DOM lev.3 •UIEvent •TextEvent •KeyboardEvent •MouseEvent •MouseMultiWheelEvent •MouseWheelEvent •MutationEvent •MutationNameEvent
Le tre fasi
Application 1 - Capture Panel
Title Window
3 Bubbling
Durante la fase di Capture, Flash Player valuta ogni elemento parent dellâ&#x20AC;&#x2122;oggetto per determinare se abbia un listener associato. Gli eventuali event listener trovati vengono eseguiti prima che lâ&#x20AC;&#x2122;evento raggiunga il target (button) Nella fase di Target, Flex invoca lâ&#x20AC;&#x2122;event handler senza valutare altri nodi della display list
2 - Target Button
Nella fase di Bubbling, viene ripetuta la valutazione effettuata durante il Capture ma in ordine inverso
La classe flash.events.Event •E’ una classe ActionScript con proprietà che descrivono l’evento generato •L’oggetto Event viene generato automaticamente ogni qual volta un evento viene gestito •Solo un oggetto Event viene generato durante le fasi di capturing e bubbling; Flex cambia il valore dell’oggetto Event muovendosi nella gerarchia della display list
Utilizzare gli eventi <?xml version="1.0"?> <!-- events/SimpleEventHandler.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();"> <mx:Script><![CDATA[ import mx.controls.Alert; private function initApp():void { b1.addEventListener(MouseEvent.CLICK, myEventHandler); } private function myEventHandler(event:Event):void { Alert.show("An event of type '" + event.type + "' occurred."); } ]]></mx:Script> <mx:Button id="b1" label="Click Me"/> </mx:Application>
Posso utilizzare l’oggetto Event all’interno dell’handler
Utilizzare gli eventi… senza addEventListener() <?xml version="1.0"?> <!-- events/SimplerEventHandler.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script><![CDATA[ import mx.controls.Alert; private function myEventHandler(event:Event):void { Alert.show("An event occurred."); } ]]></mx:Script> <mx:Button id="b1" label="Click Me" click="myEventHandler(event)"/> </mx:Application>
N.B. Con questa modalità non si può utilizzare la removeEventHandler
Il metodo addEventListener componentInstance.addEventListener( event_type:String, event_listener:Function, use_capture:Boolean, priority:int, weakRef:Boolean) event_type : può essere una stringa (“mouseOut”) oppure una costante (MouseEvent.MOUSE_OUT preferito) event_listener : è il nome della funzione che gestirà l’evento use_capture: se true, il listener sarà attivo durante la fase di capture priority: per specificare la priorità dell’event listener rispetto agli altri eventualmente definiti sull’evento weakRef: se false impedisce l’uso del garbage collector sull’event listener
Esempio di addEventListener multiplo con priorità <?xml version="1.0"?> <!-- events/CallingAddEventListenerInline.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script><![CDATA[ import mx.controls.Alert; private function myClickHandler(event:Event):void { Alert.show("PRIORITY 1: The button was clicked."); } private function myClickHandler2(event:Event):void { Alert.show("PRIORITY 2: The button was clicked."); }
Posso utilizzare addEventListener() da MXML usando la proprietà “initialize”
]]></mx:Script> <mx:Button id='b1' label="Click Me" initialize='b1.addEventListener(MouseEvent.CLICK, myClickHandler, false, 1); b1.addEventListener(MouseEvent.CLICK, myClickHandler2, false, 2);' /> </mx:Application>
Il metodo removeEventListener componentInstance.removeEventListener( event_type:String, listener_function:Function, use_capture:Boolean ) event_type, event_listener : come per la addEventListener() use_capture: per ascoltare gli eventi durante tutte le fasi occorre usare la addEventListener() due volte, una con use_capture=true, lâ&#x20AC;&#x2122;altra con use_capture=false. Pertanto, anche in fase di rimozione, andrĂ chiamata due volte la removeEventListener
Creare una classe per lâ&#x20AC;&#x2122;event handling package { // Empty package. import flash.events.Event; import mx.controls.Alert; public class MyEventHandler { public function MyEventHandler() { // Empty constructor. } public function handleAllEvents(event:Event):void { Alert.show("Some event happened."); } } } <?xml version="1.0"?> <!-- events/CustomHandler.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="createHandler()"> <mx:Script><![CDATA[ private var myListener:MyEventHandler = new MyEventHandler(); private function createHandler():void { b1.addEventListener(MouseEvent.CLICK, myListener.handleAllEvents); } ]]></mx:Script> <mx:Button label="Submit" id="b1"/> </mx:Application>
Singolo listener per pi첫 componenti <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="createHandlers(event)"> <mx:Script><![CDATA[ import mx.controls.Alert; public function createHandlers(e:Event):void { b1.addEventListener(MouseEvent.CLICK, submitForm); b2.addEventListener(MouseEvent.CLICK, submitForm); } private function submitForm(e:Event):void { // Handle event here. Alert.show("Current Target: " + e.currentTarget.id); } ]]></mx:Script> <mx:Button id="b1" label="Click Me"/> <mx:Button id="b2" label="Click Me, Too"/> </mx:Application>
Parametri addizionali negli event handler •La addEventListener accetta soltanto un parametro per cui dev’essere la listener_function a richiamare la funzione con più parametri • Usando la dichiarazione inline da MXML è possibile un numero a piacere di parametri <?xml version="1.0"?> <!-- events/MultipleHandlerParametersInline.mxml --> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script><![CDATA[ public function runMove(dir:String, e:Event):void { …… } ]]></mx:Script> <mx:Button id="b1" label="Up" click='runMove("up",event);' width="75" /> …… </mx:Application>
Dispatching manuale degli eventi <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="createListener(event);"> Occorre creare un <mx:Script><![CDATA[ nuovo oggetto Event import mx.controls.Alert; private function createListener(e:Event):void { b1.addEventListener(MouseEvent.MOUSE_OVER, myEventHandler); b1.addEventListener(MouseEvent.CLICK, myClickHandler); } private function myEventHandler(e:Event):void { var result:Boolean = b1.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true,false)); } private function myClickHandler(e:Event):void { Alert.show("The event dispatched by the MOUSE_OVER was of type '" + e.type + '."); } ]]></mx:Script> <mx:Button id="b1" label="Click Me"/> </mx:Application>
Si può usare anche l’inline MXML <mx:Button id="b1" label="Click Me" mouseOver="b1.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false));" />
Trasformare un click in un doubleClick con lo Shift premuto <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="addListeners()"> <mx:Script><![CDATA[ private function customLogEvent(e:MouseEvent):void { l1.text = String(e.currentTarget.id); l2.text = String(e.type); l3.text = String(e.shiftKey); // Remove current listener to avoid recursion. e.currentTarget.removeEventListener("doubleClick", customLogEvent); } private function handleEvent(e:MouseEvent):void { // Add new handler for custom event about to be dispatched. e.currentTarget.addEventListener("doubleClick", customLogEvent); // Create new event object. var mev:MouseEvent = new MouseEvent("doubleClick"); // Customize event object. mev.shiftKey = true; // Dispatch custom event. e.currentTarget.dispatchEvent(mev); } private function addListeners():void { b1.addEventListener("click",handleEvent); b2.addEventListener("click",handleEvent); } ]]></mx:Script>
Oggetto Event: proprietà currentTarget e target currentTarget contiene il nodo corrente nella lista degli event handler target si riferisce sempre al dispatcher dell’evento (non necessariamente l’oggetto a cui è associato un event listener, es. un suo subcomponente come l’UITextField di un Button)
Oggetto Event: identificare la fase dell’evento •La proprietà eventPhase dell’oggetto Event contiene la fase corrente e può valere: •1 — Capturing phase (CAPTURING_PHASE) •2 — Targeting phase (AT_TARGET) •3 — Bubbling phase (BUBBLING_PHASE)
•Un esempio di come identificare la fase all’interno dell’event handler: <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script><![CDATA[ import mx.controls.Alert; private function showInfo(e:MouseEvent):void { Alert.show("Phase: " + e.eventPhase + "\n" +"Current Target: " + e.currentTarget.id); } ]]></mx:Script> <mx:Button id="b1" label="Click Me" click="showInfo(event)" /> </mx:Application>
Oggetto Event: fermare la propagazione •Durante ogni fase è possibile fermare la propagazione dell’evento utilizzando i seguenti metodi dell’oggetto Event: •stopPropagation (non valuta il prossimo nodo ma esegue gli event handler trovati per il nodo corrente) •stopImmediatePropagation (non valuta il prossimo nodo e NON esegue gli altri event handler)
Oggetto Event: le sottoclassi MouseEvent e KeyboardEvent •Usando la sottoclasse opportuna per un oggetto, si evita di dover eseguire il cast dell’argomento Event dell’event handler •Le sottoclassi espongono delle proprietà più specifiche •Si può definire un handler globale per gli eventi Keyboard <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();"> <mx:Script><![CDATA[ private function initApp():void { application.addEventListener(KeyboardEvent.KEY_UP, keyHandler); } private function keyHandler(event:KeyboardEvent):void { t1.text = event.keyCode + "/" + event.charCode; } ]]></mx:Script> <mx:TextInput id="myTextInput"/> <mx:Text id="t1"/> </mx:Application>
MouseEvent e KeyboardEvent: qualche nota •La classe KeyboardEvent espone le proprietà keyCode (codice numerico del tasto) e charCode (codice carattere del tasto, relativo al character set) •Definendo un handler per il keyUp e il keyDown sia per un controllo che per il suo container, l’evento keyboard sarà generato per entrambi a causa del bubbling. La proprietà target dell’evento rimarrà costante mentre varierà la currentTarget in base al nodo esaminato dalla fase di bubbling •La classe MouseEvent (e le sue sottoclassi) espongono le seguenti proprietà che permettono di determinare l’eventuale pressione dei tasti speciali durante l’evento del mouse: •altKey •ctrlKey •shiftKey
Data providers e Collections
Le Collection • Le Collections sono oggetti che uniformano l’accesso e la definizione dei dati contenuti negli oggetti data source come Array e XMLList • Le Collections creano un livello di astrazione tra i componenti Flex e i dati che li popolano • Garantiscono l’update del controllo in caso di cambiamento dei dati sottostanti • Forniscono meccanismi di paging per dati che richiedano tempo per il download • Permettono di filtrare ed ordinare i dati sottostanti • Utilizzano le seguenti interfacce: •IList (basata sull’indice con cui sono inseriti fisicamente gli oggetti nella collezione) •ICollectionView (permette sort e filter; espone un oggetto IViewCursor che permette di scorrere bidirezionalmente la collezione, di effettuare ricerche, salvare bookmark e rimuovere/inserire item)
Il package mx.collections Classe
Descrizione
ArrayCollection
Collezione standard per lavorare con gli array; implementa sia IList che ICollectionView
XMLListCollection
Collezione per gli oggetti XMLList; implementa sia IList che ICollectionView oltre ad un subset dei metodi di XMLList
CursorBookmark
Rappresenta la posizione di un view cursor all’interno della collezione
Sort / SortField
Necessarie per l’ordinamento
ItemResponder
Gestisce i data source remoti
ListCollectionView
Classe da cui derivano ArrayCollection e XMLListCollection
Grouping
Definisce i campi da utilizzare per raggruppare i dati e visualizzarli nella AdvancedDataGrid
GroupingCollection
Permette di creare dati raggruppati a partire da dati flat
GroupingField
Definisce il singolo campo di raggruppamento
HierarchicalCollectionView
Espone una vista gerarchica a partire da una collezione standard
HierarchicalCollectionViewCursor
Permette di definire un cursore sulla vista gerarchica
HierarchicalData
Contiene dati già strutturati gerarchicamente nella forma parent-child
SummaryRow
Rappresenta la riga “summary” dell’AdvancedDataGrid
SummaryField
Rappresenta il singolo campo della SummaryRow
SummaryObject
Mantiene le informazioni “summary” per dei dati raggruppati
Data objects •Espongono la proprietà dataProvider •ButtonBar •ColorPicker •ComboBox •DataGrid •DateField •HorizontalList •LinkBar •List •Repeater •TabBar •TileList •ToggleButtonBar •Tree •Menu •MenuBar •PopUpMenuButton
List-based data objects
Hierarchical data objects
•E’ possibile utilizzare array di stringhe o oggetti come provider ma è preferibile utilizzare le collections (ArrayCollection, XMLListCollection, custom collection) perchè garantiscono la notifica di cambiamenti nei dati e permettono il sorting e il filtering
ArrayCollection <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:ComboBox id="myCB"> <mx:ArrayCollection id="stateArray"> <mx:Object label="AL" data="Montgomery"/> <mx:Object label="AK" data="Juneau"/> <mx:Object label="AR" data="Little Rock"/> </mx:ArrayCollection> </mx:ComboBox> </mx:Application> Sfrutto le default property dataProvider dell’oggetto ComboBox e source dell’oggetto ArrayCollection
ArrayCollection con ActionScript (inline MXML) <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initData()"> <mx:Script> <![CDATA[ import mx.collections.*; [Bindable] public var stateArray:ArrayCollection; public function initData():void { stateArray=new ArrayCollection( [{label:"AL", data:"Montgomery"}, {label:"AK", data:"Juneau"}, {label:"AR", data:"Little Rock"}]); } ]]> </mx:Script> <mx:ComboBox id="myComboBox" dataProvider="{stateArray}"/> <mx:Button label="Add AZâ&#x20AC;&#x153; click="stateArray.addItem({'label':'AZ', 'data':'Phoenix'});"/> </mx:Application>
ArrayCollection con ActionScript <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initData();"> <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; [Bindable] private var DGArray:ArrayCollection = new ArrayCollection([ {Artist:'Pavement', Album:'Slanted and Enchanted', Price:11.99}, {Artist:'Pavement', Album:'Brighten the Corners', Price:11.99}]); // Initialize initDG ArrayCollection variable from the ArrayCollection. public function initData():void { myGrid.dataProvider = DGArray; validateNow() serve per sincronizzare myGrid.validateNow(); tutte le proprietà dell’oggetto, compreso il myGrid.selectedIndex=1; } databind, evitando che la selectedIndex ]]> vada in errore. </mx:Script> Attenzione alle performance! <mx:DataGrid id="myGrid"> <mx:columns> <mx:DataGridColumn dataField="Album"/> <mx:DataGridColumn dataField="Price"/> </mx:columns> </mx:DataGrid> </mx:Application>
Ordinamento di unâ&#x20AC;&#x2122;ArrayCollection <!-- dpcontrols\SimpleDP.mxml --> initialize="sortAC()"> <mx:Script> <![CDATA[ import mx.collections.*; public function sortAC():void { var sortA:Sort = new Sort(); sortA.fields=[new SortField("label")]; myAC.sort=sortA; myAC.refresh(); } public function addItemToMyAC():void { myAC.addItem({label:"MD", data:"Annapolis"}); } ]]> </mx:Script> <mx:ArrayCollection id="myAC"> <mx:Array id="myArray"> <mx:Object label="MI" data="Lansing"/> <mx:Object label="MO" data="Jefferson City"/> <mx:Object label="MA" data="Boston"/> <mx:Object label="MT" data="Helena"/> <mx:Object label="ME" data="Augusta"/> <mx:Object label="MS" data="Jackson"/> <mx:Object label="MN" data="Saint Paul"/> </mx:Array> </mx:ArrayCollection>
Filtrare unâ&#x20AC;&#x2122;ArrayCollection /* Function to filter out all items with labels that are not in the range of M-N. */ public function stateFilterFunc(item:Object):Boolean { return item.label >= "M" && item.label < "O"; } /* Function to apply the filter function the ICollectionView. */ public function filterAC():void { myAC.filterFunction=stateFilterFunc; /* Refresh the collection view to apply the filter. */ myAC.refresh(); } /* Function to Reset the view to its original state. */ public function resetAC():void { myAC.filterFunction=null; myAC.sort=null; myAC.refresh(); }
Cursori Le Collections espongono un cursore tramite il metodo createCursor() public var myAC:ICollectionView = new ArrayCollection(myArray); public var myCursor:IViewCursor; … myCursor=myAC.createCursor();
Proprietà e metodi: moveNext() e movePrevious() per spostarsi tra i record while (! myCursor.afterLast) { myCursor.moveNext(); }
findAny(), findFirst(), findLast() per cercare un item myCursor.findFirst({label:"ME", data:"Augusta"});
Aggiungere, eliminare o modificare items usando i cursori -1 <mx:Script> <![CDATA[ import mx.collections.*; public var myArray:Array = ["AZ", "MA", "MZ", "MN", "MO", "MS"]; [Bindable] public var myAC:ArrayCollection; public function initData():void { myAC = new ArrayCollection(myArray); }
Cancello il primo elemento
public function changeCollection():void { var myCursor:IViewCursor=myAC.createCursor(); var removedItem:String=String(myCursor.remove()); myCursor.moveNext(); myCursor.insert("ME"); myCursor.seek(CursorBookmark.LAST, 1); myCursor.insert("MT");
Aggiungo ME come 2°elemento
Uso la funzione seek con il parametro CursorBookmark.LAST e un offset=1 per andare aggiungere ‘MT’ come ultimo elemento
Aggiungere, eliminare o modificare items usando i cursori -2 â&#x20AC;Ś. // Change MZ to MI. var sort:Sort = new Sort(); myAC.sort=sort; myAC.refresh();
La findFirst() successiva richiede che la collezione sia ordinata
if (myCursor.findFirst("MZ") && !myCursor.findFirst("MI")) { myCursor.remove(); myCursor.insert("MI"); } }
La collezione non prevede un metodo Replace
Hierarchical data objects •Menu •MenuBar •PopUpMenuButton •Tree Flex gestisce 2 tipologie di dati gerarchici: 1. XML sotto forma di stringhe o oggetti XML, XMLList, o XMLListCollection) 2. Oggetti in cui i nodi figli si trovano nel campo children [Bindable] public var fileSystemStructure:Object = {label:"mx", children: [ {label:"Containers", children: [ {label:"Accordian", children:[]}, {label:"DividedBox", children: [ {label:"BoxDivider.as", data:"BoxDivider.as"}, {label:"BoxUniter.as", data:"BoxUniter.as"}]}, {label: "Grid", children:[]}]}, {label: "Controls", children: [ {label: "Alert", data: "Alert.as"}, {label: "Styles", children: [ {label: "AlertForm.as", data:"AlertForm.as"}]}, {label: "Tree", data: "Tree.as"}, {label: "Button", data: "Button.as"}]}, {label: "Core", children:[]} ]};
Data descriptors Sono classi che interfacciano i controlli gerarchici con i data provider. Flex espone 2 interfacce Data Descriptor: â&#x20AC;˘ITreeDataDescriptor utilizzato dai controlli Tree â&#x20AC;˘IMenuDataDescriptor utilizzato dai controlli Menu, MenuBar, e PopUpMenuButton La classe DefaultDataDescriptor implementa entrambe le interfacce.
Data descriptors – proprietà e metodi Method hasChildren(node, [model])
getChildren(node, [collection]) isBranch(node, [collection])
Returns A Boolean value indicating whether the node is a branch with children. A node’s children.
DefaultDataDescriptor behavior For XML, returns true if the node has at least one child element. For other objects, returns true if the node has a nonempty children field.
Whether a node is a branch.
For XML, returns an XMLListCollection with the child elements. For other Objects, returns the contents of the node’s children field. For XML, returns true if the node has at least one child, or if it has an isBranch attribute. For other Objects, returns true if the node has an isBranch field.
getData(node, [collection])
The node data.
Returns the node.
addChildAt(node, child, index, [model])
A Boolean value indicating whether the operation succeeded.
For all cases, inserts the node as a child object before the node currently in the index location.
removeChildAt(node, index, [model])
A Boolean value indicating whether the operation succeeded.
For all cases, removes the child of the node in the index location.
getType(node) (IMenuDataDescriptor only)
A String with the menu node type. Meaningful values are check, radio, and separator. A Boolean value indicating whether a menu node is enabled.
For XML, returns the value of the type attribute of the node. For other Objects, returns the contents of the node’s type field.
isEnabled(node) (IMenuDataDescriptor only) setEnabled(node, value) (IMenuDataDescriptor only) isToggled(node) (IMenuDataDescriptor only)
For XML, sets the value of the enabled attribute of the node to true or false. For other Objects, sets the contents of the node’s enabled field. A Boolean value indicating whether a menu node is selected
setToggled(node, value) (IMenuDataDescriptor only) getGroupName(node) (IMenuDataDescriptor only)
For XML, returns the value of the enabled attribute of the node. For other Objects, returns the contents of the node’s enabled field.
Returns the value of the node’s toggled attribute.
For XML, sets the value of the selected attribute of the node to true or false. For other Objects, sets the contents of the node’s enabled field. The name of the radio button group to which the node belongs.
For XML, returns the value of the groupName attribute of the node. For other Objects, returns the contents of the node’s groupName field.
Il tag <mx:Model> Consente la definizione di un data provider in MXML Presenta i seguenti vantaggi: • La struttura è più leggibile perchè in formato XML • Le strutture sono collegabili a variabili ActionScript e quindi si può usare <mx:Model> per creare un data provider che mostri dati dinamici
Il tag <mx:Model> - Esempio - 1 <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"> <mx:Script> <![CDATA[ import mx.controls.Menu; public var productMenu:Menu; public function initMenu(): void { productMenu = Menu.createMenu(null, Products.Department); productMenu.setStyle("disabledColor", 0xCC3366); productMenu.show(10,10); } ]]> </mx:Script>
Il tag <mx:Model> - Esempio - 2 <mx:Model id="Products"> <Root> <Department label="Toys"> <children label="Teddy Bears"/> <children label="Action Figures"/> <children label="Building Blocks"/> </Department> <Department label="Kitchen"> <children label="Electronics"> <children label="Crock Pot"/> <children label="Panini Grill"/> </children> <children label="Cookware"> <children label="Grill Pan"/> <children label="Iron Skillet" enabled="false"/> </children> </Department> <Department label="{menuName.text}"> Item collegati ad <children label="{item1.text}"/> oggetti della form <children label="{item2.text}"/> <children label="{item3.text}"/> </Department> </Root> </mx:Model>
Il tag <mx:Model> - Esempio - 3 <mx:Button label="Show Products" click="initMenu()"/> <mx:Form> <mx:FormItem label="Third Submenu title"> <mx:TextInput id="menuName" text="Clothing"/> </mx:FormItem> <mx:FormItem label="Item 1"> <mx:TextInput id="item1" text="Sweaters"/> </mx:FormItem> <mx:FormItem label="Item 2"> <mx:TextInput id="item2" text="Shoes"/> </mx:FormItem> <mx:FormItem label="Item 3"> <mx:TextInput id="item3" text="Jackets"/> </mx:FormItem> </mx:Form> </mx:Application>
Data objects basati su XML Utilizzare i tag <mx:XML> o <mx:XMLList> per definire gli oggetti XML/XMLList in MXML Eâ&#x20AC;&#x2122; sempre possibile collegare i valori dei nodi ad oggetti dellâ&#x20AC;&#x2122;applicazione; ad esempio: <mx:XMLList id="myXMLList"> <child name="{textInput1.text}"/> <child name="{textInput2.text}"/> </mx:XMLList>
Se i dati devono cambiare, occorre prima convertire gli oggetti XML o XMLList in un XMLListCollection ed effettuare le modifiche su di esso <mx:XML id="capitals"> <root> <Capitals label="U.S. State Capitals"> <capital label="AL" value="Montgomery"/> <capital label="AK" value="Juneau"/> </Capitals> <Capitals label="Canadian Province Capitals"> <capital label="AB" value="Edmonton"/> <capital label="BC" value="Victoria"/> </Capitals> </root> </mx:XML> <mx:XMLListCollection id="capitalColl" source="{capitals.Capitals}"/>
XMLListCollection -1 Espone le funzionalitĂ delle collection per la classe XMLList <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import mx.collections.XMLListCollection; import mx.collections.ArrayCollection; [Bindable] public var myData:XML= <catalog> <category name="Meat"> <product name="Buffalo"/> <product name="T Bone Steak"/> </category> <category name="Vegetables"> <product name="Broccoli"/> <product name="Yellow Peppers"/> </category> <category name="Fruit"> <product name="Bananas"/> <product name="Grapes"/> <product name="Strawberries"/> </category> </catalog>;
XMLListCollection -2 [Bindable] public var listDP:XMLListCollection = new XMLListCollection(new XMLList()); private function doTreeSelect():void { if (prodTree.selectedItem) listDP.addItem(prodTree.selectedItem.copy()); } private function doListRemove():void { if (prodList.selectedItem) listDP.removeItemAt(prodList.selectedIndex); } ]]> </mx:Script> <mx:HBox> <mx:Tree id="prodTree" dataProvider="{myData}" width="200" showRoot="false" labelField="@name"/> <mx:VBox> <mx:Button id="treeSelect" label="Add to List“ click="doTreeSelect()"/> <mx:Button id="listRemove" label="Remove from List“ click="doListRemove()"/> </mx:VBox> <mx:List id="prodList" dataProvider="{listDP}" width="200“ labelField="@name"/> </mx:HBox> </mx:Application>
ArrayCollection da RemoteObject <?xml version="1.0"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" verticalGap="10"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.utils.ArrayUtil; ]]> </mx:Script> <mx:RemoteObject id="employeeRO" destination="roDest" showBusyCursor="true" fault="Alert.show(event.fault.faultString, 'Error');"> <mx:method name="getList"> <mx:arguments> <deptId>{dept.selectedItem.data}</deptId> </mx:arguments> </mx:method> </mx:RemoteObject> <mx:ArrayCollection id="employeeAC” source="{ArrayUtil.toArray(employeeRO.getList.lastResult)}"/> … <mx:DataGrid dataProvider="{employeeAC}" width="100%"> <mx:columns> <mx:DataGridColumn dataField="name" headerText="Name"/> <mx:DataGridColumn dataField="phone" headerText="Phone"/> <mx:DataGridColumn dataField="email" headerText="Email"/> </mx:columns> </mx:DataGrid>
Datagrids, AdvancedDatagrids ItemRenderer e ItemEditor
Differenze fra Datagrid e AdvancedDatagrid • Utilizzo delle GroupedCollection • Integrazione dell’oggetto TreeView e utilizzo delle HierarchicalCollection • Multicolumn sorting <mx:XML source="../assets/xml/sample.xml" id="xmlSource" /> <mx:XMLListCollection id="products" source="{xmlSource.Product}" /> <mx:AdvancedDataGrid editable="true" x="10" y="10" id="adg1" designViewDataType="flat" width="478" height="289" dataProvider="{products}"> <mx:columns> <mx:AdvancedDataGridColumn headerText="ID" dataField="ID" editable="false" /> <mx:AdvancedDataGridColumn headerText="Name" dataField="Name" editable="false" /> <mx:AdvancedDataGridColumn headerText="Price" dataField="Price"> </mx:column> </mx:AdvancedDataGrid>
ItemRenderer • Per personalizzare la renderizzazione di una cella di una Datagrid o di una AdvancedDataGrid è necessario utilizzare la classe ItemRender • Per default, la cella viene visualizzata come Label senza formattazione • E’ possibile creare un custom ItemRenderer estendendo la classe di riferimento <mx:AdvancedDataGridColumn headerText="Price" dataField="Price"> <mx:itemRenderer> <mx:Component> <components:TestRenderer></components:TestRenderer> </mx:Component> </mx:itemRenderer> </mx:AdvancedDataGridColumn> public class TestRenderer extends Label E sovrascrivendo il setter della proprietà data: nell’esempio seguente la label apparirà rossa se il valore di Price sarà compreso fra 100 e 150 override public function set data(value:Object):void { super.data = value; if (Number(value.Price) >= 100 && Number(value.Price) <= 150) this.setStyle("color", "red"); }
ItemEditor • Datagrid e AdvancedDataGrids danno la possibilità di modificare il dataProvider direttamente dalle celle • Per default, l’ItemEditor usato è un TextInput • E’ possibile estendere l’ItemEditor inserendo logica custom o creare un classe apposita • Le DataGridColumn e le AdvancedDatagridColumn sono editabili di default • L’editorDataField può essere diverso dal dataField <mx:itemEditor> <mx:Component> <mx:VBox backgroundColor="Yellow"> <mx:Script> <![CDATA[ [Bindable] public var cbSelected:Boolean; ]]> </mx:Script> <mx:CheckBox label="Sell this product?" id="cbIsSeleable" selected="{data.Seleable}" click="cbSelected=cbIsSeleable.selected" /> </mx:VBox> </mx:Component> </mx:itemEditor>
Remote Procedure Call: le classi HTTPService, WebService, RemoteObject
Tipologie di connessione • REST style web service (es. Pagina PHP, JSP o servlet, o pagina ColdFusion) tramite HTTPService component • SOAP Web services tramite il WebService component • Adobe Action Message Format (AMF) remoting services tramite RemoteObject component; più veloce perchè non occorre formattazione XML dato che accede direttamente all’oggetto Java o al componente ColdFusion o tramite WebORB per .NET)
HTTPService •La classe HTTPService permette di inviare le richieste HTTP GET, POST, HEAD, OPTIONS, PUT, TRACE, and DELETE ed elaborare le risposte nell’applicazione Flex. •I multpart form POSTs non sono supportati •In questo esempio viene richiamata una pagina PHP che richiede 2 parametri: <?xml version="1.0" encoding="utf-8"?> <mx:Application> ... <mx:HTTPService id="userRequest" url="http://server/myproj/request_post2.php" useProxy="false" method="POST"> <mx:request xmlns=""> <username>{username.text}</username> <emailaddress>{emailaddress.text}</emailaddress> </mx:request> </mx:HTTPService> </mx:Application>
WebService • La classe WebService permette di effettuare richieste SOAP in POST verso l’endpoint specificato dal WSDL del webservice stesso e gestire gli oggetti serializzati nella risposta • In questo esempio vengono richiamati due metodi del webservice definito dalla proprietà wsdl: <mx:Application> ... <mx:WebService id="userRequest“ wsdl="http://server:8500/flexapp/returncfxml.cfc?wsdl"> <mx:operation name="returnRecords" resultFormat="object" fault="mx.controls.Alert.show(event.fault.faultString)" result="remotingCFCHandler(event)"/> <mx:operation name="insertRecord" result="insertCFCHandler()“ fault="mx.controls.Alert.show(event.fault.faultString)"/> </mx:WebService> </mx:Application>
AMF (Action Message Format) • Il canale di comunicazione AMF permette di effettuare richieste asincrone verso un endpoint JAVA , ColdFusion, PHP, .NET e avere in risposta un oggetto interrogabile tramite le sue proprietà. • AMF è messo a dispozione da webapps come BlazeDS, DataServices di Adobe LiveCycle, WEBOrb .NET (anche per Rails e PHP) • Differenze fra BlazeDS e DataServices: • No PDF Generator • No RTMP (Real Time Messaging Protocol) • No offline datas
RemoteObject – 1 • RemoteObject è la classe che si occupa delle richieste remote in AMF • La proprietà principale della classe RemoteObject è destination • La configurazione dei canali AMF e delle destination è specificata nel file services-config.xml • Il file services-config.xml deve essere incluso in compilazione del Flex dalla direttiva di compilazione –services -services "C:\java\webserver\tomcat\webapps\samples\WEB-INF\flex\servicesconfig.xml“ Contenuto di services-config.xml <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/> </channel-definition> <default-channels> <channel ref="my-amf"/> </default-channels> <destination id="product"> <properties> <source>flex.samples.product.ProductService</source></properties></destination>
Chiamare i metodi remoti • Nel services-config la classe remota ProductService è associata alla destination product • Per invocare i metodi remoti di ProductService è necessario utilizzare la classe ActionScript RemoteObject instanziata con il parametro stringa che rappresenta il nome della destination da utilizzare (quindi della classe JAVA remota) var ro:RemoteObject = new RemoteObject(“product”); L’oggetto “ro” ora rappresenta la classe JAVA remota ProductService esposta tramite BlazeDS • La classe remota ProductService espone il metodo getProductsByName ed ha come parametro il nome del prodotto da ricercare • ro.getProductsByName effettua una chiamata verso la classe remota • Lato JAVA il metodo getProductsByName restituisce una List di oggetti Product che viene serializzata in Flex in un oggetto ArrayCollection di Product
Esempi di chiamate a metodi remoti - In MXML <mx:RemoteObject id="srv" destination="product"> <mx:method name=“getProductsByName" result=“getResult(event)"/> </mx:RemoteObject> srv.getProductsByName(“nokia e51”); private function getResult(event:ResultEvent):void { var products:ArrayCollection = event.result as ArrayCollection; } - In ActionScript var ro:RemoteObject = new RemoteObject("product"); ro.addEventListener(ResultEvent.RESULT, getResult); ro.getProductsByName(“nokia e51”); private function getResult(e:ResultEvent):void { var products:ArrayCollection = event.result as ArrayCollection; }
Gestire gli eventi di Result e Fault • La classe RemoteObject effettua chiamate asincrone verso l’endpoint JAVA • E’ necessario gestire gli eventi di Result (dati ricevuti) e Fault (errore durante la richiesta) • E’ possibile associate l’event listener in modo generalizzato a tutte le chiamate del RemoteObject o ad ogni singolo metodo se è necessario gestire la risposta in modo particolare • Ogni metodo remoto rappresenta una classe ActionScript Operation ed implementa EventDispatcher var ro:RemoteObject = new RemoteObject(“product”); ro.addEventListener(ResultEvent.RESULT, getResult); ro.addEventListener(FaultEvent.FAULT, onFault); Oppure specificando result e fault per ogni metodo: ro.getProductsByName.addEventListener(ResultEvent.RESULT, getResult); ro.getProductsByName.addEventListener(FaultEvent.FAULT, onFault); Private function getResult(e:ResultEvent):void { } Private function onFault(e:FaultEvent):void { }
ResultEvent e FaultEvent • ResultEvent e FaultEvent estendono la classe generica Event • ResultEvent è usato per gestire l’oggetto ricevuto dalla chiamata remota • FaultEvent è usato per gestire l’errore durante la richiesta e visualizzare il messaggio di errore relativo • L’oggetto ricevuto è contenuto nella proprietà result di ResultEvent ed è di tipo object (classe generica) e deve essere convertito alla classe di riferimento per l’utilizzo in ActionScript • L’errore è contenuto nella proprietà fault (tipo oggetto Fault) di FaultEvent private function getResult(e:ResultEvent):void { this.lastResult = e.result as ArrayCollection; } private function onFault(e:FaultEvent):void { Alert.show(e.fault.message); }
Notificare la ricezione dei dati • Se si utilizza una classe ActionScript che gestisce le chiamate tramite RemoteObject è necessario notificare all’applicazione o alle classi chiamanti l’effettiva ricezione dei dati • Tale classe deve implementare EventDispatcher • Accedere ai dati associati a RemoteObject prima della effettiva ricezione risulta in una nullpointer execption • Nell’event listener associato al ResultEvent è possibile effettuare un dispatch forzato di un evento custom che provveda al trattamento dei dati lato applicazione
Metadata tag [RemoteClass] • Lato flex è necessario creare le classi ActionScript che rappresentano gli oggetti che vengono seralizzati\deserializzati sul canale AMF • Nella classe ActionScript si specifica il metadata tag RemoteClass seguito dalla classe emota di riferimento
RemoteClass(alias="flex.samples.product.Product")] public class Product { } e lato JAVA package flex.samples.product; public class Product implements Serializable { ….
Le classi remote devono implementare le interfacce per la serializzazione come Serializable o ISerializable (java.io)
Risorse • Reply : http://www.reply.it • Flex : http://www.flex.org • AIR : http://www.adobe.com/products/air/ • Flex ShowCase : http://flex.org/showcase/ • LiveDocs Flex 3: http://livedocs.adobe.com/flex/3/html/ • LiveDocs AS3 : http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/ • BlazeDS : http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/ • LiveCycle : http://www.adobe.com/products/livecycle/ • WebORB : http://www.themidnightcoders.com/products.html • Flex Developers .it : http://flex.actionscript.it/index.php?title=Pagina_principale
â&#x20AC;˘ Francesco Bramato @ Aktive Reply â&#x20AC;&#x201C; f.bramato@reply.it