<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:custom="components.*"
creationComplete="initFlexGridTest()"
backgroundColor="#EEEEEE"
paddingTop="0" paddingLeft="0" paddingRight="0" paddingBottom="0"
backgroundAlpha="0"
frameRate="31" >
<mx:Script source="fgt.as" />
<mx:Style source="components/datagrid.css" />
<mx:Panel id="mainPanel" title="DataGrid test" height="100%" width="100%"
paddingTop="2" paddingLeft="2" paddingRight="2" paddingBottom="2" >
<mx:DataGrid id="dg" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="id" visible="false" />
<mx:DataGridColumn headerText="name" dataField="sName" />
<mx:DataGridColumn headerText="surname" dataField="sSurname" />
<mx:DataGridColumn headerText="role" dataField="eRole" />
<mx:DataGridColumn headerText="options"
textAlign="center"
resizable="false"
itemRenderer="components.IRRowOptions" width="100" sortable="false" />
</mx:columns>
</mx:DataGrid>
<mx:ControlBar width="100%">
<mx:Spacer width="100%"/>
<mx:Button id="btReloadItems" label="reload" />
<mx:Button id="btAddItem" label="add item" />
</mx:ControlBar>
</mx:Panel>
</mx:Application>
le applicazioni in flex vengono costruite a partire da particolari file xml, la loro
estensione e' .mxml, partono con un header che contiene le caratteristiche di massima,
seguono gli elementi che costituiscono l'interfaccia, ognuno dei quali puo' disporre di alcuni attributi
come la posizione, le dimensioni, aspetti grafici e codice actionscript.creationComplete="initFlexGridTest()"questo e' il punto di ingresso dell'applicazione, a partire dalla function in questione andremo a scriptare tutto il resto, il codice e' specificato dal nodo mx:script,
<mx:Script source="fgt.as" />Da notare l'associazione all'evento creationComplete, in tal modo tutto cio' che imporremo sara' eseguito solo ad interfaccia inizializzata, ovvero dopo il preload e la costruzione degli elementi visuali nel browser.
si e' scelto di separare il codice flex dall'actionscript per diverse ragioni, personalmente lo ritengo un approccio piu' razionale, dall'altro posso usare un editor AS3 coi benefici derivanti (tag coloring, code hint e quant'altro), in circolazione ci sono diversi editor ottimi e liberi, per elencare alcuni: flashdevelop, se|py ed eclipse integrato con alcuni plug-in.
Prima di continuare diamo un'occhiata a come gestire questo progetto, e' composto di diversi file sorgente e contributi vari e della sezione di pubblicazione, ecco la disposizione che di solito prediligo in eclipse:
nell'immagine si vede gia' una buona parte dei file che eventualmente andremo a creare (il codice sorgente di tutto il progetto e'
qui), le righe evidenziate corrispondono alle cartelle
che andremo a creare da subito, src conterra' i nostri file sorgente, deploy il risultato della compilazione, le sottodirectory
lib e misc conterranno rispettivamente del codice php di supporto e css/js.
La cartella src avra' anch'essa delle sottodirectory: prj e components, le vedremo piu' avanti.
/**
* @description user manager application code
* this file is intended only for inclusion
* jaco_at_pixeldump
*/
import mx.core.*;
...
import prj.*;
import prj.jaco.amf.*;
private var phpGateway:String = "http://p4nb/flashservices/gateway.php";
private var gw:RemotingConnection;
private var pop:ItemEditor;
/**
* @description ENTRY POINT
*/
private function initFlexGridTest():void{
gw = new RemotingConnection(phpGateway);
setup_button_events();
reload_items(null);
}
/**
* @description open a pop-up for adding/editing item
*/
public function item_view(itemData:Object):void {
pop = ItemEditor(PopUpManager.createPopUp(this, ItemEditor, true));
pop.set_itemData(itemData);
PopUpManager.centerPopUp(pop);
pop.btSave.addEventListener(MouseEvent.CLICK, onIESaveClick);
pop.btClose.addEventListener(MouseEvent.CLICK, onIECloseClick);
}
/**
* @event
* @description trigger when server send response on news item delete request
*/
private function onIESaveClick(evt:MouseEvent):void {
var itemData:Object = pop.get_itemData();
onIECloseClick(null);
gw.call("ItemsManager.save_update_item", new Responder(onItemsResult, onFault), itemData);
}
/**
* @event
* @description
*/
public function onItemsResult( result:Object ) : void {
dg.dataProvider = result;
var columns:Array = dg.columns;
columns[0].visible = false;
dg.columns = columns;
}
/**
* @event
* @description
*/
public function onFault( fault : String ) : void {
Alert.show("sorry there was some error talking to server");
}
/**
* @event
* @description trigger when user confirm to delete news
*/
public function request_item_delete(evt:CloseEvent):void {
if (evt.detail==Alert.YES){
var sid:String = dg.selectedItem.id;
gw.call( "ItemsManager.request_drop_item", new Responder(onItemsResult, onFault), sid);
}
}
/**
* @event
* @description
*/
public function reload_items(evt:MouseEvent):void {
gw.call( "ItemsManager.request_items", new Responder(onItemsResult, onFault));
}
/**
* @event
* @description
*/
public function pop_add_item(evt:MouseEvent):void { item_view(null); }
/**
* @event
* @description
*/
private function onIECloseClick(evt:MouseEvent):void { PopUpManager.removePopUp(pop);
}
/**
* @description assign button events
*/
private function setup_button_events():void {
btReloadItems.addEventListener(MouseEvent.CLICK, reload_items);
btAddItem.addEventListener(MouseEvent.CLICK, pop_add_item);
}
Il codice parte coi soliti import e alcune variabili globali, una importante e' phpGateway che contiene l'url del gateway amfphp, naturalmente
va settata secondo la propria installazione di amfphp.
la initFlexGridTest si limita a creare un oggetto remoting, a settare gli eventi dei due pulsanti e a caricare i dati degli utenti nel datagrid.
L'oggetto remoting e' in realta' una classe che eredita da NetConnection, presente in prj.jaco.amf:
package prj.jaco.amf { import flash.net.*; import mx.rpc.http.*; import mx.rpc.events.*; public class RemotingConnection extends NetConnection { public function RemotingConnection( sURL:String ){ objectEncoding = ObjectEncoding.AMF3; if (sURL) connect( sURL ); } } }
Tale classe ha lo scopo di semplificare la creazione di oggetti remoting pronti all'uso, sono impostati per lavorare con AMF3, percio' e' consigliato installare amfphp1.9, le versioni precedenti supportano solo il formato AMF0, eventualmente cambiare questo parametro.
la setup_buttons imposta gli eventi sui pulsanti, in AS3 e' sufficiente invocare addEventListener, una volta presa l'abitudine, gestire eventi diviene presto una semplice formalita', gestori d'evento AS1/2 addio!
infine si chiama la reload_items per popolare il datagrid, funziona chiamando un metodo remoto in amfphp attraverso il metodo call, dell'oggetto remoting creato in precedenza e impostando i gestori d'evento che reagiranno in caso di risposta valida o problemi di altra natura. Possiamo riutilizzare un oggetto remoting per qualsiasi metodo che abbiamo implementato in amfphp, per tale motivo e' stato scelto di crearne uno a livello di applicazione.
Scorrendo il codice si notera' che la maggior parte riguarda la gestione di eventi. Alcuni di questi vengono generati da un cosiddetto ItemRenderer che in pratica e' un componente personalizzato visualizzato in ogni nuova riga del datagrid, nella colonna options. Tale component e' residente nel file IRRowOptions.mxml ed e' costituito da due pulsanti di tipo immagine:<?xml version="1.0" encoding="utf-8"?> <mx:HBox horizontalAlign="center" verticalAlign="middle" xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Button id="btEditRow" styleName="editButton" click="request_item_edit()" /> <mx:Button id="btDropRow" styleName="dropButton" click="request_item_drop()" /> <mx:Script> <![CDATA[ import mx.controls.*; ... public var app:Object = mx.core.Application.application; public function request_item_drop():void { var dg:DataGrid = app.dg; var si:Object = dg.selectedItem; var id:String = si.id; var sName:String = si.sName; var sSurname:String = si.sSurname; var eRole:String = si.eRole; var msg:String = "do you really want to delete this item?"; msg += "\nid: " +id; msg += "\nname: " +sName; msg += "\nsurname: " +sSurname; msg += "\nrole: " +eRole; Alert.show(msg, "DELETE: Please confirm", 3, this, app.request_item_delete); } public function request_item_edit():void { var dg:DataGrid = app.dg; var si:Object = dg.selectedItem; app.item_view(si); } ]]> </mx:Script> </mx:HBox>Stavolta, visto che il codice non e' molto, si e' preferito mettere tutto insieme, il codice flex e actionscript. Da rilevare il fatto che il codice AS3 va inserito in un tag CDATA, per non violare la condizione well-formed del file mxml.
/** * @author jaco * created on 07/mar/07 */ package prj { import mx.containers.*; ... public class ItemEditor extends Panel { public var btClose:Button; public var btSave:Button; private var currentID:Number = -1; public var roles:Array = [ {label:"guest", data:"guest"}, {label:"user", data:"user"}, {label:"admin", data:"admin"} ] private var fiName:FormItem; ... private var lbName:TextInput; ... function ItemEditor(){ setup_layout(); } private function setup_layout():void { setStyle("backgroundColor", "#999999"); ... width = 300; height = 200; horizontalScrollPolicy = "off"; verticalScrollPolicy = "off"; var contentBox:VBox = new VBox(); contentBox.setStyle("borderSides", "10"); ... var topSpacer:Spacer = new Spacer(); topSpacer.height = 5; var lb:Label = new Label(); lb.setStyle("fontWeight", "bold"); lb.setStyle("paddingLeft", "10"); lb.text = "Create/Edit item"; var f:Form = new Form(); fiName = new FormItem(); fiName.label = "name"; fiName.required = true; fiSurname = new FormItem(); ... fiRole = new FormItem(); fiRole.label = "role"; lbName = new TextInput(); lbSurname = new TextInput(); cbRole = new ComboBox(); cbRole.dataProvider = roles; var cb:ControlBar = new ControlBar(); cb.percentWidth = 100; cb.setStyle("backgroundColor", "#999999"); var spacer:Spacer = new Spacer(); spacer.percentWidth = 100; btSave = new Button(); btSave.label = "save"; btClose = new Button(); btClose.label = "close"; cb.addChild(spacer); cb.addChild(btSave); cb.addChild(btClose); fiName.addChild(lbName); fiSurname.addChild(lbSurname); fiRole.addChild(cbRole); f.addChild(fiName); f.addChild(fiSurname); f.addChild(fiRole); contentBox.addChild(topSpacer); contentBox.addChild(lb); contentBox.addChild(f); addChild(contentBox); addChild(cb); setup_textInput_events(); } private function setup_textInput_events():void{ lbName.addEventListener(KeyboardEvent.KEY_UP, this.check_form); lbSurname.addEventListener(KeyboardEvent.KEY_UP, this.check_form); } private function check_form(evt:KeyboardEvent):void{ var sName:String = lbName.text; var sSurname:String = lbSurname.text; if(sName.length > 2 && sSurname.length > 2){ btSave.visible = true; } else btSave.visible = false; } private function handleInvalid(evt:ValidationResultEvent):void { btClose.visible = false; } public function set_itemData(itemData:Object):void{ var roleOrder:Array = ["guest", "user", "admin"]; if(!itemData) btSave.visible = false; else { lbName.text = itemData.sName; lbSurname.text = itemData.sSurname; currentID = itemData.id; var roLength:int = roleOrder.length; for(var i:int = 0; i < roLength; i++){ if(roleOrder[i] == itemData.eRole){ cbRole.selectedIndex = i; break; } } } } public function get_itemData():Object { var itemData:Object = new Object(); itemData.id = currentID; itemData.sName = lbName.text; itemData.sSurname = lbSurname.text; itemData.eRole = cbRole.selectedItem.data; return itemData; } } }Curiosamente questo file contiene piu' codice di quello principale, alcune parti ripetitive sono state tagliate, fate sempre riferimento al codice sorgente.
mxmlc FlexGridTest.mxml -output ../deploy/FlexGridTest.swfSe si riceve un errore sulla mancanza di mxmlc e' chiaro che questi manca nel path environment, in tal caso o si chiama mxmlc esplicitamente col percorso di installazione nel proprio file system o si aggiunge il path nelle variabili d'ambiente, chiaramente la seconda scelta e' assai piu' comoda.
config.inc.php contiene alcuni settaggi per il backend, per l'accesso al db e i path degli script
db_utils.php contiene alcuni wrapper d'accesso al db e al lancio delle query.
ItemsManager.php andra' nella directory services di amfphp insieme a ItemsManager.methodTable.php, questi costituiscono l'interfaccia tra flex
e il server.
03.2k7
:)
jaco_at_pixeldump