Skip to content

14. Aplikacja [SimuPaie] – wersja 10 – klient Flex serwisu internetowego ASP.NET

Przedstawiamy teraz klienta Flex dla usługi internetowej ASP.NET w wersji 5. Wykorzystywanym narzędziem jest Flex Builder 3 (IDE). Wersję demonstracyjną tego produktu można pobrać pod adresem URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 jest środowiskiem Eclipse. Ponadto do uruchamiania klienta Flex używamy serwera WWW Apache z pakietu Wamp [http://www.wampserver.com/]. Nadaje się do tego dowolny serwer Apache. Przeglądarka wyświetlająca klienta Flex musi posiadać wtyczkę Flash Player w wersji co najmniej 9.

Aplikacje Flex charakteryzują się tym, że działają w ramach wtyczki Flash Player przeglądarki. Pod tym względem przypominają aplikacje Ajax, które osadzają w stronach wysyłanych do przeglądarki skrypty JavaScript, które są następnie wykonywane w przeglądarce. Aplikacja Flex nie jest aplikacją internetową w potocznym rozumieniu tego terminu: jest to aplikacja kliencka korzystająca z usług dostarczanych przez serwery internetowe. Pod tym względem przypomina aplikację biurkową, która byłaby klientem tych samych usług. Różni się jednak pod jednym względem: jest ona początkowo pobierana z serwera internetowego do przeglądarki wyposażonej we wtyczkę Flash Player zdolną do jej uruchomienia.

Podobnie jak aplikacja biurkowa, aplikacja Flex składa się głównie z dwóch elementów:

  • część prezentacyjna: widoki wyświetlane w przeglądarce. Widoki te charakteryzują się bogactwem funkcji typowym dla okien aplikacji biurowych. Widok jest opisany za pomocą języka znaczników o nazwie MXML.
  • część kodu, która zarządza głównie zdarzeniami wywołanymi przez działania użytkownika na widoku. Kod ten można napisać również w języku MXML lub w języku obiektowym o nazwie ActionScript. Należy rozróżnić dwa rodzaje zdarzeń:
    • zdarzenie wymagające wymiany danych z serwerem internetowym: wypełnienie listy danymi dostarczonymi przez aplikację internetową, wysłanie danych z formularza na serwer itp. Flex udostępnia szereg metod umożliwiających komunikację z serwerem w sposób przezroczysty dla programisty. Metody te są domyślnie asynchroniczne: użytkownik może kontynuować interakcję z widokiem podczas wysyłania żądania do serwera.
    • zdarzenie, które zmienia wyświetlany widok bez wymiany danych z serwerem, na przykład przeciągnięcie elementu z drzewa i upuszczenie go na liście. Ten rodzaj zdarzenia jest w całości przetwarzany lokalnie w przeglądarce.

Aplikacja Flex jest często uruchamiana w następujący sposób:

  • w , żądana jest strona HTML
  • w [2], a następnie jest wysyłana. Zawiera ona plik binarny SWF (ShockWave Flash) zawierający całą aplikację Flex: wszystkie widoki oraz kod zarządzający zdarzeniami w tych widokach. Plik ten zostanie uruchomiony przez wtyczkę Flash Player przeglądarki.
  • Klient Flex działa lokalnie w przeglądarce, z wyjątkiem sytuacji, gdy potrzebuje danych zewnętrznych. W takim przypadku wysyła zapytanie do serwera [3]. Otrzymuje je w formacie [4] w różnych formatach: XML lub binarnym. Aplikacja wywoływana na serwerze WWW może być napisana w dowolnym języku programowania. Liczy się wyłącznie format odpowiedzi.

Opisaliśmy architekturę działania aplikacji Flex, aby czytelnik mógł dostrzec różnicę między nią a klasyczną aplikacją internetową, w której strony nie zawierają kodu (JavaScript, Flex, Silverlight itp.), który byłby wykonywany przez przeglądarkę. W tym ostatnim przypadku przeglądarka pełni rolę bierną: po prostu wyświetla strony HTML utworzone na serwerze internetowym, który je do niej przesyła.

14.1. Architektura aplikacji klient-serwer

Wdrożona architektura klient-serwer jest analogiczna do architektury z wersji 6 i 8:

W [1] warstwa internetowa ASP.NET została zastąpiona warstwą internetową Flex napisaną w MXML i ActionScript. Klient [C] zostanie wygenerowany przez program IDE Flex Builder. Należy tutaj pamiętać, że architektura ta obejmuje dwa serwery internetowe, które nie zostały przedstawione:

  • serwer internetowy ASP.NET, na którym działa usługa internetowa [S]
  • serwer WWW APACHE, na którym działa klient WWW [1]

14.2. Projekt klienta w Flex 3

Tworzymy klienta Flex przy użyciu programu IDE Flex Builder 3:

  • w programie Flex Builder 3 tworzymy nowy projekt o nazwie [1]
  • nadajemy mu nazwę [2] i określamy w [3], w którym folderze ma zostać wygenerowany
  • w [4] nadajemy nazwę głównej aplikacji (tej, która będzie uruchamiana)
  • w polu [5] określa się, że po wygenerowaniu projekt
  • w [6] – główny plik aplikacji MXML
  • plik MXML zawiera widok oraz kod obsługi zdarzeń tego widoku. Zakładka [Source] [7] zapewnia dostęp do pliku MXML. Znajdują się w nim znaczniki <mx> opisujące widok, a także kod ActionScript.
  • Widok można utworzyć graficznie, korzystając z zakładki [Design] [8]. Tagi MXML opisujące widok są wówczas generowane automatycznie w zakładce [Source]. Działa to również w drugą stronę: tagi MXML dodane bezpośrednio w zakładce [Source] są odzwierciedlane graficznie w zakładce [Design].

14.3. Widok nr 1

Będziemy stopniowo tworzyć interfejs internetowy analogiczny do tego z wersji 1 (patrz akapit 4). Najpierw tworzymy następujący interfejs:

  • w [1] – widok po nawiązaniu połączenia z serwisem internetowym. Lista pracowników jest wtedy wypełniona.
  • w pliku [2] – widok po nieudanej próbie połączenia z serwisem internetowym. Wyświetlany jest wówczas komunikat o błędzie.

Główny plik klienta [main.xml] wygląda następująco:


<?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[
...    
            // dane
            [Bindable]
            private var employes : ArrayCollection;

            private function init():void{
...
            }
        ]]>
    </mx:Script>
