14. A aplicação [SimuPaie] – versão 10 – cliente Flex de um serviço web ASP.NET
Apresentamos agora um cliente Flex do serviço web ASP.NET da versão 5. O IDE utilizado é o Flex Builder 3. Está disponível para download uma versão de demonstração deste produto em URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. O Flex Builder 3 é um IDE Eclipse. Além disso, para executar o cliente Flex, utilizamos um servidor web Apache da ferramenta Wamp [http://www.wampserver.com/]. Qualquer servidor Apache serve. O navegador que exibe o cliente Flex deve dispor do plugin Flash Player, no mínimo na versão 9.
As aplicações Flex têm a particularidade de serem executadas no plugin Flash Player do navegador. Nesse aspeto, assemelham-se às aplicações Ajax, que incorporam nas páginas enviadas ao navegador scripts JavaScript que são posteriormente executados no próprio navegador. Uma aplicação Flex não é uma aplicação web no sentido em que normalmente se entende: é uma aplicação cliente de serviços fornecidos por servidores web. Nesse aspeto, é análoga a uma aplicação de secretária que fosse cliente desses mesmos serviços. Difere, no entanto, num ponto: é inicialmente descarregada de um servidor web para um navegador que disponha do plugin Flash Player capaz de a executar.
Tal como uma aplicação de secretária, uma aplicação Flex é composta principalmente por dois elementos:
- uma parte de apresentação: as vistas exibidas no navegador. Estas vistas têm a riqueza das janelas das aplicações de secretária. Uma vista é descrita através de uma linguagem de marcadores chamada MXML.
- uma parte de código que gere principalmente os eventos provocados pelas ações do utilizador na vista. Este código pode ser escrito também em MXML ou numa linguagem orientada para objetos chamada ActionScript. É necessário distinguir dois tipos de eventos:
- o evento que requer uma troca de dados com o servidor web: preenchimento de uma lista com dados fornecidos por uma aplicação web, envio dos dados de um formulário para o servidor, etc. O Flex fornece vários métodos para comunicar com o servidor de forma transparente para o programador. Estes métodos são, por predefinição, assíncronos: o utilizador pode continuar a interagir com a vista durante a solicitação ao servidor.
- o evento que altera a vista apresentada sem troca de dados com o servidor, por exemplo, arrastar um elemento de uma árvore para o colocar numa lista. Este tipo de evento é tratado inteiramente a nível local, no navegador.
Uma aplicação Flex é frequentemente executada da seguinte forma:
![]() |
- em [1], é solicitada uma página HTML
- em [2], a mesma é enviada. Esta inclui um ficheiro binário SWF (ShockWave Flash) que contém a aplicação Flex na íntegra: todas as vistas e o código de gestão dos respetivos eventos. Este ficheiro será executado pelo plugin Flash Player do navegador.
![]() |
- A execução do cliente Flex ocorre localmente no navegador, exceto quando necessita de dados externos. Nesse caso, solicita-os ao servidor [3]. Recebe-os em [4] em diversos formatos: XML ou binário. A aplicação consultada no servidor web pode ser escrita em qualquer linguagem. O que importa é apenas o formato da resposta.
Descrevemos a arquitetura de execução de uma aplicação Flex para que o leitor perceba a diferença entre esta e a de uma aplicação Web clássica, em que as páginas não incorporam código (JavaScript, Flex, Silverlight, ...) que o navegador executaria. Nesta última, o navegador é passivo: limita-se a apresentar páginas HTML criadas no servidor web que lhas envia.
14.1. Arquitetura da aplicação cliente/servidor
A arquitetura cliente/servidor implementada é semelhante à das versões 6 e 8:
![]() |
No [1], a camada web ASP.NET é substituída por uma camada web Flex escrita em MXML e ActionScript. O cliente [C] será gerado pelo Flex Builder IDE. É importante referir aqui que esta arquitetura inclui dois servidores web não representados:
- um servidor web ASP.NET que executa o serviço web [S]
- um servidor web APACHE que executa o cliente web [1]
14.2. O projeto Flex 3 do cliente
Estamos a desenvolver o cliente Flex com o Flex Builder 3 IDE:
![]() |
- no Flex Builder 3, criamos um novo projeto em [1]
- atribuímos-lhe um nome em [2] e especificamos em [3] em que pasta o gerar
![]() |
- em [4], atribui-se um nome à aplicação principal (aquela que será executada)
- em [5], o projeto, uma vez gerado
- em [6], o ficheiro principal da aplicação MXML
- um ficheiro MXML contém uma vista e o código de gestão dos eventos da mesma. O separador [Source] [7] dá acesso ao ficheiro MXML. Nele encontram-se as balizas <mx> que descrevem a vista, bem como o código ActionScript.
- A vista pode ser criada graficamente utilizando o separador [Design] [8]. As balizas MXML que descrevem a vista são então geradas automaticamente no separador [Source]. O inverso também é verdadeiro: as balizas MXML adicionadas diretamente no separador [Source] são refletidas graficamente no separador [Design].
14.3. A vista n.º 1
Vamos construir progressivamente uma interface web análoga à da versão 1 (ver parágrafo 4). Começamos por construir a seguinte interface:
![]() |
- em [1], a visualização quando a ligação ao serviço web foi bem-sucedida. A lista suspensa dos funcionários é então preenchida.
- em [2], a visualização quando não foi possível estabelecer a ligação ao serviço web. É então apresentada uma mensagem de erro.
O ficheiro principal do cliente, [main.xml], é o seguinte:
<?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[
...
// dados
[Bindable]
private var employes : ArrayCollection;
private function init():void{
...
}
]]>
</mx:Script>
</mx:Application>
Neste código, é necessário distinguir vários elementos:
- a definição da aplicação (linhas 2-3)
- a descrição da respetiva vista (linhas 4-25)
- os gestores de eventos na linguagem ActionScript dentro da baliza <mx:Script> (linhas 31-42).
- a definição do serviço web remoto (linhas 27-29)
Comecemos por comentar a definição da própria aplicação e a descrição da sua vista:
- linhas 2-3: definem:
- o modo de disposição dos componentes no contentor da vista. O atributo layout="vertical" indica que os componentes ficarão uns abaixo dos outros.
- o método a executar quando a vista for instanciada, c.a.d. o momento em que todos os seus componentes tiverem sido instanciados. O atributo creationComplete="init();" indica que é o método init da linha 38 que deve ser executado. creationComplete é um dos eventos que a classe Application pode emitir.
- as linhas 4-25 definem os componentes da vista
- linhas 4-25: um contentor vertical: os componentes serão colocados uns abaixo dos outros
- linha 5: define um texto
- linhas 6-23: um contentor horizontal: os componentes serão colocados horizontalmente neste.
- linhas 7-10: um contentor vertical que conterá um texto e uma lista suspensa
- linha 8: o texto
- linha 9: a lista suspensa na qual será inserida a lista de funcionários. A baliza dataProvider="{employes}" indica a fonte dos dados que devem preencher a lista. Aqui, a lista será preenchida com o objeto employes definido na linha 36. Para poder escrever dataProvider="{employes}", é necessário que o campo employes tenha o atributo [Bindable] (linha 35). Este atributo permite que uma variável ActionScript seja referenciada fora da baliza <mx:Script>. O campo employes é do tipo ArrayCollection, um tipo ActionScript que permite armazenar listas de objetos, neste caso uma lista de objetos do tipo Employe.
- linhas 11-14: um contentor vertical que conterá um texto e uma área de introdução de dados
- linha 12: o texto
- linha 13: o campo de introdução das horas trabalhadas.
- linhas 15-18: um contentor vertical que irá conter um texto e um contador
- linha 16: o texto
- linha 17: o contador que permitirá a introdução dos dias trabalhados.
- linhas 19-22: um contentor vertical que conterá um texto e um botão que acionará o cálculo do salário da pessoa selecionada na lista suspensa.
- linha 20: o texto
- linha 21: o botão.
- linha 23: fim do contentor horizontal iniciado na linha 6
- linha 24: uma área de texto num componente do tipo TextArea. Apresentará as mensagens de erro.
- linha 25: fim do contentor vertical iniciado na linha 4
As linhas 4-25 geram a seguinte visualização no separador [Design]:
![]() |
- [1]: foi gerado pelo componente Label da linha 5
- [2]: foi gerado pelo componente ComboBox da linha 9
- [3]: foi gerado pelo componente TextInput da linha 13
- [4]: foi gerado pelo componente NumericStepper da linha 17
- [5]: foi gerado pelo componente Button da linha 21
- [6]: foi gerado pelo componente TextArea da linha 24
Analisemos agora a declaração do serviço 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>
- linha 1: o serviço web é um componente com o identificador pam (atributo id)
- linha 2: o URI do ficheiro WSDL do serviço web (ver parágrafo 9.2)
- linha 3: o método a executar em caso de erro durante as trocas de dados com o serviço web: o método wsFault.
- linha 4: solicita que seja exibido um indicador para mostrar ao utilizador que está em curso uma troca de dados com o serviço web.
- linhas 5-10: uma das operações disponibilizadas pelo serviço web remoto. Neste caso, o método GetAllIdentitesEmployes.
- linha 7: o método a executar quando a chamada deste método terminar normalmente, c.a.d. Quando o serviço web devolver corretamente a lista de funcionários
- linha 8: o método a executar quando a chamada a este método terminar com um erro.
- linha 9: os parâmetros da operação GetAllIdentitesEmployes. Sabemos que este método não espera parâmetros. Por isso, deixamos a baliza <mx:request> vazia.
Vamos agora analisar o código ActionScript associado ao serviço web:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// dados
[Bindable]
private var employes : ArrayCollection;
private function init():void{
// regista-se a localização da área de mensagem
msgHeight=msg.height;
msgWidth=msg.width;
// oculta-se a área de mensagem
hideMsg();
// solicitação ao serviço web remoto para obter a lista simplificada de funcionários
pam.GetAllIdentitesEmployes.send();
}
private function wsFault(event:Event):void{
// é sinalizado o erro
msg.text="Service distant indisponible";
showMsg();
}
private function loadEmployesCompleted(event:ResultEvent):void{
// preenchimento da lista suspensa de funcionários
employes=event.result as ArrayCollection;
}
private function displayEmploye(employe:Object):String{
// identidade de um funcionário
return employe.Prenom + " " + employe.Nom;
}
private function loadEmployesFault(event:FaultEvent):void{
// exibição da mensagem de erro
msg.text=event.fault.message;
// formulário
showMsg();
}
// gestão de blocos
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>
- linha 11: o método init é executado no arranque da aplicação porque escrevemos:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
- linhas 13-14: guardam-se a altura e a largura da área de mensagem. Utilizam-se dois métodos, hideMsg (linhas 48-51) e showMsg (linhas 53-56), para, respetivamente, ocultar/mostrar a área de mensagem, consoante tenha ocorrido ou não um erro. O método hideMsg oculta a área de mensagem, definindo a sua altura e largura para 0. O método showMsg exibe a área de mensagem, restaurando a sua altura e largura memorizadas no método init.
- linha 16: oculta-se a área de mensagem. Inicialmente, não há erros.
- linha 18: é chamado o método GetAllIdentitesEmploye (linha 6 do serviço web) do serviço web pam (linha 1 do serviço web). A chamada é assíncrona. A linha 7 do serviço web indica que o método loadEmployesCompleted será executado se esta chamada assíncrona for bem-sucedida. A linha 8 do serviço web indica que o método loadEmployesFault será executado se esta chamada assíncrona falhar.
- linha 27: o método loadEmployesCompleted, que é executado se a chamada ao serviço web da linha 18 for bem-sucedida.
- linha 29: sabemos que o serviço web devolve uma resposta XML. É aconselhável voltar a esta resposta para compreender o código ActionScript:
![]() |
- em [1], página do serviço web [Service.asmx]
- em [2], o link para a página de teste do método [GetAllIdentitesEmployes]
- em [3], o teste está concluído. Não são esperados parâmetros.
- em [4]: a resposta XML contém uma tabela de funcionários. Para cada um deles, existem cinco informações encapsuladas nas balizas <Id>, <Version>, <SS>, <Nom>, <Prenom>. Se a resposta XML for inserida numa tabela employes do tipo ArrayCollection:
- employes.getItemAt(i): é o elemento n.º i da tabela
- employes.getItemAt(i).SS: é o número de segurança social desse funcionário.
- employes.getItemAt(i).Nome: é o nome deste funcionário
- ...
Voltemos ao código ActionScript:
- linha 29: event.result representa a resposta XML do serviço web. O método GetAllIdentitesEmployes devolve uma matriz de funcionários. event.result representa essa matriz de funcionários. É colocada numa variável do tipo ArrayCollection, um tipo que representa, de forma geral, uma coleção de objetos. Esta variável, denominada employes, é declarada na linha 9. Recorde-se que esta variável é a fonte de dados do menu suspenso dos funcionários:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
Para cada funcionário da sua fonte de dados, a lista suspensa irá chamar o método displayEmploye (atributo labelFunction) para apresentar o funcionário. Nas linhas 32 a 34, verifica-se que este método apresenta o nome e o apelido do funcionário.
- linha 37: o método loadEmployesFault, que é executado se a chamada ao serviço web da linha 18 falhar. event.fault.message é a mensagem de erro devolvida pelo serviço web.
- linha 39: esta mensagem de erro é inserida na área de mensagem
- linha 41: a área de mensagem é apresentada.
Quando a aplicação foi compilada, o seu código executável encontra-se na pasta [bin-debug] do projeto Flex:
![]() |
Acima,
- o ficheiro [main.html] representa o ficheiro HTML que será solicitado pelo navegador ao servidor web para obter o cliente Flex
- o ficheiro [main.swf] é o binário do cliente Flex que será encapsulado na página HTML enviada ao navegador e, posteriormente, executado pelo plugin Flash Player deste.
Estamos prontos para executar o cliente Flex. Antes disso, temos de configurar o ambiente de execução necessário para o mesmo. Voltemos à arquitetura cliente/servidor testada:
![]() |
Do lado do servidor:
- iniciar o serviço web ASP.NET [S]
Do lado do cliente:
- iniciar o servidor Apache, ao qual será solicitada a aplicação Flex.
Aqui, utilizamos a ferramenta Wamp. Com esta ferramenta, podemos associar um alias à pasta [bin-debug] do projeto Flex.
![]() |
- O ícone do Wamp encontra-se na parte inferior do ecrã [1]
- com um clique do botão esquerdo no ícone Wamp, selecione a opção Apache [2] / Alias Directories [3, 4]
- selecione a opção [5]: Adicionar um alias
![]() |
- em [6], atribuir um alias (qualquer nome) à aplicação web que vai ser executada
- em [7], indicar a raiz da aplicação web que terá esse alias: trata-se da pasta [bin-debug] do projeto Flex que acabámos de criar.
Recorde-se a estrutura da pasta [bin-debug] do projeto Flex:
![]() |
O ficheiro [main.html] é o ficheiro HTML da aplicação Flex. Graças ao alias que acabámos de criar na pasta [bin-debug], este ficheiro será obtido através do URL [http://localhost/pam-v10-flex-client-webservice/main.html]. Abrimos este ficheiro num navegador que disponha do plugin Flash Player versão 9 ou superior:
![]() |
- no [1], o URL da aplicação Flex
- em [2], a lista de funcionários quando tudo está a funcionar corretamente
- em [3], o resultado obtido quando o serviço web está inativo
Pode ter curiosidade em visualizar o código-fonte da página HTML recebida:
- O corpo da página começa na linha 25. Não contém o HTML clássico, mas sim um objeto (linha 28) do tipo «application/x-shockwave-flash» (linha 41). Trata-se do ficheiro [main.swf] (linha 31), que pode ser visto na pasta [bin-debug] do projeto Flex. É um ficheiro de tamanho considerável: cerca de 600 K para este exemplo simples.
14.4. A vista n.º 2
Vamos adicionar um novo contentor do tipo VBox à vista atual:
![]() |
![]() |
- em [4,5], definimos [main2.mxml] como a nova aplicação predefinida. É esta que passará a ser compilada.
- em [6], a aplicação predefinida é indicada por um ponto azul.
O contentor [1] apresentará as informações relativas ao funcionário selecionado no menu suspenso [2]. Duplicamos o [main.xml] para [main2.xml] e [3] para criar a nova vista. Passaremos agora a trabalhar com o [main2.xml].
![]() |
A alteração feita ao projeto anterior consiste na adição do contentor da linha 26 acima, que contém o código MXML do contentor [1] da vista. Atribuímos-lhe o identificador employe para podermos manipulá-lo através de código. Com efeito, este contentor deverá poder ser ocultado/exibido através da mesma técnica utilizada anteriormente para a área de mensagem.
Voltemos ao aspeto visual da vista:
![]() |
Identifiquemos os diferentes contentores das novas informações apresentadas:
- V1: contentor vertical de todos os componentes: o rótulo Employé, [1] e os contentores horizontais [H1] e [H2]
- H1: contentor horizontal para as informações Nom, Prénom, Adresse
- V2: contentor vertical para a legenda Nom e a exibição do nome do funcionário.
- H2: contentor horizontal para as informações Ville, Código Postal, Indice
O código completo do contentor «employe» é o seguinte:
<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>
O código é autoexplicativo. Vamos apenas explicar o contentor vertical que exibe o nome do funcionário, por exemplo:
- linhas 4-9: o contentor vertical
- linha 5: a designação Nom
- linhas 6-8: um contentor vertical que exibirá o nome do funcionário (linha 7). Queremos atribuir uma cor de fundo diferente aos campos que exibem informações sobre o funcionário. O componente Text não oferece essa possibilidade (ou então não consegui encontrá-la). É possível definir a cor de fundo de um contentor. É por isso que foi utilizado aqui.
- linha 7: o componente Text, que irá apresentar o nome do funcionário. Definimos uma altura e uma largura mínimas para ele.
Vamos utilizar o contentor «employe» para apresentar as informações do funcionário que o utilizador selecionar na lista suspensa de funcionários, independentemente do botão [Salaire], cuja função será, posteriormente, calcular o salário assim que todas as informações necessárias tiverem sido introduzidas.
Para gerir a alteração da seleção na lista suspensa «employes», o seu código MXML é alterado da seguinte forma:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>
O evento «change» é disparado pela lista suspensa quando o utilizador altera a sua seleção. O gestor deste evento será o método displayInfosEmploye.
Recorde-se que os métodos expostos pelo serviço web remoto são:
// lista de todas as identidades dos funcionários
public Employe[] GetAllIdentitesEmployes();
// ------- cálculo do salário
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);
Queremos aqui apresentar as informações (nome, apelido, etc.) do funcionário selecionado na lista suspensa. O serviço web não disponibiliza nenhum método para obter essas informações. No entanto, podemos utilizar o método GetSalaire, passando o n.º SS do funcionário selecionado e 0 para as horas e dias trabalhados. Será efetuado um cálculo salarial desnecessário, mas o método GetSalaire devolver-nos-á um objeto do tipo FeuilleSalaire, no qual encontraremos as informações de que necessitamos.
A declaração atual do serviço web é alterada para incluir a definição do método 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>
- linhas 11-19: a definição do método GetSalaire do serviço web
- linha 12: define o método a executar quando a chamada ao método GetSalaire for bem-sucedida
- linha 13: define o método a executar quando a chamada ao método GetSalaire falhar
- linhas 14-18: o método GetSalaire espera três parâmetros. Estes são definidos dentro de uma baliza <mx:request> na forma <param1>valor1</param1>. O identificador param1 não pode ser qualquer um. É necessário utilizar os nomes esperados pelo serviço web:
![]() |
- em [1], a página do serviço web [http://localhost:1077/Service1.asmx]
- em [2], o link para a página de teste do método [GetSalaire]
- em [3], os parâmetros esperados pelo método. São estes nomes que devem ser utilizados como tags filhas da tag <mx:request>.
Voltemos à declaração do serviço 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>
- linha 5: o parâmetro ss. Recorde-se que, no arranque da aplicação Flex, a tabela de todos os funcionários foi armazenada numa variável «empregados» do tipo ArrayCollection.
- employes.getItemAt(i): é o funcionário n.º i da tabela
- employes.getItemAt(i).SS: é o número de segurança social desse funcionário.
- cmbEmployes.selectedIndex: é o número do elemento selecionado na lista suspensa de funcionários cmbemployes.
No exemplo acima, como se sabe que SS é o número de segurança social de um funcionário? Para isso, é necessário voltar à resposta enviada pelo método GetAllIdentitesEmployes:
![]() |
- em [1], página do serviço web [Service.asmx]
- em [2], o link para a página de teste do método [GetAllIdentitesEmployes]
- em [3], o teste está concluído. Não são esperados parâmetros.
- em [4]: a resposta XML contém uma tabela de funcionários. Foi esta tabela que foi armazenada na variável employes. Vemos em [5] que SS é, de facto, a baliza utilizada para conter o número de segurança social.
Concluamos a análise do serviço 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>
- linha 6: o número de horas trabalhadas será fornecido por uma variável heuresTravaillees
- linha 6: o número de dias trabalhados será fornecido por uma variável joursDeTravail
Estas variáveis devem ser declaradas na baliza <mx:Script> com o atributo [Bindable], que permite que sejam referenciadas por componentes MXML (linhas 7-10 abaixo).
<mx:Script>
<![CDATA[
...
// dados
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
...
</mx:Script>
O código de gestão de eventos da vista evolui da seguinte forma:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// dados
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
private function init():void{
// regista-se a altura/largura de # blocos
employeHeight=employe.height;
employeWidth=employe.width;
// ocultam-se alguns elementos
hideEmploye();
...
}
private function displayInfosEmploye():void{
// formulário
hideEmploye();
// calcula-se um salário fictício
heuresTravaillees=0;
joursDeTravail=0;
pam.GetSalaire.send();
}
private function getSalaireCompleted(event:ResultEvent):void{
...
}
private function getSalaireFault(event:FaultEvent):void{
...
}
// vistas parciais -------------------------------------------------
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>
- linha 15: o método init, executado no arranque da aplicação Flex, memoriza a altura e a largura do contentor vertical employe, para que seja possível restaurá-lo (linhas 50-53) após o ter ocultado (linhas 45-48).
- linha 24: o método displayInfosEmploye é o método executado quando o utilizador altera a sua seleção no menu suspenso dos funcionários.
- linha 26: o contentor employe é ocultado, caso estivesse visível
- linha 30: o método GetSalaire do serviço web é chamado de forma assíncrona. Sabe-se que este método espera três parâmetros:
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
- linha 1: o parâmetro ss será o n.º SS do funcionário selecionado na lista suspensa de funcionários
- linha 2: o método displayInfosEmploye atribui o valor 0 à variável heuresTravaillees (linha 28)
- linha 3: o método displayInfosEmploye atribui o valor 0 à variável joursDeTravail (linha 29)
O método GetSalaireCompleted é executado se o método GetSalaire do serviço web for concluído com sucesso:
private function getSalaireCompleted(event:ResultEvent):void{
// ocultar a mensagem de erro
hideMsg();
// recebe-se uma folha de salário
var feuilleSalaire:Object=event.result;
// exibição
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();
}
- linha 3: oculta-se a área de mensagem, caso esta venha a ser exibida.
- linha 5: recupera-se a folha de salário devolvida pelo método GetSalaire
Para saber exatamente o que o método GetSalaire devolve, voltamos à página do serviço web:
![]() |
- em [1], a página do serviço web [Service.asmx]
- para [2], o link que conduz à página de teste do método [GetSalaire]
- em [3], são fornecidos parâmetros
- em [4], o resultado XML obtido.
Voltemos ao método getSalaireCompleted:
private function getSalaireCompleted(event:ResultEvent):void{
// oculta-se a mensagem de erro
hideMsg();
// recebe-se uma folha de vencimento
var feuilleSalaire:Object=event.result;
// exibição
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();
}
- linha 5: feuilleSalaire=event.result representa o fluxo XML [4] devolvido pelo método GetSalaire. De acordo com este fluxo, verifica-se que:
- feuilleSalaire.Employe é o fluxo XML de um colaborador
- feuilleSalaire.Employe.Nom é o nome desse colaborador
- ...
- linhas 7-12: o fluxo XML feuilleSalaire é utilizado para preencher os diferentes campos do contentor «empregado».
- linha 13: o contentor «empregado» é apresentado.
O método getSalaireFault é executado se o método GetSalaire do serviço web falhar:
private function getSalaireFault(event:FaultEvent):void{
// exibição da mensagem de erro
msg.text=event.fault.message;
// formulário
showMsg();
}
- linha 3: a mensagem de erro event.fault.message é inserida na área de mensagem
- linha 5: o campo de mensagem é exibido
Aqui terminam as alterações necessárias para esta nova versão. Ao guardá-la e se estiver sintaticamente correta, a versão executável é gerada na pasta [bin-debug] do projeto:
![]() |
Acima, [main2.html] é a página HTML que incorpora o ficheiro binário da aplicação Flex [main2.swf], que será executado pelo Flash Player.
Podemos testar esta nova versão:
- o serviço web ASP.NET deve estar em execução
- o servidor Apache deve ser iniciado para o cliente Flex
Supondo que o alias [pam-v10-flex-client-webservice] utilizado na versão anterior ainda exista, solicitamos ao servidor Apache o URL e o [http://localhost/pam-v10-flex-client-webservice/main2.html] num navegador:
![]() |
![]() |
- em [1], o URL solicitado
- em [2], a lista suspensa dos funcionários
- em [3], altera-se a seleção no menu suspenso para provocar o evento change
- em [4], o resultado obtido: a ficha de Justine Laverti.
14.5. A vista n.º 3
A vista n.º 3 apresenta a verificação de validade do formulário. Aqui, apenas o campo de introdução de dados «txtHeuresTravaillees» é verificado. Enquanto o formulário estiver incorreto, o botão «btnSalaire» permanecerá desativado.
Para adicionar esta funcionalidade, duplicamos o [main2.mxml] no [main3.mxml]:
![]() |
A partir de agora, iremos trabalhar com o [main3.mxml], que definiremos como aplicação por predefinição (ver este conceito no parágrafo 14.4). Em primeiro lugar, adicionamos um atributo ao componente «txtHeuresTravaillees»:
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
Sempre que o conteúdo do campo de introdução «txtHeuresTravaillees» mudar, o método validateForm é chamado. Trata-se de um método local escrito pelo programador. Nesse método, poderíamos verificar se o conteúdo do campo de introdução «txtHeuresTravaillees» é, de facto, um número inteiro positivo. Vamos proceder de outra forma, utilizando um componente de validação:
<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"/>
- linha 1: o componente <mx:NumberValidator> permite verificar se outro componente contém um número inteiro ou real.
- linha 1: o atributo id atribui um identificador ao componente.
- linha 1: source é o ID do componente verificado pelo componente NumberValidator. Neste caso, é o campo de introdução de dados «txtHeuresTravaillees» que é verificado.
- linha 1: property é o nome da propriedade do componente source que contém o valor a verificar. Em última análise, é o valor source.property que é verificado, neste caso txtHeuresTravaillees.text.
- linha 2: precision define o número máximo de casas decimais permitidas. precision=0 serve para verificar se o número introduzido é um número inteiro.
- linha 2: allowNegative indica se os números negativos são permitidos ou não
- linha 7: required indica se a introdução de dados é obrigatória ou não.
Quando uma condição de validação não é verificada, é exibida uma mensagem de erro num balão junto ao componente com erro. Por predefinição, estas mensagens estão em inglês. É possível definir estas mensagens:
- (continuação)
- invalidCharError: a mensagem de erro quando o texto contém um caractere que não pode ocorrer num número
- precisionError: a mensagem de erro quando o número de casas decimais está incorreto em relação ao atributo precision
- negativeError: a mensagem de erro quando o número é negativo, embora o atributo allowNegative esteja definido como «false»
- requiredFieldError: a mensagem de erro quando não foi introduzido qualquer valor, embora o atributo requiredField esteja definido como "true"
- invalidFormatCharsError: a mensagem de erro quando o texto contém caracteres ou um formato inválido?
Voltemos ao componente «txtHeuresTravaillees»:
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
O método validateForm poderia ser o seguinte na baliza <mx:Script>:
private function validateForm(event:Event):void
{
// Validação das horas trabalhadas
var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
// validação bem-sucedida?
btnSalaire.enabled=evt.type==ValidationResultEvent.VALID;
}
- linha 4: o validador «heuresTravailleesValidator» é executado. Este devolve um resultado do tipo ValidationResultEvent.
- linha 6: evt.type é do tipo String e indica o tipo do evento. evt.type tem dois valores possíveis para o tipo ValidationResultEvent: «invalid» ou «valid», representados pelas constantes ValidationResultEvent.INVALID e ValidationResultEvent.VALID. Se, na linha 4, a validação tiver sido bem-sucedida, evt.type deve ter como valor ValidationResultEvent.VALID. Neste caso, o botão btnSalaire fica ativado; caso contrário, fica desativado.
Isto é suficiente para testar a validade das horas trabalhadas.
![]() |
Acima, a compilação do projeto produziu os ficheiros [main3.html] e [main3.swf]. Abrimos o URL e o [http://localhost/pam-v10-flex-client-webservice/main3.html] num navegador e verificamos vários casos de erro:
![]() |
![]() |
- um campo com erro apresenta uma moldura vermelha ([1, 2, 3]), enquanto um campo correto apresenta uma moldura azul ([4]).
- Em [4], note-se que o botão [Salaire] está ativo porque o número de horas trabalhadas está correto.
14.6. A vista n.º 4
A vista n.º 4 conclui o formulário de cálculo do salário. Para tal, duplicamos [main3.xml] em [main4.xml] e passamos a trabalhar com main4, que definimos como aplicação por predefinição (ver parágrafo 14.4).
![]() |
As alterações introduzidas em [main4.xml] e [1] são as seguintes:
- foi adicionado um novo contentor vertical à vista [2] para apresentar os elementos do salário do colaborador
- foi adicionado um componente que permite formatar os valores monetários em [3]
- a exibição dos elementos do salário é gerida pelo gestor associado ao evento «clique» do botão «btnSalaire».
A vista evolui da seguinte forma:
![]() |
O novo contentor segue o princípio do anterior. Trata-se de um contentor vertical VBox [V1] que contém quatro contentores horizontais HBox [Hi]. Os contentores horizontais H1 a H3 são formados por contentores verticais que contêm duas legendas, sendo que a segunda se encontra, por sua vez, num contentor vertical para ter uma cor de fundo.
Questão 1: escreva o contentor do salário. Passará a ser designado por complements.
Questão 2: escreva os métodos que permitem ocultar/mostrar o contentor complements. Inspire-se no que foi feito anteriormente para o contentor employe.
Associamos um manipulador ao evento «click» do botão «btnSalaire»:
<mx:Button id="btnSalaire" label="Salaire" click="calculerSalaire()"/>
O método calculerSalaire é o seguinte:
private function calculerSalaire():void{
// preparação do formulário
affichageSalaire=true;
msg.text="";
// parâmetros de cálculo do salário
heuresTravaillees=Number(txtHeuresTravaillees.text);
joursDeTravail=int(joursTravailles.value);
// o salário é solicitado ao serviço web
pam.GetSalaire.send();
}
- linha 3: o valor booleano affichageSalaire serve para indicar se se deve ou não exibir o contentor complements, que apresenta os elementos do salário. O método getSalaireCompleted é executado em dois eventos:
- a mudança de funcionário na lista suspensa de funcionários, para apresentar as suas informações sem o salário. Nesse caso, define-se affichageSalaire=false.
- o cálculo do salário
- linha 6: o texto do campo de introdução txtHeuresTravaillees é convertido num número real.
- linha 7: o valor do incrementador joursTravailles é convertido num número inteiro.
- linha 9: chamada do método remoto GetSalaire. Recorde-se que este método espera três parâmetros, dos quais os parâmetros heuresTravaillees e joursDeTravail são inicializados nas linhas 6 e 7. Recorde-se também que, se a chamada assíncrona do método GetSalaire:
- for bem-sucedido, o método getSalaireCompleted será chamado
- falhar, o método getSalaireFault será chamado
Pergunta 3: completar o método atual getSalaireCompleted para que apresente o salário do funcionário caso o botão btnSalaire tenha sido clicado.
Atualmente, os elementos do salário são apresentados sem o símbolo do euro. É possível incluir esse símbolo no código ou utilizar um formatador. É isso que se propõe agora. O formatador será o seguinte:
<mx:CurrencyFormatter id="eurosFormatter" precision="2"
currencySymbol="€" useNegativeSign="true"
alignSymbol="right"/>
- linha 1: id é o identificador do formador, precision o número de casas decimais a manter.
- linha 2: currencySymbol é o símbolo monetário a utilizar. useNegativeSign indica se se deve ou não utilizar o sinal «-» para os valores negativos.
- linha 3: alignSymbol indica onde colocar o símbolo monetário em relação ao número.
Este formador é utilizado no código do script da seguinte forma:
- eurosFormatter é o ID do formatador a utilizar
- format é o método a chamar para formatar um número. Este método devolve uma cadeia de caracteres.
- feuilleSalaire.Indemnites.BaseHeure é, neste caso, o número a formatar.
- lblSH é o nome de um componente do tipo Text.
Questão 4: altere o método getSalaireCompleted para que utilize o formatador monetário.





























