14. L’applicazione [SimuPaie] – versione 10 – client Flex di un servizio web ASP.NET
Presentiamo ora un client Flex del servizio web ASP.NET della versione 5. Il IDE utilizzato è Flex Builder 3. Una versione dimostrativa di questo prodotto è scaricabile all'indirizzo URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 è un IDE Eclipse. Inoltre, per eseguire il client Flex, utilizziamo un server web Apache dello strumento Wamp [http://www.wampserver.com/]. È possibile utilizzare qualsiasi server Apache. Il browser che visualizza il client Flex deve disporre del plugin Flash Player, versione 9 o superiore.
Le applicazioni Flex hanno la particolarità di essere eseguite all’interno del plugin Flash Player del browser. In questo senso sono simili alle applicazioni Ajax, che incorporano nelle pagine inviate al browser degli script JavaScript che vengono poi eseguiti all’interno del browser. Un'applicazione Flex non è un'applicazione web nel senso comune del termine: è un'applicazione client che utilizza servizi forniti da server web. In questo senso, è analoga a un'applicazione desktop che fungesse da client per gli stessi servizi. Si differenzia tuttavia per un aspetto: viene inizialmente scaricata da un server web all'interno di un browser dotato del plugin Flash Player in grado di eseguirla.
Come un’applicazione desktop, un’applicazione Flex è composta principalmente da due elementi:
- una parte di presentazione: le viste visualizzate nel browser. Queste viste hanno la ricchezza delle finestre delle applicazioni desktop. Una vista è descritta tramite un linguaggio a tag chiamato MXML.
- una parte di codice che gestisce principalmente gli eventi generati dalle azioni dell’utente sulla vista. Questo codice può essere scritto anche in MXML o con un linguaggio orientato agli oggetti chiamato ActionScript. È necessario distinguere due tipi di eventi:
- l’evento che richiede uno scambio con il server web: il popolamento di un elenco con dati forniti da un’applicazione web, l’invio dei dati di un modulo al server, ... Flex fornisce una serie di metodi per comunicare con il server in modo trasparente per lo sviluppatore. Questi metodi sono di default asincroni: l’utente può continuare a interagire con la vista durante la richiesta al server.
- l'evento che modifica la vista visualizzata senza scambio di dati con il server, ad esempio trascinare un elemento da un albero per inserirlo in un elenco. Questo tipo di evento viene gestito interamente a livello locale all'interno del browser.
Un'applicazione Flex viene spesso eseguita nel modo seguente:
![]() |
- in [1], viene richiesta una pagina HTML
- in [2], la pagina viene inviata. Essa include un file binario SWF (ShockWave Flash) contenente l’intera applicazione Flex: tutte le viste e il codice di gestione degli eventi delle stesse. Questo file verrà eseguito dal plugin Flash Player del browser.
![]() |
- L’esecuzione del client Flex avviene localmente sul browser, tranne quando ha bisogno di dati esterni. In tal caso, li richiede al server [3]. Li riceve in [4] in vari formati: XML o binario. L'applicazione interpellata sul server web può essere scritta in qualsiasi linguaggio. Ciò che conta è solo il formato della risposta.
Abbiamo descritto l’architettura di esecuzione di un’applicazione Flex affinché il lettore possa cogliere la differenza tra questa e quella di un’applicazione web classica, in cui le pagine non contengono codice (JavaScript, Flex, Silverlight, ...) che il browser dovrebbe eseguire. In quest'ultima, il browser è passivo: si limita a visualizzare le pagine HTML generate sul server web che gliele invia.
14.1. Architettura client/server dell’applicazione
L’architettura client/server implementata è analoga a quella delle versioni 6 e 8:
![]() |
In [1], il livello web ASP.NET è sostituito da un livello web Flex scritto in MXML e ActionScript. Il client [C] verrà generato da IDE Flex Builder. È importante ricordare che questa architettura comprende due server web non rappresentati:
- un server web ASP.NET che esegue il servizio web [S]
- un server web APACHE che esegue il client web [1]
14.2. Il progetto Flex 3 del client
Sviluppiamo il client Flex con Flex Builder 3 IDE:
![]() |
- in Flex Builder 3, creiamo un nuovo progetto in [1]
- gli si assegna un nome in [2] e si specifica in [3] in quale cartella generarlo
![]() |
- in [4], si assegna un nome all'applicazione principale (quella che verrà eseguita)
- in [5], il progetto una volta generato
- in [6], il file principale dell'applicazione MXML
- un file MXML contiene una vista e il codice di gestione degli eventi relativi a essa. La scheda [Source] [7] consente di accedere al file MXML. In esso si trovano i tag <mx> che descrivono la vista, nonché il codice ActionScript.
- La vista può essere creata graficamente utilizzando la scheda [Design] [8]. I tag MXML che descrivono la vista vengono quindi generati automaticamente nella scheda [Source]. Vale anche il contrario: i tag MXML aggiunti direttamente nella scheda [Source] vengono riprodotti graficamente nella scheda [Design].
14.3. La vista n. 1
Costruiremo progressivamente un'interfaccia web analoga a quella della versione 1 (cfr. paragrafo 4). Per prima cosa realizziamo la seguente interfaccia:
![]() |
- in [1], la schermata visualizzata quando la connessione al servizio web è stata stabilita con successo. Il menu a tendina dei dipendenti viene quindi compilato.
- in [2], la schermata visualizzata quando non è stato possibile stabilire la connessione al servizio web. Viene quindi visualizzato un messaggio di errore.
Il file principale del cliente [main.xml] è il seguente:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
<mx:VBox width="100%">
<mx:Label text="Feuille de salaire" fontSize="30"/>
<mx:HBox>
<mx:VBox>
<mx:Label text="Employés"/>
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Heures travaillées"/>
<mx:TextInput id="txtHeuresTravaillees"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Jours travaillés"/>
<mx:NumericStepper id="joursTravailles" minimum="0" maximum="31" stepSize="1"/>
</mx:VBox>
<mx:VBox>
<mx:Label text=""/>
<mx:Button id="btnSalaire" label="Salaire"/>
</mx:VBox>
</mx:HBox>
<mx:TextArea id="msg" minWidth="400" minHeight="100" editable="false" visible="true" enabled="true" horizontalScrollPolicy="auto" verticalScrollPolicy="auto" x="0" y="0" maxHeight="100" maxWidth="400"/>
</mx:VBox>
<mx:WebService ...>
...
</mx:WebService>
<mx:Script>
<![CDATA[
...
// dati
[Bindable]
private var employes : ArrayCollection;
private function init():void{
...
}
]]>
</mx:Script>
</mx:Application>
In questo codice, occorre distinguere diversi elementi:
- la definizione dell’applicazione (righe 2-3)
- la descrizione della relativa vista (righe 4-25)
- i gestori di eventi in linguaggio ActionScript all'interno del tag <mx:Script> (righe 31-42)
- la definizione del servizio web remoto (righe 27-29)
Per cominciare, commentiamo la definizione dell’applicazione stessa e la descrizione della sua vista:
- righe 2-3: definiscono:
- la modalità di disposizione dei componenti nel contenitore della vista. L’attributo layout="vertical" indica che i componenti saranno disposti uno sotto l’altro.
- il metodo da eseguire una volta che la vista sarà stata istanziata, c.a.d. il momento in cui tutti i suoi componenti saranno stati istanziati. L’attributo creationComplete="init();" indica che deve essere eseguito il metodo init della riga 38. creationComplete è uno degli eventi che può essere emesso dalla classe Application.
- le righe 4-25 definiscono i componenti della vista
- righe 4-25: un contenitore verticale: i componenti vi saranno disposti uno sotto l'altro
- riga 5: definisce un testo
- righe 6-23: un contenitore orizzontale: i componenti saranno disposti orizzontalmente al suo interno.
- righe 7-10: un contenitore verticale che conterrà un testo e un menu a tendina
- riga 8: il testo
- riga 9: l'elenco a discesa in cui verrà inserito l'elenco dei dipendenti. Il tag dataProvider="{employes}" indica la fonte dei dati che devono compilare l'elenco. In questo caso, l'elenco verrà compilato con l'oggetto employes definito alla riga 36. Per poter scrivere dataProvider="{employes}", è necessario che il campo employes abbia l’attributo [Bindable] (riga 35). Questo attributo consente di fare riferimento a una variabile ActionScript al di fuori del tag <mx:Script>. Il campo employes è di tipo ArrayCollection, un tipo ActionScript che consente di memorizzare elenchi di oggetti, in questo caso un elenco di oggetti di tipo Employe.
- righe 11-14: un contenitore verticale che conterrà un testo e un campo di immissione
- riga 12: il testo
- riga 13: il campo di immissione delle ore lavorate.
- righe 15-18: un contenitore verticale che conterrà un testo e un contatore
- riga 16: il testo
- riga 17: il contatore che consentirà l'inserimento dei giorni lavorati.
- righe 19-22: un contenitore verticale che conterrà un testo e un pulsante che avvierà il calcolo dello stipendio della persona selezionata nel menu a tendina.
- riga 20: il testo
- riga 21: il pulsante.
- riga 23: fine del contenitore orizzontale iniziato alla riga 6
- riga 24: un campo di testo in un componente di tipo TextArea. Visualizzerà i messaggi di errore.
- riga 25: fine del contenitore verticale iniziato alla riga 4
Le righe da 4 a 25 generano la seguente visualizzazione nella scheda [Design]:
![]() |
- [1]: generato dal componente Label della riga 5
- [2]: è stato generato dal componente ComboBox della riga 9
- [3]: è stato generato dal componente TextInput della riga 13
- [4]: è stato generato dal componente NumericStepper della riga 17
- [5]: è stato generato dal componente Button della riga 21
- [6]: è stato generato dal componente TextArea della riga 24
Esaminiamo ora la dichiarazione del servizio web remoto:
<mx:WebService id="pam"
wsdl="http://localhost:1077/Service1.asmx?WSDL"
fault="wsFault(event);"
showBusyCursor="true">
<mx:operation
name="GetAllIdentitesEmployes"
result="loadEmployesCompleted(event)"
fault="loadEmployesFault(event);">
<mx:request/>
</mx:operation>
</mx:WebService>
- riga 1: il servizio web è un componente con identificativo pam (attributo id)
- riga 2: l'URI del file WSDL del servizio web (cfr. paragrafo 9.2)
- riga 3: il metodo da eseguire in caso di errore durante gli scambi con il servizio web: il metodo wsFault.
- riga 4: richiede che venga visualizzato un indicatore per segnalare all’utente che è in corso uno scambio con il servizio web.
- righe 5-10: una delle operazioni offerte dal servizio web remoto. In questo caso, il metodo GetAllIdentitesEmployes.
- riga 7: il metodo da eseguire quando la chiamata a questo metodo termina normalmente, c.a.d, ovvero quando il servizio web restituisce correttamente l’elenco dei dipendenti
- riga 8: il metodo da eseguire quando la chiamata a questo metodo termina con un errore.
- riga 9: i parametri dell’operazione GetAllIdentitesEmployes. Sappiamo che questo metodo non richiede parametri; pertanto, lasciamo vuoto il tag <mx:request>.
Esaminiamo ora il codice ActionScript relativo al servizio web:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// dati
[Bindable]
private var employes : ArrayCollection;
private function init():void{
// si annotano le coordinate dell'area del messaggio
msgHeight=msg.height;
msgWidth=msg.width;
// si nasconde l'area dei messaggi
hideMsg();
// richiesta al servizio web remoto per ottenere l'elenco semplificato dei dipendenti
pam.GetAllIdentitesEmployes.send();
}
private function wsFault(event:Event):void{
// viene segnalato l'errore
msg.text="Service distant indisponible";
showMsg();
}
private function loadEmployesCompleted(event:ResultEvent):void{
// compilazione del menu a tendina dei dipendenti
employes=event.result as ArrayCollection;
}
private function displayEmploye(employe:Object):String{
// Identità di un dipendente
return employe.Prenom + " " + employe.Nom;
}
private function loadEmployesFault(event:FaultEvent):void{
// visualizzazione del messaggio di errore
msg.text=event.fault.message;
// modulo
showMsg();
}
// gestione dei blocchi
private var msgWidth:int;
private var msgHeight:int;
private function hideMsg():void{
msg.height=0;
msg.width=0;
}
private function showMsg():void{
msg.height=msgHeight;
msg.width=msgWidth;
}
]]>
</mx:Script>
- riga 11: il metodo init viene eseguito all’avvio dell’applicazione perché è stato scritto:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
- righe 13-14: si memorizzano l'altezza e la larghezza dell'area del messaggio. Si utilizzano due metodi, hideMsg (righe 48-51) e showMsg (righe 53-56), rispettivamente per nascondere o mostrare l’area dei messaggi a seconda che si sia verificato o meno un errore. Il metodo hideMsg nasconde l'area dei messaggi impostandone l'altezza e la larghezza a 0. Il metodo showMsg visualizza l'area dei messaggi ripristinandone l'altezza e la larghezza memorizzate nel metodo init.
- riga 16: si nasconde l’area dei messaggi. Inizialmente non vi è alcun errore.
- riga 18: viene chiamato il metodo GetAllIdentitesEmploye (riga 6 del servizio web) del servizio web pam (riga 1 del servizio web). La chiamata è asincrona. La riga 7 del servizio web indica che il metodo loadEmployesCompleted verrà eseguito se questa chiamata asincrona va a buon fine. La riga 8 del servizio web indica che il metodo loadEmployesFault verrà eseguito se questa chiamata asincrona non va a buon fine.
- riga 27: il metodo loadEmployesCompleted, che viene eseguito se la chiamata al servizio web della riga 18 va a buon fine.
- riga 29: sappiamo che il servizio web restituisce una risposta XML. È utile tornare a questa per comprendere il codice ActionScript:
![]() |
- in [1], pagina del servizio web [Service.asmx]
- in [2], il link alla pagina di test del metodo [GetAllIdentitesEmployes]
- in [3], il test è stato eseguito. Non è previsto alcun parametro.
- in [4]: la risposta XML contiene una tabella dei dipendenti. Per ciascuno di essi, sono presenti cinque informazioni racchiuse nei tag <Id>, <Version>, <SS>, <Nom>, <Prenom>. Se la risposta XML viene inserita in un array employes di tipo ArrayCollection:
- employes.getItemAt(i): è l'elemento n. i dell'array
- employes.getItemAt(i).SS: è il numero di previdenza sociale di questo dipendente.
- employes.getItemAt(i).Nome: è il nome di questo dipendente
- ...
Torniamo al codice ActionScript:
- riga 29: event.result rappresenta la risposta XML del servizio web. Il metodo GetAllIdentitesEmployes restituisce un array di dipendenti. event.result rappresenta questo array di dipendenti. Viene inserito in una variabile di tipo ArrayCollection, un tipo che rappresenta in generale una raccolta di oggetti. Questa variabile, denominata employes, è dichiarata alla riga 9. Ricordiamo che questa variabile è la fonte dei dati del menu a tendina dei dipendenti:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
Per ogni dipendente della sua fonte dati, il menu a tendina richiamerà il metodo displayEmploye (attributo labelFunction) per visualizzare il dipendente. Nelle righe 32-34 si vede che questo metodo visualizza il cognome e il nome del dipendente.
- riga 37: il metodo loadEmployesFault, che viene eseguito se la chiamata al servizio web della riga 18 non va a buon fine. event.fault.message è il messaggio di errore restituito dal servizio web.
- riga 39: questo messaggio di errore viene inserito nell'area dei messaggi
- riga 41: viene visualizzata l'area dei messaggi.
Una volta compilata l’applicazione, il suo codice eseguibile si trova nella cartella [bin-debug] del progetto Flex:
![]() |
Sopra,
- il file [main.html] rappresenta il file HTML che verrà richiesto dal browser al server web per ottenere il client Flex
- il file [main.swf] è il binario del client Flex che verrà incapsulato nella pagina HTML inviata al browser e successivamente eseguito dal plugin Flash Player di quest’ultimo.
Siamo pronti per eseguire il client Flex. Prima però dobbiamo configurare l’ambiente di esecuzione necessario. Torniamo all’architettura client/server testata:
![]() |
Lato server:
- avviare il servizio web ASP.NET [S]
Lato client:
- avviare il server Apache a cui verrà richiesta l’applicazione Flex.
In questo caso utilizziamo lo strumento Wamp. Con questo strumento, possiamo associare un alias alla cartella [bin-debug] del progetto Flex.
![]() |
- L'icona di Wamp si trova nella parte inferiore dello schermo [1]
- facendo clic con il tasto sinistro sull’icona Wamp, selezionare l’opzione Apache [2] / Alias Directories [3, 4]
- selezionare l’opzione [5]: Aggiungi un alias
![]() |
- in [6] assegnare un alias (un nome qualsiasi) all’applicazione web che verrà eseguita
- in [7] indicare la radice dell’applicazione web a cui verrà assegnato questo alias: si tratta della cartella [bin-debug] del progetto Flex che abbiamo appena creato.
Ricordiamo la struttura della cartella [bin-debug] del progetto Flex:
![]() |
Il file [main.html] corrisponde al file HTML dell’applicazione Flex. Grazie all’alias che abbiamo appena creato sulla cartella [bin-debug], questo file sarà accessibile tramite URL e [http://localhost/pam-v10-flex-client-webservice/main.html]. Lo apriamo in un browser dotato del plugin Flash Player versione 9 o superiore:
![]() |
- in [1], l'URL dell'applicazione Flex
- in [2], il menu a tendina dei dipendenti quando tutto funziona correttamente
- in [3], il risultato ottenuto quando il servizio web è inattivo
Potreste essere curiosi di visualizzare il codice sorgente della pagina HTML ricevuta:
- Il corpo della pagina inizia alla riga 25. Non contiene il classico HTML, ma un oggetto (riga 28) di tipo "application/x-shockwave-flash" (riga 41). Si tratta del file [main.swf] (riga 31) che è possibile visualizzare nella cartella [bin-debug] del progetto Flex. È un file di dimensioni considerevoli: circa 600 K per questo semplice esempio.
14.4. La vista n. 2
Aggiungeremo un nuovo contenitore di tipo VBox alla vista attuale:
![]() |
![]() |
- in [4,5], impostiamo [main2.mxml] come nuova applicazione predefinita. D'ora in poi sarà questa ad essere compilata.
- In [6], l’applicazione predefinita è contrassegnata da un punto blu.
Il contenitore [1] visualizzerà le informazioni relative al dipendente selezionato nel menu a tendina [2]. Duplichiamo [main.xml] in [main2.xml] e [3] per creare la nuova vista. D'ora in poi lavoreremo con [main2.xml].
![]() |
La modifica apportata al progetto precedente consiste nell’aggiunta del contenitore alla riga 26 sopra riportata, che contiene il codice MXML del contenitore [1] della vista. Gli assegniamo l’identificativo employe per poterlo gestire tramite codice. Infatti, questo contenitore dovrà poter essere nascosto/mostrato utilizzando la stessa tecnica impiegata in precedenza per l’area dei messaggi.
Torniamo alla parte visiva della vista:
![]() |
Individuiamo i diversi contenitori delle nuove informazioni visualizzate:
- V1: contenitore verticale di tutti i componenti: l’etichetta Employé [1] e i contenitori orizzontali [H1] e [H2]
- H1: contenitore orizzontale per le informazioni Nom, Prénom, Adresse
- V2: contenitore verticale per la dicitura Nom e la visualizzazione del nome del dipendente.
- H2: contenitore orizzontale per le informazioni Ville, codice postale, Indice
Il codice completo del contenitore "employe" è il seguente:
<mx:VBox id="employe" width="100%">
<mx:Label text="Employé" fontSize="20" color="#09F3EB"/>
<mx:HBox>
<mx:VBox >
<mx:Label text="Nom"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblNom" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Prénom"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblPreNom" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Adresse"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblAdresse" minWidth="250" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
</mx:HBox>
<mx:HBox>
<mx:VBox >
<mx:Label text="Ville"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblVille" minWidth="100" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Code Postal"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblCodePostal" minWidth="70" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
<mx:VBox >
<mx:Label text="Indice"/>
<mx:VBox backgroundColor="#EECA05">
<mx:Text id="lblIndice" minWidth="20" minHeight="20" fontFamily="Verdana" textAlign="center"/>
</mx:VBox>
</mx:VBox>
</mx:HBox>
</mx:VBox>
Il codice è intuitivo. Spieghiamo semplicemente il contenitore verticale che visualizza il nome del dipendente, ad esempio:
- righe 4-9: il contenitore verticale
- riga 5: la dicitura Nom
- righe 6-8: un contenitore verticale che visualizzerà il nome del dipendente (riga 7). Vogliamo assegnare un colore di sfondo diverso ai campi che visualizzano le informazioni sul dipendente. Il componente Text non offre questa possibilità (o forse non ho cercato bene). È possibile impostare il colore di sfondo di un contenitore. Ecco perché è stato utilizzato in questo caso.
- riga 7: il componente Text che visualizzerà il nome del dipendente. Gli vengono assegnate un'altezza e una larghezza minime.
Utilizzeremo il contenitore "employe" per visualizzare le informazioni del dipendente selezionate dall’utente nel menu a tendina dei dipendenti, indipendentemente dal pulsante [Salaire], il cui ruolo sarà in seguito quello di calcolare lo stipendio una volta inserite tutte le informazioni necessarie.
Per gestire il cambio di selezione nel menu a tendina "employes", il suo codice MXML viene modificato come segue:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>
L'evento "change" viene emesso dal menu a tendina quando l'utente modifica la propria selezione. Il gestore di questo evento sarà il metodo displayInfosEmploye.
Ricordiamo i metodi esposti dal servizio web remoto:
// elenco di tutte le identità dei dipendenti
public Employe[] GetAllIdentitesEmployes();
// ------- calcolo dello stipendio
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);
In questo caso vogliamo visualizzare le informazioni (cognome, nome, ...) del dipendente selezionato nel menu a tendina. Il servizio web non espone alcun metodo per ottenerle. Tuttavia, possiamo utilizzare il metodo GetSalaire passando il numero SS del dipendente selezionato e 0 per le ore e i giorni lavorati. Verrà eseguito un calcolo dello stipendio superfluo, ma il metodo GetSalaire ci restituirà un oggetto di tipo FeuilleSalaire in cui troveremo le informazioni di cui abbiamo bisogno.
La dichiarazione attuale del servizio web viene modificata per includere la definizione del metodo GetSalaire:
<mx:WebService id="pam"
wsdl="http://localhost:1077/Service1.asmx?WSDL"
fault="wsFault(event);"
showBusyCursor="true">
<mx:operation
name="GetAllIdentitesEmployes"
result="loadEmployesCompleted(event)"
fault="loadEmployesFault(event);">
<mx:request/>
</mx:operation>
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
</mx:WebService>
- righe 11-19: la definizione del metodo GetSalaire del servizio web
- riga 12: definisce il metodo da eseguire quando la chiamata al metodo GetSalaire va a buon fine
- riga 13: definisce il metodo da eseguire quando la chiamata al metodo GetSalaire fallisce
- righe 14-18: il metodo GetSalaire richiede tre parametri. Questi sono definiti all'interno di un tag <mx:request> nella forma <param1>valore1</param1>. L'identificatore param1 non può essere arbitrario. È necessario utilizzare i nomi previsti dal servizio web:
![]() |
- in [1], la pagina del servizio web [http://localhost:1077/Service1.asmx]
- in [2], il link alla pagina di test del metodo [GetSalaire]
- in [3], i parametri previsti dal metodo. Sono questi i nomi da utilizzare come tag figli del tag <mx:request>.
Torniamo alla dichiarazione del servizio web:
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
- riga 5: il parametro ss. Ricordiamo che all’avvio dell’applicazione Flex, l’array di tutti i dipendenti è stato memorizzato in una variabile «employes» di tipo ArrayCollection.
- employes.getItemAt(i): è il dipendente n. i dell’array
- employes.getItemAt(i).SS: è il numero di previdenza sociale di questo dipendente.
- cmbEmployes.selectedIndex: è il numero dell’elemento selezionato nel menu a tendina dei dipendenti cmbemployes.
Come si fa a sapere che SS è il numero di previdenza sociale di un dipendente? Per capirlo, occorre tornare alla risposta inviata dal metodo GetAllIdentitesEmployes:
![]() |
- in [1], pagina del servizio web [Service.asmx]
- in [2], il link alla pagina di test del metodo [GetAllIdentitesEmployes]
- in [3], il test è stato eseguito. Non è previsto alcun parametro.
- in [4]: la risposta XML contiene una tabella dei dipendenti. È proprio questa tabella che è stata memorizzata nella variabile employes. In [5] si vede che SS è effettivamente il tag utilizzato per contenere il numero di previdenza sociale.
Concludiamo l’analisi del servizio web:
<mx:operation name="GetSalaire"
result="getSalaireCompleted(event)"
fault="getSalaireFault(event);">
<mx:request>
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
</mx:request>
</mx:operation>
- riga 6: il numero di ore lavorate sarà fornito dalla variabile heuresTravaillees
- riga 6: il numero di giorni lavorati sarà fornito da una variabile joursDeTravail
Queste variabili devono essere dichiarate nel tag <mx:Script> con l’attributo [Bindable] che consente loro di essere referenziate dai componenti MXML (righe 7-10 di seguito).
<mx:Script>
<![CDATA[
...
// dati
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
...
</mx:Script>
Il codice di gestione degli eventi della vista viene modificato come segue:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// dati
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
private function init():void{
// si annotano l'altezza e la larghezza di # blocchi
employeHeight=employe.height;
employeWidth=employe.width;
// si nascondono alcuni elementi
hideEmploye();
...
}
private function displayInfosEmploye():void{
// modulo
hideEmploye();
// si calcola uno stipendio fittizio
heuresTravaillees=0;
joursDeTravail=0;
pam.GetSalaire.send();
}
private function getSalaireCompleted(event:ResultEvent):void{
...
}
private function getSalaireFault(event:FaultEvent):void{
...
}
// viste parziali -------------------------------------------------
private var employeHeight:int;
private var employeWidth:int;
private function hideEmploye():void{
employe.height=0;
employe.width=0;
}
private function showEmploye():void{
employe.height=employeHeight;
employe.width=employeWidth;
}
]]>
</mx:Script>
- riga 15: il metodo init, eseguito all’avvio dell’applicazione Flex, memorizza l’altezza e la larghezza del contenitore verticale employe, in modo da poterlo ripristinare (righe 50-53) dopo averlo nascosto (righe 45-48).
- riga 24: il metodo displayInfosEmploye è quello eseguito quando l’utente modifica la propria selezione nel menu a tendina dei dipendenti.
- riga 26: il contenitore employe viene nascosto se era visibile
- riga 30: il metodo GetSalaire del servizio web viene chiamato in modo asincrono. Si sa che richiede tre parametri:
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
- riga 1: il parametro ss sarà il numero SS del dipendente selezionato nel menu a tendina dei dipendenti
- riga 2: il metodo displayInfosEmploye assegna il valore 0 alla variabile heuresTravaillees (riga 28)
- riga 3: il metodo displayInfosEmploye assegna il valore 0 alla variabile joursDeTravail (riga 29)
Il metodo GetSalaireCompleted viene eseguito se il metodo GetSalaire del servizio web viene completato con successo:
private function getSalaireCompleted(event:ResultEvent):void{
// si nasconde il messaggio di errore
hideMsg();
// si riceve una busta paga
var feuilleSalaire:Object=event.result;
// visualizzazione
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indice;
showEmploye();
}
- riga 3: si nasconde l'area dei messaggi nel caso in cui venga visualizzata.
- riga 5: si recupera il foglio paga restituito dal metodo GetSalaire
Per sapere esattamente cosa restituisce il metodo GetSalaire, torniamo alla pagina del servizio web:
![]() |
- in [1], la pagina del servizio web [Service.asmx]
- in [2], il link che porta alla pagina di test del metodo [GetSalaire]
- in [3], vengono forniti i parametri
- in [4], il risultato ottenuto XML.
Torniamo al metodo getSalaireCompleted:
private function getSalaireCompleted(event:ResultEvent):void{
// si nasconde il messaggio di errore
hideMsg();
// si riceve una busta paga
var feuilleSalaire:Object=event.result;
// visualizzazione
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indemnites.Indice;
showEmploye();
}
- riga 5: feuilleSalaire=event.result rappresenta il flusso XML [4] restituito dal metodo GetSalaire. Da questo flusso si evince che:
- feuilleSalaire.Employe è il flusso XML di un dipendente
- feuilleSalaire.Employe.Nom è il nome di tale dipendente
- ...
- righe 7-12: il flusso XML feuilleSalaire viene utilizzato per compilare i vari campi del contenitore «dipendente».
- riga 13: viene visualizzato il contenitore «dipendente».
Il metodo getSalaireFault viene eseguito se il metodo GetSalaire del servizio web termina con un errore:
private function getSalaireFault(event:FaultEvent):void{
// visualizzazione del messaggio di errore
msg.text=event.fault.message;
// modulo
showMsg();
}
- riga 3: il messaggio di errore event.fault.message viene inserito nell'area dei messaggi
- riga 5: viene visualizzato il campo messaggio
Qui terminano le modifiche necessarie per questa nuova versione. Una volta salvata, se la sintassi è corretta, la versione eseguibile viene generata nella cartella [bin-debug] del progetto:
![]() |
Quanto sopra: [main2.html] è la pagina HTML che incorpora il file binario dell'applicazione Flex [main2.swf] che verrà eseguito da Flash Player.
Possiamo testare questa nuova versione:
- il servizio web ASP.NET deve essere avviato
- il server Apache deve essere avviato per il client Flex
Supponendo che l'alias [pam-v10-flex-client-webservice] utilizzato nella versione precedente esista ancora, si richiede al server Apache l'accesso a URL e [http://localhost/pam-v10-flex-client-webservice/main2.html] in un browser:
![]() |
![]() |
- in [1], il URL richiesto
- in [2], il menu a tendina dei dipendenti
- in [3], si modifica la selezione nel menu a tendina per attivare l'evento change
- in [4], il risultato ottenuto: la scheda di Justine Laverti.
14.5. La vista n. 3
La vista n. 3 esegue il controllo di validità del modulo. Qui viene verificato solo il campo di immissione "txtHeuresTravaillees". Finché il modulo non è corretto, il pulsante "btnSalaire" rimarrà disabilitato.
Per aggiungere questa funzionalità, duplichiamo [main2.mxml] in [main3.mxml]:
![]() |
D'ora in poi lavoreremo con [main3.mxml], che imposteremo come applicazione predefinita (vedere questo concetto al paragrafo 14.4). Innanzitutto, aggiungiamo un attributo al componente "txtHeuresTravaillees":
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
Ogni volta che il contenuto del campo di immissione "txtHeuresTravaillees" cambia, viene chiamato il metodo validateForm. Si tratta di un metodo locale scritto dallo sviluppatore. All’interno di esso, potremmo verificare che il contenuto del campo di immissione «txtHeuresTravaillees» sia effettivamente un numero intero positivo. Procederemo invece in modo diverso utilizzando un componente di validazione:
<mx:NumberValidator id="heuresTravailleesValidator" source="{txtHeuresTravaillees}" property="text"
precision="2" allowNegative="false"
invalidCharError="Caractères invalides"
precisionError="Deux chiffres au plus après la virgule"
negativeError="Le nombre d'heures doit être positif ou nul"
invalidFormatCharsError="Format invalide"
required="true"
requiredFieldError="Donnée requise"/>
- riga 1: il componente <mx:NumberValidator> consente di verificare che un altro componente contenga un numero intero o reale.
- riga 1: l’attributo id assegna un identificatore al componente.
- riga 1: source è l’ID del componente verificato dal componente NumberValidator. In questo caso, viene verificato il campo di immissione “txtHeuresTravaillees”.
- riga 1: property è il nome della proprietà del componente source che contiene il valore da verificare. In definitiva, è il valore source.property che viene verificato, in questo caso txtHeuresTravaillees.text.
- riga 2: precision stabilisce il numero massimo di cifre decimali consentite. precision=0 serve a verificare che il numero inserito sia un numero intero.
- riga 2: allowNegative indica se i numeri negativi sono consentiti o meno
- riga 7: required indica se l’inserimento è obbligatorio o meno.
Quando una condizione di convalida non viene verificata, viene visualizzato un messaggio di errore in un fumetto accanto al componente errato. Per impostazione predefinita, questi messaggi sono in inglese. È possibile definire autonomamente questi messaggi:
- (continua)
- invalidCharError: il messaggio di errore quando il testo contiene un carattere che non può essere presente in un numero
- precisionError: il messaggio di errore quando il numero di cifre decimali non è corretto rispetto all’attributo precision
- negativeError: il messaggio di errore quando il numero è negativo mentre l'attributo allowNegative è impostato su "false"
- requiredFieldError: il messaggio di errore quando non è stato inserito alcun dato mentre l'attributo requiredField è impostato su "true"
- invalidFormatCharsError: il messaggio di errore quando il testo contiene caratteri o un formato non valido?
Torniamo al componente "txtHeuresTravaillees":
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
Il metodo validateForm potrebbe essere il seguente all’interno del tag <mx:Script>:
private function validateForm(event:Event):void
{
// si convalidano le ore lavorate
var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
// convalida riuscita?
btnSalaire.enabled=evt.type==ValidationResultEvent.VALID;
}
- riga 4: viene eseguito il validatore "heuresTravailleesValidator". Restituisce un risultato di tipo ValidationResultEvent.
- riga 6: evt.type è di tipo String e indica il tipo di evento. evt.type ha due possibili valori per il tipo ValidationResultEvent, "invalid" o "valid", rappresentati dalle costanti ValidationResultEvent.INVALID e ValidationResultEvent.VALID. Se alla riga 4 la convalida ha avuto esito positivo, evt.type deve assumere il valore ValidationResultEvent.VALID. In questo caso, il pulsante btnSalaire è attivo, altrimenti è disattivato.
Questo è sufficiente per verificare la validità delle ore lavorate.
![]() |
Come illustrato sopra, la compilazione del progetto ha generato i file [main3.html] e [main3.swf]. Richiediamo i file URL e [http://localhost/pam-v10-flex-client-webservice/main3.html] in un browser e verifichiamo diversi casi di errore:
![]() |
![]() |
- un campo errato presenta un bordo rosso [1, 2, 3], mentre un campo corretto presenta un bordo blu [4].
- In [4], si noti che il pulsante [Salaire] è attivo perché il numero di ore lavorate è corretto.
14.6. La vista n. 4
La vista n. 4 completa il modulo di calcolo dello stipendio. A tal fine, duplichiamo [main3.xml] in [main4.xml] e d’ora in poi lavoriamo con main4, che impostiamo come applicazione predefinita (cfr. paragrafo 14.4).
![]() |
Le modifiche apportate in [main4.xml] e [1] sono le seguenti:
- è stato aggiunto un nuovo contenitore verticale alla vista [2] per visualizzare le voci relative alla retribuzione del dipendente
- è stato aggiunto un componente che consente di formattare i valori monetari in [3]
- la visualizzazione delle voci salariali è gestita dal gestore associato all'evento "clic" del pulsante "btnSalaire".
La vista si evolve come segue:
![]() |
Il nuovo contenitore segue lo stesso principio del precedente. Si tratta di un contenitore verticale VBox [V1] contenente quattro contenitori orizzontali HBox [Hi]. I contenitori orizzontali da H1 a H3 sono costituiti da contenitori verticali contenenti due etichette, di cui la seconda si trova a sua volta in un contenitore verticale per avere un colore di sfondo.
Domanda 1: scrivere il contenitore dello stipendio. D'ora in poi verrà chiamato complements.
Domanda 2: scrivere i metodi che consentono di nascondere/mostrare il contenitore complements. Ci si ispirerà a quanto fatto in precedenza per il contenitore employe.
Si associa un gestore all’evento “click” del pulsante “btnSalaire”:
<mx:Button id="btnSalaire" label="Salaire" click="calculerSalaire()"/>
Il metodo calculerSalaire è il seguente:
private function calculerSalaire():void{
// preparazione del modulo
affichageSalaire=true;
msg.text="";
// parametri di calcolo dello stipendio
heuresTravaillees=Number(txtHeuresTravaillees.text);
joursDeTravail=int(joursTravailles.value);
// la richiesta dello stipendio viene inviata al servizio web
pam.GetSalaire.send();
}
- riga 3: il valore booleano affichageSalaire serve a indicare se visualizzare o meno il contenitore complements che mostra le voci dello stipendio. Il metodo getSalaireCompleted viene eseguito in occasione di due eventi:
- il cambio di dipendente nel menu a tendina dei dipendenti per visualizzarne le informazioni senza lo stipendio. In questo caso si imposterà affichageSalaire=false.
- il calcolo dello stipendio
- riga 6: il testo del campo di immissione txtHeuresTravaillees viene convertito in un numero reale.
- riga 7: il valore dell'incrementatore joursTravailles viene convertito in un numero intero.
- riga 9: chiamata al metodo remoto GetSalaire. Si ricorda che questo metodo richiede tre parametri, tra cui i parametri heuresTravaillees e joursDeTravail inizializzati alle righe 6 e 7. Si ricorda inoltre che, se la chiamata asincrona del metodo GetSalaire:
- ha esito positivo, verrà chiamato il metodo getSalaireCompleted
- non va a buon fine, verrà chiamato il metodo getSalaireFault
Domanda 3: integrare l'attuale metodo getSalaireCompleted in modo che visualizzi lo stipendio del dipendente se è stato cliccato il pulsante btnSalaire.
Attualmente, le voci dello stipendio vengono visualizzate senza il simbolo dell'euro. È possibile includerlo nel codice oppure utilizzare un formatore. Questa è la soluzione proposta al momento. Il formatore sarà il seguente:
<mx:CurrencyFormatter id="eurosFormatter" precision="2"
currencySymbol="€" useNegativeSign="true"
alignSymbol="right"/>
- riga 1: id è l’identificativo del formatore, precision il numero di cifre decimali da mantenere.
- riga 2: currencySymbol è il simbolo monetario da utilizzare. useNegativeSign indica se utilizzare o meno il segno - per i valori negativi.
- riga 3: alignSymbol indica dove posizionare il simbolo monetario rispetto al numero.
Questo formatore va utilizzato nel codice dello script nel modo seguente:
- eurosFormatter è l’ID del formatore da utilizzare
- format è il metodo da chiamare per formattare un numero. Restituisce una stringa di caratteri.
- feuilleSalaire.Indemnites.BaseHeure è in questo caso il numero da formattare.
- lblSH è il nome di un componente di tipo Text.
Domanda 4: modificare il metodo getSalaireCompleted in modo che utilizzi il formattatore monetario.





























