Skip to content

14. L'applicazione [SimuPaie] – versione 10 – un client Flex per un servizio web ASP.NET

Presentiamo ora un client Flex per il servizio web ASP.NET, versione 5. L'IDE utilizzato è Flex Builder 3. Una versione demo di questo prodotto può essere scaricata all'URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 è un IDE basato su Eclipse. Inoltre, per eseguire il client Flex, utilizziamo un server web Apache fornito dallo strumento Wamp [http://www.wampserver.com/]. È compatibile qualsiasi server Apache. Il browser che visualizza il client Flex deve avere installato Flash Player versione 9 o superiore.

Le applicazioni Flex sono uniche in quanto vengono eseguite all'interno del plugin Flash Player del browser. Sotto questo aspetto, sono simili alle applicazioni Ajax, che incorporano script JavaScript nelle pagine inviate al browser, che vengono poi eseguiti all'interno del browser. Un'applicazione Flex non è un'applicazione web nel senso comune del termine: è un'applicazione client per servizi forniti da server web. Sotto questo aspetto, è analoga a un'applicazione desktop che fungerebbe da client per quegli stessi servizi. Si differenzia, tuttavia, sotto un aspetto: viene inizialmente scaricata da un server web in un browser dotato del plugin Flash Player in grado di eseguirla.

Proprio come un'applicazione desktop, un'applicazione Flex è costituita principalmente da due elementi:

  • un livello di presentazione: le viste visualizzate nel browser. Queste viste offrono la stessa ricchezza delle finestre delle applicazioni desktop. Una vista viene descritta utilizzando un linguaggio di markup chiamato MXML.
  • una sezione di codice che gestisce principalmente gli eventi attivati dalle azioni dell'utente sulla vista. Questo codice può essere scritto in MXML o in un linguaggio orientato agli oggetti chiamato ActionScript. Esistono due tipi di eventi da distinguere:
    • eventi che richiedono la comunicazione con il server web: popolamento di un elenco con dati forniti da un'applicazione web, invio di dati di moduli al server, ecc. Flex fornisce una serie di metodi per comunicare con il server in modo trasparente per lo sviluppatore. Questi metodi sono asincroni per impostazione predefinita: l'utente può continuare a interagire con la vista mentre la richiesta al server è in corso.
    • eventi che modificano la vista visualizzata senza scambiare dati con il server, come trascinare un elemento da una struttura ad albero e rilasciarlo in un elenco. Questo tipo di evento viene gestito interamente a livello locale all'interno del browser.

Un'applicazione Flex viene spesso eseguita come segue:

  • in [1], viene richiesta una pagina HTML
  • in [2], viene inviata. Essa include un file binario SWF (ShockWave Flash) contenente l’intera applicazione Flex: tutte le viste e il relativo codice di gestione degli eventi. Questo file verrà eseguito dal plugin Flash Player del browser.
  • Il client Flex viene eseguito localmente nel browser, tranne quando necessita di dati esterni. In tal caso, li richiede al server [3]. Li riceve in [4] in vari formati: XML o binario. L'applicazione interrogata sul server web può essere scritta in qualsiasi linguaggio. Conta solo il formato della risposta.

Abbiamo descritto l'architettura di esecuzione di un'applicazione Flex in modo che il lettore possa comprendere la differenza tra questa e quella di un'applicazione web tradizionale, in cui le pagine non incorporano codice (JavaScript, Flex, Silverlight, ecc.) che il browser dovrebbe eseguire. In quest'ultimo caso, il browser è passivo: si limita a visualizzare le pagine HTML costruite sul server web che gliele invia.

14.1. Architettura dell'applicazione client/server

L'architettura client/server implementata qui è simile 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 dall'IDE Flex Builder. Va notato che questa architettura include due server web non mostrati:

  • 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 client Flex 3

Realizziamo il client Flex utilizzando l'IDE Flex Builder 3:

  • in Flex Builder 3, creiamo un nuovo progetto in [1]
  • gli diamo un nome in [2] e specifichiamo in [3] in quale cartella generarlo
  • in [4], diamo un nome all'applicazione principale (quella che verrà eseguita)
  • in [5], il progetto una volta generato
  • in [6], il file MXML principale dell’applicazione
  • Un file MXML contiene una vista e il codice per la gestione dei suoi eventi. La scheda [Source] [7] consente di accedere al file MXML. Qui troverete i tag <mx> che descrivono la vista, oltre al codice ActionScript.
  • La vista può essere costruita graficamente utilizzando la scheda [Design] [8]. I tag MXML che descrivono la vista vengono quindi generati automaticamente nella scheda [Source]. È vero anche il contrario: i tag MXML aggiunti direttamente nella scheda [Source] si riflettono graficamente nella scheda [Design].

14.3. Vista n. 1

Costruiremo gradualmente un'interfaccia web simile a quella della Versione 1 (vedi Sezione 4). Per prima cosa, costruiremo la seguente interfaccia:

  • in [1], la vista quando la connessione al servizio web ha avuto esito positivo. Il menu a tendina dei dipendenti viene quindi popolato.
  • in [2], la schermata visualizzata quando la connessione al servizio web non va a buon fine. Viene quindi visualizzato un messaggio di errore.

Il file principale del client [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[
...    
            // données
            [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 sua vista (righe 4–25)
  • i gestori di eventi ActionScript all'interno del tag <mx:Script> (righe 31–42)
  • la definizione del servizio web remoto (righe 27–29)

Iniziamo con alcune osservazioni sulla definizione dell'applicazione stessa e sulla descrizione della sua vista:

  • Righe 2–3: definiscono:
    • il layout dei componenti all'interno del contenitore della vista. L'attributo layout="vertical" indica che i componenti saranno disposti uno sotto l'altro.
    • il metodo da eseguire quando la vista è stata istanziata, ovvero quando tutti i suoi componenti sono stati istanziati. L'attributo creationComplete="init();" indica che deve essere eseguito il metodo init alla riga 38. creationComplete è uno degli eventi che la classe Application può emettere.
  • Le righe 4–25 definiscono i componenti della vista
  • Righe 4–25: un contenitore verticale: i componenti saranno posizionati uno sotto l'altro
  • Riga 5: definisce un componente di testo
  • Righe 6–23: un contenitore orizzontale: i componenti saranno posizionati orizzontalmente al suo interno.
  • Righe 7–10: un contenitore verticale che conterrà testo e un elenco a discesa
  • riga 8: il testo
  • riga 9: l'elenco a discesa in cui verrà inserito l'elenco dei dipendenti. Il tag dataProvider="{employees}" specifica l'origine dati che deve popolare l'elenco. In questo caso, l'elenco verrà popolato con l'oggetto employees definito alla riga 36. Per poter scrivere dataProvider="{employees}", il campo employees deve avere l'attributo [Bindable] (riga 35). Questo attributo consente di fare riferimento a una variabile ActionScript al di fuori del tag <mx:Script>. Il campo employees è di tipo ArrayCollection, un tipo ActionScript che consente di memorizzare elenchi di oggetti, in questo caso un elenco di oggetti di tipo Employee.
  • Righe 11–14: un contenitore verticale che conterrà del testo e un campo di immissione
  • riga 12: il testo
  • Riga 13: il campo di immissione per le ore lavorate.
  • righe 15-18: un contenitore verticale che conterrà del testo e un contatore
  • riga 16: il testo
  • riga 17: il contatore per l'inserimento dei giorni lavorati
  • Righe 19–22: un contenitore verticale che conterrà del testo e un pulsante che attiverà il calcolo dello stipendio per la 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'area di testo in un componente 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 vista nella scheda [Design]:

  • [1]: generato dal componente Label alla riga 5
  • [2]: è stato generato dal componente ComboBox alla riga 9
  • [3]: è stato generato dal componente TextInput alla riga 13
  • [4]: è stato generato dal componente NumericStepper alla riga 17
  • [5]: è stato generato dal componente Button alla riga 21
  • [6]: è stato generato dal componente TextArea alla riga 24

Ora esaminiamo 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 l'identificatore "pam" (attributo id)
  • riga 2: l'URI del file WSDL del servizio web (vedere la sezione 9.2)
  • riga 3: il metodo da eseguire in caso di errore durante la comunicazione con il servizio web: il metodo wsFault.
  • riga 4: richiede la visualizzazione di 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 GetAllIdentitiesEmployees.
  • riga 7: il metodo da eseguire quando la chiamata a questo metodo si conclude con successo, 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 per l'operazione GetAllEmployeeIDs. Sappiamo che questo metodo non richiede alcun parametro. Pertanto, lasciamo vuoto il tag <mx:request>.

Esaminiamo ora il codice ActionScript collegato al servizio web:


<mx:Script>
        <![CDATA[
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
 
            // données
            [Bindable]
            private var employes : ArrayCollection;
 
            private function init():void{
                // on note les coordonnées de la zone de message
                msgHeight=msg.height;
                msgWidth=msg.width;
                // on cache la zone de message
                hideMsg();
                // requête au service web distant pour avoir la liste simplifiée des employés
                pam.GetAllIdentitesEmployes.send();
            }
 
            private function wsFault(event:Event):void{
                    // on signale l'erreur
                    msg.text="Service distant indisponible";
                    showMsg();
            }
 
            private function loadEmployesCompleted(event:ResultEvent):void{
                // remplissage combo des employés
                employes=event.result as ArrayCollection;
            }
 
            private function displayEmploye(employe:Object):String{
                // identité d'un employé
                return employe.Prenom + " " + employe.Nom;
            }
 
            private function loadEmployesFault(event:FaultEvent):void{
                // affichage msg d'erreur
                msg.text=event.fault.message;
                // formulaire
                showMsg();
            }
 
    // gestion des blocs
        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é abbiamo scritto:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
    creationComplete="init()">
  • righe 13-14: memorizziamo l'altezza e la larghezza dell'area dei messaggi. Utilizziamo due metodi, hideMsg (righe 48-51) e showMsg (righe 53-56), per nascondere o mostrare l'area dei messaggi, rispettivamente, 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 ripristinando l'altezza e la larghezza memorizzate nel metodo init.
  • Riga 16: L'area dei messaggi è nascosta. Inizialmente, non si verifica 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 fallisce.
  • Riga 27: il metodo loadEmployesCompleted, che viene eseguito se la chiamata al servizio web alla riga 18 va a buon fine.
  • Riga 29: sappiamo che il servizio web restituisce una risposta XML. È utile fare riferimento a questa per comprendere il codice ActionScript:
  • in [1], la pagina del servizio web [Service.asmx]
  • in [2], il link alla pagina di test per il metodo [GetAllIdentitesEmployes]
  • in [3], viene eseguito il test. Non sono previsti parametri.
  • In [4]: La risposta XML contiene un array di dipendenti. Per ogni dipendente, sono presenti cinque informazioni racchiuse nei tag <Id>, <Version>, <SS>, <LastName> e <FirstName>. Se la risposta XML è memorizzata in un array `employees` di tipo `ArrayCollection`:
    • employees.getItemAt(i): è l'i-esimo elemento dell'array
    • employees.getItemAt(i).SS: è il numero di previdenza sociale di questo dipendente.
    • employees.getItemAt(i).LastName: è il cognome di quel dipendente
    • ...

Torniamo al codice ActionScript:

  • riga 29: event.result rappresenta la risposta XML dal servizio web. Il metodo GetAllIdentitesEmployes restituisce un array di dipendenti. event.result rappresenta questo array di dipendenti. È memorizzato in una variabile di tipo ArrayCollection, un tipo che generalmente rappresenta una raccolta di oggetti. Questa variabile, denominata employees, è dichiarata alla riga 9. Ricordiamo che questa variabile è l'origine dati per la casella combinata dei dipendenti:

<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>

Per ogni dipendente presente nella sua origine dati, la casella combinata chiamerà il metodo displayEmploye (attributo labelFunction) per visualizzare il dipendente. Le righe 32-34 mostrano che questo metodo visualizza il nome e il cognome del dipendente.

  • Riga 37: il metodo loadEmployesFault, che viene eseguito se la chiamata al servizio web alla riga 18 fallisce. event.fault.message è il messaggio di errore restituito dal servizio web.
  • Riga 39: Questo messaggio di errore viene inserito nella finestra di messaggio
  • Riga 41: Viene visualizzata la finestra di messaggio.

Una volta compilata l'applicazione, il codice eseguibile si trova nella cartella [bin-debug] del progetto Flex:

Come indicato sopra,

  • il file [main.html] rappresenta il file HTML che il browser richiederà al server web per ottenere il client Flex
  • il file [main.swf] è il binario del client Flex che verrà incorporato nella pagina HTML inviata al browser e quindi eseguito dal plugin Flash Player del browser.

Siamo pronti per eseguire il client Flex. Per prima cosa, dobbiamo configurare l'ambiente di runtime richiesto. Torniamo all'architettura client/server che abbiamo testato:

Lato server:

  • Avvia il servizio web ASP.NET [S]

Lato client:

  • Avvia il server Apache che servirà l'applicazione Flex.

Qui stiamo utilizzando lo strumento Wamp. Con questo strumento, possiamo assegnare un alias alla cartella [bin-debug] del progetto Flex.

  • L'icona di Wamp si trova nella parte inferiore dello schermo [1]
  • Fare clic con il tasto sinistro del mouse sull'icona di Wamp, selezionare l'opzione Apache [2] / Directory alias [3, 4]
  • Seleziona l'opzione [5]: Aggiungi un alias
  • In [6], assegnare un alias (un nome qualsiasi) all'applicazione web che verrà eseguita
  • In [7], specifica la radice dell'applicazione web che utilizzerà questo alias: si tratta della cartella [bin-debug] del progetto Flex che abbiamo appena creato.

Esaminiamo la struttura della cartella [bin-debug] nel progetto Flex:

Il file [main.html] è il file HTML dell'applicazione Flex. Grazie all'alias appena creato per la cartella [bin-debug], questo file sarà accessibile tramite l'URL [http://localhost/pam-v10-flex-client-webservice/main.html]. Accediamo a questo URL in un browser con il 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 quando il servizio web è inattivo

Potresti essere curioso di vedere il codice sorgente della pagina HTML ricevuta:

<!-- saved from url=(0014)about:internet -->
<html lang="en">

<!-- 
Smart developers always View Source. 

This application was built using Adobe Flex, an open source framework
for building rich Internet applications that get delivered via the
Flash Player or to desktops via Adobe AIR. 

Learn more about Flex at http://flex.org 
// -->

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<!--  BEGIN Browser History required section -->
<link rel="stylesheet" type="text/css" href="history/history.css" />
<!--  END Browser History required section -->

<title></title>
....
</head>

<body scroll="no">
...
<noscript>
        <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
                        id="main" width="100%" height="100%"
                        codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
                        <param name="movie" value="main.swf" />
                        <param name="quality" value="high" />
                        <param name="bgcolor" value="#869ca7" />
                        <param name="allowScriptAccess" value="sameDomain" />
                        <embed src="main.swf" quality="high" bgcolor="#869ca7"
                                width="100%" height="100%" name="main" align="middle"
                                play="true"
                                loop="false"
                                quality="high"
                                allowScriptAccess="sameDomain"
                                type="application/x-shockwave-flash"
                                pluginspage="http://www.adobe.com/go/getflashplayer">
                        </embed>
        </object>
</noscript>
</body>
</html>
  • Il corpo della pagina inizia alla riga 25. Non contiene HTML standard, ma un oggetto (riga 28) di tipo "application/x-shockwave-flash" (riga 41). Si tratta del file [main.swf] (riga 31) che si trova nella cartella [bin-debug] del progetto Flex. È un file di grandi dimensioni: circa 600 KB per questo semplice esempio.

14.4. Vista n. 2

Aggiungeremo un nuovo contenitore VBox alla vista corrente:

  • In [4,5], abbiamo impostato [main2.mxml] come nuova applicazione predefinita. È questa che verrà ora compilata.
  • In [6], l'applicazione predefinita è indicata da un punto blu.

Il contenitore [1] visualizzerà le informazioni relative al dipendente selezionato nella casella combinata [2]. Duplichiamo [main.xml] come [main2.xml] [3] per creare la nuova vista. Ora lavoreremo con [main2.xml].

La modifica apportata al progetto precedente consiste nell'aggiunta del contenitore alla riga 26 sopra, che contiene il codice MXML per il contenitore [1] della vista. Gli assegniamo l'identificatore employe in modo da poterlo manipolare tramite codice. Questo contenitore deve poter essere nascosto o mostrato utilizzando la stessa tecnica usata in precedenza per l'area dei messaggi.

Torniamo al layout visivo della vista:

Identifichiamo i diversi contenitori per le nuove informazioni visualizzate:

  • V1: contenitore verticale per tutti i componenti: l'etichetta "Dipendente [1]" e i contenitori orizzontali [H1] e [H2]
  • H1: contenitore orizzontale per le informazioni relative a Cognome, Nome e Indirizzo
  • V2: contenitore verticale per l'etichetta "Cognome" e la visualizzazione del cognome del dipendente.
  • H2: contenitore orizzontale per le informazioni relative a Città, Codice postale e Indice

Il codice completo per il 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 brevemente il contenitore verticale che visualizza il nome del dipendente, ad esempio:

  • righe 4–9: il contenitore verticale
  • riga 5: l'etichetta "Nome"
  • 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 sui dipendenti. Il componente Testo non offre questa opzione (o forse non ho cercato abbastanza). È possibile impostare il colore di sfondo di un contenitore. Ecco perché è stato utilizzato qui.
  • Riga 7: il componente Testo che visualizzerà il nome del dipendente. Abbiamo impostato un'altezza e una larghezza minime per esso.

Useremo il contenitore "employee" per visualizzare le informazioni sul dipendente selezionate dall'utente dal menu a tendina dei dipendenti, indipendentemente dal pulsante [Salary], che verrà utilizzato in seguito per calcolare lo stipendio una volta inserite tutte le informazioni necessarie.

Per gestire la modifica della selezione nella casella combinata "dipendenti", il codice MXML viene modificato come segue:


<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>

L'evento change viene attivato dalla casella combinata quando l'utente modifica la propria selezione. Il gestore di questo evento sarà il metodo displayInfosEmploye.

Esaminiamo i metodi esposti dal servizio web remoto:


    // liste de toutes les identités des employés 
    public Employe[] GetAllIdentitesEmployes();
    // ------- le calcul du salaire 
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);

Qui, vogliamo visualizzare le informazioni (cognome, nome, ecc.) relative al dipendente selezionato nel menu a tendina. Il servizio web non espone un metodo per recuperare queste informazioni. Tuttavia, possiamo utilizzare il metodo GetSalary passando il numero di previdenza sociale del dipendente selezionato e 0 per le ore e i giorni lavorati. Verrà eseguito un calcolo dello stipendio ridondante, ma il metodo GetSalary restituirà un oggetto PayStub contenente le informazioni di cui abbiamo bisogno.

La dichiarazione del servizio web corrente 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 GetSalary del servizio web
  • riga 12: definisce il metodo da eseguire quando la chiamata al metodo GetSalaire ha esito positivo
  • 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>value1</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. Questi sono i nomi che devono essere utilizzati 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 denominata employees di tipo ArrayCollection.
    • employees.getItemAt(i): è il dipendente numero i nell'array
    • employees.getItemAt(i).SS: è il numero di previdenza sociale di questo dipendente.
    • cmbEmployees.selectedIndex: è l'indice dell'elemento selezionato nella casella combinata employees (cmbEmployees).

Nel codice sopra riportato, come facciamo a sapere che SS è il numero di previdenza sociale di un dipendente? Per rispondere a questa domanda, dobbiamo fare riferimento alla risposta inviata dal metodo GetAllIdentitiesEmployees:

  • in [1], la pagina del servizio web [Service.asmx]
  • in [2], il link alla pagina di test per il metodo [GetAllIdentitesEmployes]
  • in [3], viene eseguito il test. Non sono previsti parametri.
  • In [4]: La risposta XML contiene un array di dipendenti. Questo array viene memorizzato nella variabile `employees`. Come mostrato in [5], `SS` è effettivamente il tag utilizzato per memorizzare il numero di previdenza sociale.

Concludiamo la nostra 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 da una variabile hoursWorked
  • riga 6: il numero di giorni lavorati sarà fornito da una variabile daysWorked

Queste variabili devono essere dichiarate all'interno del tag <mx:Script> con l'attributo [Bindable], che consente loro di essere referenziate dai componenti MXML (righe 7–10 di seguito).


    <mx:Script>
        <![CDATA[
...
            // données
            [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 cambia come segue:


<mx:Script>
        <![CDATA[
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
 
            // données
            [Bindable]
            private var employes : ArrayCollection;
            [Bindable]
            private var heuresTravaillees:Number;
            [Bindable]
            private var joursDeTravail:int;
 
            private function init():void{
                // on note les hauteur / largeur de # blocs
                employeHeight=employe.height;
                employeWidth=employe.width;
                // on cache certains éléments
                hideEmploye();
...
            }
 
            private function displayInfosEmploye():void{
                // formulaire
                hideEmploye();
                // on calcule un salaire fictif
                heuresTravaillees=0;
                joursDeTravail=0;
                pam.GetSalaire.send();
            }
 
            private function getSalaireCompleted(event:ResultEvent):void{
    ...
            }
 
            private function getSalaireFault(event:FaultEvent):void{
    ...
            }
 
        // vues partielles -------------------------------------------------
        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 dei dipendenti in modo che possa essere ripristinato (righe 50–53) dopo essere stato nascosto (righe 45–48).
  • Riga 24: Il metodo displayInfosEmploye viene eseguito quando l'utente modifica la propria selezione nella casella combinata "employee".
  • Riga 26: Il contenitore employee viene nascosto se era precedentemente visibile
  • Riga 30: Il metodo GetSalary del servizio web viene chiamato in modo asincrono. Sappiamo 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 nella casella combinata dei dipendenti
  • riga 2: il metodo displayInfosEmploye assegna il valore 0 alla variabile hoursWorked (riga 28)
  • riga 3: il metodo displayInfosEmploye assegna il valore 0 alla variabile daysWorked (riga 29)

Il metodo GetSalaireCompleted viene eseguito se il metodo GetSalaire del servizio web viene completato con successo:


private function getSalaireCompleted(event:ResultEvent):void{
                // hide error msg
                hideMsg();
                // you receive a payslip
                var feuilleSalaire:Object=event.result;
                // display
                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: nascondiamo la finestra di messaggio nel caso in cui venga visualizzata.
  • Riga 5: recuperiamo la busta paga restituita dal metodo GetSalary

Per scoprire esattamente cosa restituisce il metodo GetSalaire, torniamo alla pagina del servizio web:

  • [1] è la pagina del servizio web [Service.asmx]
  • in [2], il link che porta alla pagina di test per il metodo [GetSalaire]
  • in [3], forniamo i parametri
  • in [4], l'XML risultante.

Torniamo al metodo getSalaireCompleted:


private function getSalaireCompleted(event:ResultEvent):void{
                // hide error msg
                hideMsg();
                // you receive a payslip
                var feuilleSalaire:Object=event.result;
                // display
                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: PayrollSheet = event.result rappresenta il feed XML [4] restituito dal metodo GetSalary. Da questo feed, possiamo vedere che:
    • payrollSheet.Employee è il flusso XML relativo a un dipendente
    • sheetSalary.Employee.Name è il nome di questo dipendente
    • ...
  • righe 7–12: il feed XML Payroll.Employee viene utilizzato per popolare i vari campi del contenitore del dipendente.
  • riga 13: viene visualizzato il contenitore del dipendente.

Il metodo getSalaireFault viene eseguito se il metodo GetSalaire del servizio web fallisce:


            private function getSalaireFault(event:FaultEvent):void{
                // error msg display
                msg.text=event.fault.message;
                // form
                showMsg();            
            }
  • Riga 3: il messaggio di errore event.fault.message viene inserito nella finestra di messaggio
  • riga 5: viene visualizzata la finestra di messaggio

Questo conclude le modifiche richieste per questa nuova versione. Una volta salvato, se la sintassi è corretta, la versione eseguibile viene generata nella cartella [bin-debug] del progetto:

 

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 in esecuzione
  • il server Apache deve essere in esecuzione per il client Flex

Supponendo che l'alias [pam-v10-flex-client-webservice] utilizzato nella versione precedente esista ancora, richiediamo l'URL [http://localhost/pam-v10-flex-client-webservice/main2.html] dal server Apache in un browser:

  • in [1], l'URL richiesto
  • in [2], il menu a tendina dei dipendenti
  • in [3], modificare la selezione nel menu a tendina per attivare l'evento di modifica
  • in [4], il risultato ottenuto: il profilo di Justine Laverti.

14.5. Vista n. 3

La vista 3 gestisce la convalida del modulo. Qui viene controllato solo il campo di immissione "txtHeuresTravaillees". Finché il modulo non è valido, 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 (vedi questo concetto nella sezione 14.4). Per prima cosa, 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. Al suo interno, potremmo verificare che il contenuto del campo di immissione "txtHeuresTravaillees" sia effettivamente un numero intero positivo. Procederemo 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> verifica che un altro componente contenga un numero intero o un numero reale.
  • riga 1: L'attributo id assegna un identificatore al componente.
  • riga 1: source è l'ID del componente che viene convalidato dal componente NumberValidator. In questo caso, viene convalidato il campo di immissione "txtHeuresTravaillees".
  • Riga 1: property è il nome della proprietà del componente sorgente che contiene il valore da convalidare. In definitiva, è il valore source.property che viene convalidato, in questo caso txtHeuresTravaillees.text.
  • Riga 2: precision imposta il numero massimo di cifre decimali consentite. precision=0 significa che il numero inserito deve essere un numero intero.
  • Riga 2: allowNegative specifica se i numeri negativi sono consentiti o meno
  • Riga 7: required specifica se l'inserimento è obbligatorio o meno.

Quando una condizione di convalida non è soddisfatta, viene visualizzato un messaggio di errore in un tooltip vicino al componente non valido. Per impostazione predefinita, questi messaggi sono in inglese. È possibile definire questi messaggi autonomamente:

  • (continua)
    • invalidCharError: il messaggio di errore quando il testo contiene un carattere che non può comparire 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 ma l'attributo allowNegative="false" è impostato
    • requiredFieldError: il messaggio di errore quando non è stato fornito alcun input anche se è impostato l'attributo requiredField="true"
    • invalidFormatCharsError: il messaggio di errore che compare quando il testo contiene caratteri non validi o ha 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 
        {                    
            // validate hours worked
            var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
            // successful validation?
            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, nella riga 4, la convalida ha avuto esito positivo, evt.type deve avere il valore ValidationResultEvent.VALID. In questo caso, il pulsante btnSalaire è abilitato; in caso contrario, è disabilitato.

Questo è sufficiente per verificare la validità delle ore lavorate.

In precedenza, la compilazione del progetto ha generato i file [main3.html] e [main3.swf]. Inseriamo l'URL [http://localhost/pam-v10-flex-client-webservice/main3.html] in un browser e verifichiamo la presenza di vari casi di errore:

  • un campo errato è contornato da una cornice rossa [1, 2, 3], mentre un campo corretto è contornato da una cornice blu [4].
  • In [4], si noti che il pulsante [Stipendio] è attivo perché il numero di ore lavorate è corretto.

14.6. Vista n. 4

La vista 4 completa il modulo di calcolo dello stipendio. Per farlo, duplichiamo [main3.xml] in [main4.xml] e ora lavoriamo con main4, che impostiamo come applicazione predefinita (vedere la sezione 14.4).

Le modifiche apportate in [main4.xml] [1] sono le seguenti:

  • è stato aggiunto un nuovo contenitore verticale alla vista [2] per visualizzare le componenti salariali del dipendente
  • viene aggiunto un componente per la formattazione dei valori monetari [3]
  • la visualizzazione delle componenti salariali è gestita dall'handler associato all'evento "click" del pulsante "btnSalaire".

La vista si evolve come segue:

Il nuovo contenitore segue lo stesso principio del precedente. Si tratta di un contenitore VBox verticale [V1] che contiene quattro contenitori HBox orizzontali [Hi]. I contenitori orizzontali da H1 a H3 sono costituiti da contenitori verticali contenenti due etichette, la seconda delle quali è a sua volta all'interno di un contenitore verticale per fornire un colore di sfondo.


Domanda 1: Scrivi il contenitore salary. D'ora in poi verrà indicato con il nome complements.



Domanda 2: Scrivi i metodi per nascondere/mostrare il contenitore "complements". Prendi spunto da quanto fatto in precedenza per il contenitore "employee".


Associamo 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{
                // form preparation
                affichageSalaire=true;
                msg.text="";                
                // salary calculation parameters
                heuresTravaillees=Number(txtHeuresTravaillees.text);
                joursDeTravail=int(joursTravailles.value);
                // the salary is requested from the web service
                pam.GetSalaire.send();
}
  • Riga 3: La variabile booleana displaySalary viene utilizzata per indicare se visualizzare o meno il contenitore dei complementi, che mostra i dettagli dello stipendio. Il metodo getSalaryCompleted viene eseguito in due casi:
    • quando si cambia dipendente nel menu a tendina dei dipendenti per visualizzarne le informazioni senza lo stipendio. In questo caso, viene impostato salaryDisplay=false.
    • calcolo dello stipendio
  • Riga 6: Il testo nel campo di immissione txtHeuresTravaillees viene convertito in un numero reale.
  • Riga 7: Il valore del contatore daysWorked viene convertito in un numero intero.
  • Riga 9: viene chiamato il metodo remoto GetSalary. Si noti che questo metodo richiede tre parametri, inclusi i parametri hoursWorked e daysWorked inizializzati alle righe 6 e 7. Si noti inoltre che se la chiamata asincrona al metodo GetSalary:
    • ha esito positivo, verrà chiamato il metodo getSalaireCompleted
    • fallisce, verrà chiamato il metodo getSalaireFault

Domanda 3: Completa il metodo getSalaireCompleted in modo che visualizzi lo stipendio del dipendente se è stato cliccato il pulsante btnSalaire.


Attualmente, le voci relative allo stipendio vengono visualizzate senza il simbolo dell'euro. Puoi inserirlo nel codice oppure utilizzare un formattatore. Questo è l'approccio che stiamo adottando ora. Il formattatore sarà il seguente:


    <mx:CurrencyFormatter id="eurosFormatter" precision="2"
        currencySymbol="" useNegativeSign="true"
alignSymbol="right"/>
  • Riga 1: id è l'identificatore del formattatore, precision è il numero di cifre decimali da mantenere.
  • Riga 2: currencySymbol è il simbolo della valuta da utilizzare. useNegativeSign indica se utilizzare o meno il segno meno per i valori negativi.
  • Riga 3: alignSymbol specifica dove posizionare il simbolo della valuta rispetto al numero.

Questo formattatore viene utilizzato nel codice dello script come segue:

                    lblSH.text=eurosFormatter.format(feuilleSalaire.Indemnites.BaseHeure);
  • eurosFormatter è l'ID del formattatore da utilizzare
  • format è il metodo da chiamare per formattare un numero. Restituisce una stringa.
  • feuilleSalaire.Indemnites.BaseHeure è il numero da formattare.
  • lblSH è il nome di un componente Text.

Domanda 4: Modifica il metodo getSalaireCompleted in modo che utilizzi il formattatore di valuta.