</mx:Application>

W tym kodzie należy rozróżnić różne elementy:

  • definicję aplikacji (wiersze 2–3)
  • opis widoku aplikacji (wiersze 4–25)
  • procedury obsługi zdarzeń w języku ActionScript wewnątrz tagu <mx:Script> (wiersze 31–42)
  • definicję zdalnej usługi internetowej (wiersze 27–29)

Na początek omówmy definicję samej aplikacji oraz opis jej widoku:

  • wiersze 2–3: określają:
    • sposób rozmieszczenia komponentów w kontenerze widoku. Atrybut layout="vertical" wskazuje, że komponenty będą umieszczone jeden pod drugim.
    • metodę, która ma zostać wykonana po utworzeniu instancji widoku, c.a.d. moment, w którym wszystkie jego komponenty zostaną zainicjowane. Atrybut creationComplete="init();" oznacza, że należy wykonać metodę init z linii 38. creationComplete jest jednym ze zdarzeń, które może wygenerować klasa Application.
  • wiersze 4–25 definiują komponenty widoku
  • wiersze 4–25: kontener pionowy: komponenty będą w nim umieszczone jeden pod drugim
  • wiersz 5: definiuje tekst
  • wiersze 6–23: kontener poziomy: komponenty będą w nim rozmieszczone poziomo
  • wiersze 7–10: kontener pionowy, który będzie zawierał tekst i listę rozwijaną
  • wiersz 8: tekst
  • wiersz 9: lista rozwijana, w której umieścimy listę pracowników. Tag dataProvider="{employes}" wskazuje źródło danych, które mają wypełnić listę. W tym przypadku lista zostanie wypełniona obiektem employes zdefiniowanym w wierszu 36. Aby móc wpisać dataProvider="{employes}", pole employes musi posiadać atrybut [Bindable] (wiersz 35). Atrybut ten umożliwia odwołanie się do zmiennej ActionScript poza tagiem <mx:Script>. Pole employes jest typu ArrayCollection, czyli typu ActionScript, który umożliwia przechowywanie list obiektów, w tym przypadku listy obiektów typu Employe.
  • wiersze 11–14: pionowy kontener, który będzie zawierał tekst i pole wprowadzania danych
  • wiersz 12: tekst
  • wiersz 13: pole wprowadzania przepracowanych godzin.
  • wiersze 15–18: pionowy kontener, który będzie zawierał tekst i licznik
  • wiersz 16: tekst
  • wiersz 17: licznik, który umożliwi wprowadzanie liczby przepracowanych dni.
  • wiersze 19–22: pionowy kontener, w którym znajdzie się tekst oraz przycisk uruchamiający obliczenie wynagrodzenia osoby wybranej z listy rozwijanej.
  • wiersz 20: tekst
  • wiersz 21: przycisk.
  • wiersz 23: koniec kontenera poziomego rozpoczętego w wierszu 6
  • wiersz 24: pole tekstowe w komponencie typu TextArea. Będzie ono wyświetlać komunikaty o błędach.
  • wiersz 25: koniec kontenera pionowego rozpoczętego w wierszu 4

Wiersze 4–25 generują następujący widok w zakładce [Design]:

  • [1]: został wygenerowany przez komponent Label z wiersza 5
  • [2]: został wygenerowany przez komponent ComboBox z wiersza 9
  • [3]: został wygenerowany przez komponent TextInput z wiersza 13
  • [4]: został wygenerowany przez komponent NumericStepper z linii 17
  • [5]: został wygenerowany przez komponent Button z wiersza 21
  • [6]: został wygenerowany przez komponent TextArea z wiersza 24

Przyjrzyjmy się teraz deklaracji zdalnej usługi internetowej:


