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





