<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>

  • wiersz 1: serwis internetowy ma identyfikator pam (atrybut id)
  • wiersz 2: identyfikator URI pliku WSDL serwisu internetowego (patrz punkt 9.2)
  • wiersz 3: metoda, która ma zostać wykonana w przypadku wystąpienia błędu podczas wymiany danych z usługą internetową: metoda wsFault.
  • wiersz 4: żądanie wyświetlenia wskaźnika informującego użytkownika, że trwa wymiana danych z serwisem internetowym.
  • wiersze 5–10: jedna z operacji oferowanych przez zdalną usługę internetową. W tym przypadku metoda GetAllIdentitesEmployes.
  • wiersz 7: metoda do wykonania, gdy wywołanie tej metody zakończy się pomyślnie, c.a.d. Gdy serwis internetowy poprawnie zwróci listę pracowników
  • wiersz 8: metoda do wykonania, gdy wywołanie tej metody zakończy się błędem.
  • wiersz 9: parametry operacji GetAllIdentitesEmployes. Wiemy, że ta metoda nie oczekuje żadnych parametrów. Dlatego pozostawiamy tag <mx:request> pusty.

Przyjrzyjmy się teraz kodowi ActionScript powiązanemu z usługą internetową:


<mx:Script>
        <![CDATA[
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
                        
            // dane
            [Bindable]
            private var employes : ArrayCollection;
    
            private function init():void{
                // zapisywanie współrzędnych pola komunikatu
                msgHeight=msg.height;
                msgWidth=msg.width;
                // ukrywanie pola komunikatu
                hideMsg();
                // wysyłamy żądanie do zdalnej usługi internetowej w celu uzyskania uproszczonej listy pracowników
                pam.GetAllIdentitesEmployes.send();
            }
            
            private function wsFault(event:Event):void{
                    // zgłaszanie błędu
                    msg.text="Service distant indisponible";
                    showMsg();
            }
            
            private function loadEmployesCompleted(event:ResultEvent):void{
                // wypełnianie listy rozwijanej pracowników
                employes=event.result as ArrayCollection;
            }
            
            private function displayEmploye(employe:Object):String{
                // identyfikator pracownika
                return employe.Prenom + " " + employe.Nom;
            }
            
            private function loadEmployesFault(event:FaultEvent):void{
                // wyświetlenie komunikatu o błędzie
                msg.text=event.fault.message;
                // formularz
                showMsg();
            }

    // zarządzanie blokami
        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>
  • wiersz 11: metoda init jest wykonywana przy uruchomieniu aplikacji, ponieważ napisaliśmy:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
    creationComplete="init()">
  • wiersze 13–14: zapisujemy wysokość i szerokość obszaru komunikatu. Wykorzystuje się dwie metody: hideMsg (wiersze 48–51) oraz showMsg (wiersze 53–56) do odpowiednio ukrywania lub wyświetlania obszaru komunikatu w zależności od tego, czy wystąpił błąd, czy nie. Metoda hideMsg ukrywa obszar komunikatu, ustawiając jego wysokość i szerokość na 0. Metoda showMsg wyświetla obszar komunikatu, przywracając mu wysokość i szerokość zapisane w metodzie init.
  • wiersz 16: ukrywa się obszar komunikatu. Na początku nie ma błędu.
  • wiersz 18: wywoływana jest metoda GetAllIdentitesEmploye (wiersz 6 serwisu internetowego) serwisu internetowego pam (wiersz 1 serwisu internetowego). Wywołanie ma charakter asynchroniczny. Wiersz 7 usługi internetowej wskazuje, że metoda loadEmployesCompleted zostanie wykonana, jeśli to wywołanie asynchroniczne zakończy się pomyślnie. Wiersz 8 usługi internetowej wskazuje, że metoda loadEmployesFault zostanie wykonana, jeśli to wywołanie asynchroniczne zakończy się niepomyślnie.
  • wiersz 27: metoda loadEmployesCompleted, która jest wykonywana, jeśli wywołanie serwisu internetowego z wiersza 18 zakończy się pomyślnie.
  • wiersz 29: wiadomo, że serwis internetowy zwraca odpowiedź XML. Warto powrócić do niej, aby zrozumieć kod ActionScript:
  • w [1], strona serwisu internetowego [Service.asmx]
  • w [2], link do strony testowej metody [GetAllIdentitesEmployes]
  • w [3] – test został przeprowadzony. Nie oczekuje się żadnych parametrów.
  • w [4]: odpowiedź XML zawiera tabelę pracowników. Dla każdego z nich mamy pięć informacji zawartych w tagach <Id>, <Version>, <SS>, <Nom>, <Prenom>. Jeśli odpowiedź XML zostanie umieszczona w tablicy employes typu ArrayCollection:
    • employes.getItemAt(i): jest elementem nr i tablicy
    • employes.getItemAt(i).SS: to numer ubezpieczenia społecznego tego pracownika.
    • employes.getItemAt(i).Nazwisko: to nazwisko tego pracownika
    • ...

Wróćmy do kodu ActionScript:

  • wiersz 29: event.result reprezentuje odpowiedź XML z serwisu internetowego. Metoda GetAllIdentitesEmployes zwraca tablicę pracowników. event.result reprezentuje tę tablicę pracowników. Jest ona umieszczana w zmiennej typu ArrayCollection, który ogólnie reprezentuje zbiór obiektów. Ta zmienna o nazwie employes została zadeklarowana w wierszu 9. Przypomnijmy, że zmienna ta stanowi źródło danych dla listy rozwijanej pracowników:

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

Dla każdego pracownika z tego źródła danych lista rozwijana wywoła metodę displayEmploye (atrybut labelFunction) w celu wyświetlenia pracownika. W wierszach 32–34 widać, że metoda ta wyświetla imię i nazwisko pracownika.

  • wiersz 37: metoda loadEmployesFault, która jest uruchamiana, jeśli wywołanie serwisu internetowego z wiersza 18 zakończy się niepowodzeniem. event.fault.message to komunikat o błędzie zwrócony przez serwis internetowy.
  • wiersz 39: ten komunikat o błędzie jest umieszczany w polu komunikatu
  • wiersz 41: wyświetlane jest pole komunikatu.

Po skompilowaniu aplikacji jej kod wykonywalny znajduje się w folderze [bin-debug] projektu Flex:

Powyżej

  • plik [main.html] odpowiada plikowi HTML, o który przeglądarka zwróci się do serwera WWW w celu uzyskania klienta Flex
  • plik [main.swf] to plik binarny klienta Flex, który zostanie osadzony w stronie HTML wysłanej do przeglądarki, a następnie uruchomiony przez wtyczkę Flash Player tej przeglądarki.

Jesteśmy gotowi do uruchomienia klienta Flex. Najpierw musimy jednak skonfigurować niezbędne środowisko uruchomieniowe. Wróćmy do testowanej architektury klient-serwer:

Po stronie serwera:

  • uruchom usługę internetową ASP.NET [S]

Po stronie klienta:

  • uruchom serwer Apache, do którego zostanie skierowane żądanie dotyczące aplikacji Flex.

W tym przypadku korzystamy z narzędzia Wamp. Za pomocą tego narzędzia możemy przypisać alias do folderu [bin-debug] projektu Flex.

  • Ikona Wamp znajduje się na dole ekranu [1]
  • klikając lewym przyciskiem myszy na ikonę Wamp, wybierz opcję Apache [2] / Alias Directories [3, 4]
  • wybrać opcję [5]: Dodaj alias
  • w [6] nadać alias (dowolną nazwę) aplikacji internetowej, która ma zostać uruchomiona
  • w [7] należy wskazać katalog główny aplikacji internetowej, która będzie nosiła ten alias: jest to folder [bin-debug] projektu Flex, który właśnie utworzyliśmy.

Przypomnijmy strukturę folderu [bin-debug] projektu Flex:

Plik [main.html] to plik HTML aplikacji Flex. Dzięki aliasowi, który właśnie utworzyliśmy dla folderu [bin-debug], plik ten będzie dostępny poprzez URL i [http://localhost/pam-v10-flex-client-webservice/main.html]. Otwieramy go w przeglądarce wyposażonej we wtyczkę Flash Player w wersji 9 lub nowszej:

  • w [1], plik URL z aplikacji Flex
  • w [2] – lista pracowników, gdy wszystko działa poprawnie
  • na [3] – wynik uzyskany po zatrzymaniu serwisu internetowego

Być może zechcesz wyświetlić kod źródłowy otrzymanej strony HTML:

<!-- zapisano z adresu 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 sekcja wymagająca historii przeglądarki -->
<link rel="stylesheet" type="text/css" href="history/history.css" />
<!--   END Sekcja wymagająca historii przeglądarki -->

<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>
  • Treść strony zaczyna się w wierszu 25. Nie zawiera ona klasycznego kodu HTML, lecz obiekt (wiersz 28) typu „application/x-shockwave-flash” (wiersz 41). Jest to plik [main.swf] (wiersz 31), który można zobaczyć w folderze [bin-debug] projektu Flex. Jest to plik o znacznej wielkości: około 600 K dla tego prostego przykładu.

14.4. Widok nr 2

Dodamy nowy kontener typu VBox do bieżącego widoku:

  • w [4,5], ustawiamy [main2.mxml] jako nową aplikację domyślną. To właśnie ona będzie odtąd kompilowana.
  • W [6] domyślna aplikacja jest oznaczona niebieską kropką.

Kontener [1] wyświetli informacje dotyczące pracownika wybranego z listy rozwijanej [2]. Duplikujemy [main.xml] do [main2.xml] i [3], aby utworzyć nowy widok. Od tej pory będziemy pracować z [main2.xml].

Zmianą wprowadzoną w poprzednim projekcie jest dodanie kontenera z wiersza 26 powyżej, który zawiera kod MXML z kontenera [1] widoku. Nadajemy mu identyfikator employe, aby móc nim sterować za pomocą kodu. Kontener ten musi bowiem dać się ukrywać i wyświetlać przy użyciu tej samej techniki, co wcześniej w przypadku obszaru komunikatu.

Wróćmy do interfejsu widoku:

Zaznaczmy różne kontenery nowych wyświetlanych informacji:

  • V1: pionowy kontener zawierający wszystkie komponenty: etykieta Employé, [1] oraz poziome kontenery [H1] i [H2]
  • H1: poziomy kontener na informacje Nom, Prénom, Adresse
  • V2: pionowy element zawierający opis Nom oraz nazwisko pracownika.
  • H2: poziomy kontener na informacje Ville, kod pocztowy, Indice

Pełny kod kontenera „employe” wygląda następująco:


<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>

Kod jest zrozumiały sam w sobie. Wyjaśnijmy pokrótce działanie kontenera pionowego wyświetlającego na przykład nazwisko pracownika:

  • wiersze 4–9: kontener pionowy
  • wiersz 5: etykieta Nom
  • wiersze 6–8: kontener pionowy, który wyświetli imię i nazwisko pracownika (wiersz 7). Chcemy nadać inny kolor tła polom wyświetlającym informacje o pracowniku. Komponent Text nie oferuje takiej możliwości (a może po prostu źle szukałem). Można ustawić kolor tła kontenera. Dlatego właśnie został on tutaj użyty.
  • wiersz 7: komponent Text, który wyświetli imię i nazwisko pracownika. Ustawiamy dla niego minimalną wysokość i szerokość.

Wykorzystamy kontener „employe” do wyświetlania informacji o pracowniku wybranym przez użytkownika z listy rozwijanej pracowników, niezależnie od przycisku [Salaire], którego rolą będzie później obliczenie wynagrodzenia po wprowadzeniu wszystkich niezbędnych informacji.

Aby obsłużyć zmianę wyboru w liście rozwijanej „employes”, jej kod MXML zmienia się w następujący sposób:


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

Zdarzenie „change” jest wysyłane przez listę rozwijaną, gdy użytkownik zmienia swój wybór. Obsługą tego zdarzenia zajmie się metoda displayInfosEmploye.

Przypomnijmy metody udostępniane przez zdalną usługę internetową:


    // lista wszystkich tożsamości pracowników 
    public Employe[] GetAllIdentitesEmployes();
    // ------- obliczenie wynagrodzenia 
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);

Chcemy tutaj wyświetlić informacje (nazwisko, imię itp.) o pracowniku wybranym z listy rozwijanej. Serwis internetowy nie udostępnia metody pozwalającej na uzyskanie tych danych. Niemniej jednak możemy skorzystać z metody GetSalaire, przekazując numer SS wybranego pracownika oraz wartość 0 dla liczby przepracowanych godzin i dni. Zostanie wykonane zbędne obliczenie wynagrodzenia, ale metoda GetSalaire zwróci nam obiekt typu FeuilleSalaire, w którym znajdziemy potrzebne nam informacje.

Obecna deklaracja serwisu internetowego została zmodyfikowana w celu uwzględnienia definicji metody 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>
  • wiersze 11–19: definicja metody GetSalaire usługi internetowej
  • wiersz 12: definiuje metodę, która ma zostać wykonana w przypadku pomyślnego wywołania metody GetSalaire
  • wiersz 13: definiuje metodę, która ma zostać wykonana w przypadku niepowodzenia wywołania metody GetSalaire
  • wiersze 14–18: metoda GetSalaire oczekuje trzech parametrów. Są one zdefiniowane wewnątrz tagu <mx:request> w postaci <param1>wartość1</param1>. Identyfikator param1 nie może być dowolny. Należy użyć nazw oczekiwanych przez serwis internetowy:
  • w [1] – strona serwisu internetowego [http://localhost:1077/Service1.asmx]
  • w [2], link do strony testowej metody [GetSalaire]
  • w [3] – parametry oczekiwane przez metodę. To właśnie te nazwy należy użyć jako tagi potomne tagu <mx:request>.

Wróćmy do deklaracji serwisu internetowego:


        <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>
  • wiersz 5: parametr ss. Przypomnijmy, że podczas uruchomienia aplikacji Flex tablica wszystkich pracowników została zapisana w zmiennej „employes” typu ArrayCollection.
    • employes.getItemAt(i): to pracownik nr i z tablicy
    • employes.getItemAt(i).SS: to numer ubezpieczenia społecznego tego pracownika.
    • cmbEmployes.selectedIndex: to numer elementu wybranego z listy rozwijanej pracowników cmbemployes.

Skąd wiadomo, że SS jest numerem ubezpieczenia społecznego pracownika? Aby to ustalić, należy powrócić do odpowiedzi wysłanej przez metodę GetAllIdentitesEmployes:

  • w [1], strona serwisu internetowego [Service.asmx]
  • w [2], link do strony testowej metody [GetAllIdentitesEmployes]
  • na [3], test został przeprowadzony. Nie oczekuje się żadnych parametrów.
  • w [4]: odpowiedź XML zawiera tabelę pracowników. To właśnie ta tabela została zapisana w zmiennej employes. W [5] widać, że SS jest rzeczywiście tagiem używanym do przechowywania numeru ubezpieczenia społecznego.

Zakończmy analizę serwisu internetowego:


        <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>
  • wiersz 6: liczba przepracowanych godzin zostanie podana przez zmienną heuresTravaillees
  • wiersz 6: liczba przepracowanych dni zostanie podana przez zmienną joursDeTravail

Zmienne te należy zadeklarować w tagu <mx:Script> wraz z atrybutem [Bindable], który umożliwia odwołanie się do nich przez komponenty MXML (wiersze 7–10 poniżej).


    <mx:Script>
        <![CDATA[
...
            // dane
            [Bindable]
            private var employes : ArrayCollection;
            [Bindable]
            private var heuresTravaillees:Number;
            [Bindable]
            private var joursDeTravail:int;
...
</mx:Script>

Kod zarządzania zdarzeniami widoku zmienia się w następujący sposób:


<mx:Script>
        <![CDATA[
            import mx.rpc.events.FaultEvent;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
                        
            // dane
            [Bindable]
            private var employes : ArrayCollection;
            [Bindable]
            private var heuresTravaillees:Number;
            [Bindable]
            private var joursDeTravail:int;
    
            private function init():void{
                // odnotowuje się wysokość i szerokość # bloków
                employeHeight=employe.height;
                employeWidth=employe.width;
                // ukrywanie niektórych elementów
                hideEmploye();
...
            }
            
            private function displayInfosEmploye():void{
                // formularz
                hideEmploye();
                // obliczamy fikcyjne wynagrodzenie
                heuresTravaillees=0;
                joursDeTravail=0;
                pam.GetSalaire.send();
            }
            
            private function getSalaireCompleted(event:ResultEvent):void{
    ...
            }
            
            private function getSalaireFault(event:FaultEvent):void{
    ...
            }

        // widoki częściowe -------------------------------------------------
        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>
  • wiersz 15: metoda init wykonywana podczas uruchamiania aplikacji Flex zapamiętuje wysokość i szerokość pionowego kontenera employe, aby móc go przywrócić (wiersze 50–53) po jego ukryciu (wiersze 45–48).
  • wiersz 24: metoda displayInfosEmploye jest wywoływana, gdy użytkownik zmienia wybór w polu kombi pracowników.
  • wiersz 26: kontener employe jest ukrywany, jeśli był widoczny
  • wiersz 30: metoda GetSalaire serwisu internetowego jest wywoływana asynchronicznie. Wiadomo, że oczekuje ona trzech parametrów:

                <ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
              <heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
              <joursTravailles>{joursDeTravail}</joursTravailles>
  • wiersz 1: parametrem ss będzie numer SS pracownika wybranego z listy rozwijanej pracowników
  • wiersz 2: metoda displayInfosEmploye nadaje zmiennej heuresTravaillees (wiersz 28) wartość 0
  • wiersz 3: metoda displayInfosEmploye przypisuje wartość 0 do zmiennej joursDeTravail (wiersz 29)

Metoda GetSalaireCompleted jest wykonywana, jeśli metoda GetSalaire serwisu internetowego zakończy się pomyślnie:


private function getSalaireCompleted(event:ResultEvent):void{
                // ukrywanie komunikatu o błędzie
                hideMsg();
                // otrzymuje się odcinek wypłaty
                var feuilleSalaire:Object=event.result;
                // wyświetlanie
                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();
            }
  • wiersz 3: ukrywa się pole komunikatu na wypadek, gdyby było wyświetlane.
  • wiersz 5: pobieramy arkusz wynagrodzeń zwrócony przez metodę GetSalaire

Aby dowiedzieć się, co dokładnie zwraca metoda GetSalaire, wracamy do strony serwisu internetowego:

  • w [1], strona serwisu internetowego [Service.asmx]
  • w [2], link prowadzący do strony testowej metody [GetSalaire]
  • w [3] podaje się parametry
  • w [4] – uzyskany wynik XML.

Wróćmy do metody getSalaireCompleted:


private function getSalaireCompleted(event:ResultEvent):void{
                // ukrywanie komunikatu o błędzie
                hideMsg();
                // otrzymuje się odcinek wypłaty
                var feuilleSalaire:Object=event.result;
                // wyświetlanie
                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();
}
  • wiersz 5: feuilleSalaire=event.result reprezentuje przepływ XML [4] zwrócony przez metodę GetSalaire. Na podstawie tego strumienia widać, że:
    • feuilleSalaire.Employe to strumień XML dotyczący pracownika
    • feuilleSalaire.Employe.Nom to imię i nazwisko tego pracownika
    • ...
  • wiersze 7–12: strumień XML feuilleSalaire jest wykorzystywany do wypełnienia różnych pól kontenera „pracownik”.
  • wiersz 13: wyświetlany jest kontener „employe”.

Metoda getSalaireFault jest wykonywana, jeśli metoda GetSalaire serwisu internetowego zakończy się niepowodzeniem:


            private function getSalaireFault(event:FaultEvent):void{
                // wyświetlanie komunikatu o błędzie
                msg.text=event.fault.message;
                // formularz
                showMsg();            
            }
  • wiersz 3: komunikat o błędzie event.fault.message jest umieszczany w polu komunikatu
  • wiersz 5: pole komunikatu jest wyświetlane

Na tym kończą się zmiany niezbędne dla tej nowej wersji. Po zapisaniu pliku, o ile jest on poprawnie skompilowany, wersja wykonywalna zostanie wygenerowana w folderze [bin-debug] projektu:

 

Powyżej [main2.html] to strona HTML, która zawiera plik binarny aplikacji Flex [main2.swf], który zostanie uruchomiony przez Flash Player.

Możemy przetestować tę nową wersję:

  • należy uruchomić usługę internetową ASP.NET
  • należy uruchomić serwer Apache dla klienta Flex

Zakładając, że alias [pam-v10-flex-client-webservice] używany w poprzedniej wersji nadal istnieje, w przeglądarce należy zwrócić się do serwera Apache z żądaniem URL lub [http://localhost/pam-v10-flex-client-webservice/main2.html]:

  • w [1], żądany plik URL
  • na [2], lista pracowników
  • w [3], zmieniamy wybór w liście rozwijanej, aby wywołać zdarzenie change
  • w [4], uzyskany wynik: karta Justine Laverti.

14.5. Widok nr 3

Widok nr 3 odpowiada za sprawdzanie poprawności formularza. Tutaj sprawdzane jest wyłącznie pole wprowadzania danych „txtHeuresTravaillees”. Dopóki formularz jest nieprawidłowy, przycisk „btnSalaire” pozostanie nieaktywny.

Aby dodać tę funkcjonalność, duplikujemy [main2.mxml] w [main3.mxml]:

Od tej pory będziemy pracować z komponentem [main3.mxml], który ustanowimy jako domyślny (patrz ten projekt, paragraf 14.4). Najpierw dodajemy atrybut do komponentu „txtHeuresTravaillees”:


<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>

Za każdym razem, gdy zmienia się zawartość pola wprowadzania danych „txtHeuresTravaillees”, wywoływana jest metoda validateForm. Jest to metoda lokalna napisana przez programistę. W tej metodzie moglibyśmy sprawdzić, czy zawartość pola wprowadzania danych „txtHeuresTravaillees” jest rzeczywiście dodatnią liczbą całkowitą. Postąpimy jednak inaczej, wykorzystując komponent walidacyjny:


    <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"/>
  • wiersz 1: komponent <mx:NumberValidator> pozwala sprawdzić, czy inny komponent zawiera liczbę całkowitą lub rzeczywistą.
  • wiersz 1: atrybut id nadaje identyfikator komponentowi.
  • wiersz 1: source to identyfikator komponentu sprawdzanego przez komponent NumberValidator. W tym przypadku sprawdzane jest pole wprowadzania danych „txtHeuresTravaillees”.
  • wiersz 1: property to nazwa właściwości komponentu source, która zawiera wartość podlegającą weryfikacji. Ostatecznie weryfikowana jest wartość source.property, w tym przypadku txtHeuresTravaillees.text.
  • wiersz 2: precision określa maksymalną liczbę dozwolonych miejsc po przecinku. precision=0 sprawdza, czy wprowadzona liczba jest liczbą całkowitą.
  • wiersz 2: allowNegative określa, czy liczby ujemne są dozwolone, czy nie
  • wiersz 7: required określa, czy wprowadzenie danych jest obowiązkowe, czy nie.

Gdy warunek walidacji nie jest spełniony, w dymku obok błędnego elementu wyświetlany jest komunikat o błędzie. Domyślnie komunikaty te są w języku angielskim. Istnieje możliwość samodzielnego zdefiniowania tych komunikatów:

  • (ciąg dalszy)
    • invalidCharError: komunikat o błędzie, gdy tekst zawiera znak, który nie może występować w liczbie
    • precisionError: komunikat o błędzie, gdy liczba miejsc po przecinku jest nieprawidłowa w stosunku do atrybutu precision
    • negativeError: komunikat o błędzie, gdy liczba jest ujemna, mimo że atrybut allowNegative ma wartość „false”
    • requiredFieldError: komunikat o błędzie pojawiający się, gdy nie wprowadzono danych, mimo że ustawiono atrybut requiredField="true"
    • invalidFormatCharsError: komunikat o błędzie, gdy tekst zawiera nieprawidłowe znaki lub ma nieprawidłowy format?

Wróćmy do komponentu „txtHeuresTravaillees”:


<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>

Metoda validateForm mogłaby wyglądać następująco w tagu <mx:Script>:


        private function validateForm(event:Event):void 
        {                    
            // zatwierdzamy przepracowane godziny
            var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
            // potwierdzenie zakończone sukcesem?
            btnSalaire.enabled=evt.type==ValidationResultEvent.VALID;
}
  • wiersz 4: uruchamiany jest walidator „heuresTravailleesValidator”. Zwraca on wynik typu ValidationResultEvent.
  • wiersz 6: evt.type jest typu String i określa typ zdarzenia. evt.type może przyjmować dwie wartości dla typu ValidationResultEvent: „invalid” lub „valid”, reprezentowane przez stałe ValidationResultEvent.INVALID i ValidationResultEvent.VALID. Jeśli w wierszu 4 walidacja zakończyła się powodzeniem, wartość evt.type musi wynosić ValidationResultEvent.VALID. W takim przypadku przycisk btnSalaire jest aktywny, w przeciwnym razie jest nieaktywny.

To wystarczy do sprawdzenia poprawności godzin przepracowanych.

Powyżej kompilacja projektu wygenerowała pliki [main3.html] i [main3.swf]. Wywołujemy pliki URL i [http://localhost/pam-v10-flex-client-webservice/main3.html] w przeglądarce i sprawdzamy różne przypadki błędów:

  • pola z błędami mają czerwoną ramkę [1, 2, 3], a pola poprawne – niebieską ramkę [4].
  • W przypadku [4] należy zauważyć, że przycisk [Salaire] jest aktywny, ponieważ liczba przepracowanych godzin jest prawidłowa.

14.6. Widok nr 4

Widok nr 4 stanowi zakończenie formularza obliczania wynagrodzenia. W tym celu powielamy [main3.xml] do [main4.xml] i od tej pory pracujemy z main4, który ustawiamy jako domyślny (patrz punkt 14.4).

Zmiany wprowadzone w plikach [main4.xml] i [1] są następujące:

  • do widoku [2] dodano nowy kontener pionowy w celu wyświetlania elementów wynagrodzenia pracownika
  • dodano komponent umożliwiający formatowanie wartości pieniężnych w widoku [3]
  • wyświetlanie składników wynagrodzenia jest obsługiwane przez menedżera powiązanego ze zdarzeniem „kliknięcie” przycisku „btnSalaire”.

Widok zmienia się w następujący sposób:

Nowy kontener działa na tej samej zasadzie co poprzedni. Jest to kontener pionowy VBox [V1] zawierający cztery kontenery poziome HBox [Hi]. Pojemniki poziome o numerach od H1 do H3 składają się z pojemników pionowych zawierających dwa napisy, z których drugi znajduje się z kolei w pojemniku pionowym, aby zapewnić kolor tła.


Pytanie 1: napisz kontener wynagrodzenia. Będzie on dalej nazywany complements.



Zadanie 2: Napisz metody umożliwiające ukrywanie/wyświetlanie kontenera complements. Skorzystaj z rozwiązania zastosowanego wcześniej dla kontenera employe.


Przypisujemy procedurę obsługi do zdarzenia „click” przycisku „btnSalaire”:


                <mx:Button id="btnSalaire" label="Salaire" click="calculerSalaire()"/>

Metoda calculerSalaire wygląda następująco:


            private function calculerSalaire():void{
                // przygotowanie formularza
                affichageSalaire=true;
                msg.text="";                
                // parametry obliczania wynagrodzenia
                heuresTravaillees=Number(txtHeuresTravaillees.text);
                joursDeTravail=int(joursTravailles.value);
                // wniosek o wynagrodzenie wysłany do serwisu internetowego
                pam.GetSalaire.send();
}
  • wiersz 3: wartość logiczna affichageSalaire służy do wskazania, czy należy wyświetlić kontener complements, który zawiera elementy wynagrodzenia. Metoda getSalaireCompleted jest wykonywana w przypadku dwóch zdarzeń:
    • zmiana pracownika w liście rozwijanej pracowników w celu wyświetlenia jego danych bez wynagrodzenia. W takim przypadku należy ustawić affichageSalaire=false.
    • przy obliczaniu wynagrodzenia
  • wiersz 6: tekst z pola wprowadzania danych txtHeuresTravaillees jest przekształcany na liczbę rzeczywistą.
  • wiersz 7: wartość licznika joursTravailles jest przekształcana na liczbę całkowitą.
  • wiersz 9: wywołanie metody zdalnej GetSalaire. Przypominamy, że metoda ta oczekuje trzech parametrów, w tym parametrów heuresTravaillees i joursDeTravail zainicjowanych w wierszach 6 i 7. Przypominamy również, że jeśli asynchroniczne wywołanie metody GetSalaire:
    • zakończy się powodzeniem, zostanie wywołana metoda getSalaireCompleted
    • zakończy się niepowodzeniem, wywołana zostanie metoda getSalaireFault

Pytanie 3: należy uzupełnić obecną metodę getSalaireCompleted tak, aby wyświetlała wynagrodzenie pracownika po kliknięciu przycisku btnSalaire.


Obecnie elementy wynagrodzenia są wyświetlane bez znaku euro. Można go dodać w kodzie lub użyć formatora. Takie rozwiązanie jest obecnie proponowane. Formator będzie wyglądał następująco:


    <mx:CurrencyFormatter id="eurosFormatter" precision="2"
        currencySymbol="" useNegativeSign="true"
alignSymbol="right"/>
  • wiersz 1: id to identyfikator formatera, precision to liczba miejsc po przecinku, które mają zostać zachowane.
  • wiersz 2: currencySymbol to symbol waluty, który ma być używany. useNegativeSign określa, czy w przypadku wartości ujemnych należy używać znaku „-”.
  • wiersz 3: alignSymbol określa, gdzie należy umieścić znak waluty względem liczby.

Ten formatator stosuje się w kodzie skryptu w następujący sposób:

                    lblSH.text=eurosFormatter.format(feuilleSalaire.Indemnites.BaseHeure);
  • eurosFormatter to identyfikator formatownika, którego należy użyć
  • format to metoda, którą należy wywołać w celu sformatowania liczby. Zwraca ona ciąg znaków.
  • feuilleSalaire.Indemnites.BaseHeure to liczba, którą należy sformatować.
  • lblSH to nazwa komponentu typu Text.

Pytanie 4: zmodyfikuj metodę getSalaireCompleted tak, aby korzystała z formatowania walutowego.