14. La aplicación [SimuPaie] – versión 10 – un cliente Flex para un servicio web ASP.NET
Presentamos ahora un cliente Flex para el servicio web ASP.NET, versión 5. El IDE utilizado es Flex Builder 3. Se puede descargar una versión de demostración de este producto en la URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 es un IDE basado en Eclipse. Además, para ejecutar el cliente Flex, utilizamos un servidor web Apache de la herramienta Wamp [http://www.wampserver.com/]. Cualquier servidor Apache servirá. El navegador que muestre el cliente Flex debe tener instalado Flash Player versión 9 o superior.
Las aplicaciones Flex son únicas en el sentido de que se ejecutan dentro del complemento Flash Player del navegador. En este sentido, son similares a las aplicaciones Ajax, que incrustan scripts de JavaScript en las páginas enviadas al navegador, los cuales se ejecutan posteriormente dentro del navegador. Una aplicación Flex no es una aplicación web en el sentido habitual: es una aplicación cliente para servicios prestados por servidores web. En este sentido, es análoga a una aplicación de escritorio que actuaría como cliente de esos mismos servicios. Sin embargo, difiere en un aspecto: inicialmente se descarga desde un servidor web a un navegador equipado con el complemento Flash Player capaz de ejecutarla.
Al igual que una aplicación de escritorio, una aplicación Flex se compone principalmente de dos elementos:
- una capa de presentación: las vistas que se muestran en el navegador. Estas vistas ofrecen la misma riqueza que las ventanas de las aplicaciones de escritorio. Una vista se describe mediante un lenguaje de marcado llamado MXML.
- una sección de código que gestiona principalmente los eventos desencadenados por las acciones del usuario en la vista. Este código puede escribirse en MXML o en un lenguaje orientado a objetos llamado ActionScript. Hay dos tipos de eventos que deben distinguirse:
- eventos que requieren comunicación con el servidor web: rellenar una lista con datos proporcionados por una aplicación web, enviar datos de un formulario al servidor, etc. Flex proporciona varios métodos para comunicarse con el servidor de una forma transparente para el desarrollador. Estos métodos son asíncronos por defecto: el usuario puede seguir interactuando con la vista mientras la solicitud al servidor está en curso.
- eventos que modifican la vista mostrada sin intercambiar datos con el servidor, como arrastrar un elemento desde un árbol y soltarlo en una lista. Este tipo de evento se gestiona íntegramente de forma local dentro del navegador.
Una aplicación Flex suele ejecutarse de la siguiente manera:
![]() |
- en [1], se solicita una página HTML
- en [2], se envía. Incluye un archivo binario SWF (ShockWave Flash) que contiene toda la aplicación Flex: todas las vistas y su código de gestión de eventos. Este archivo será ejecutado por el complemento Flash Player del navegador.
![]() |
- El cliente Flex se ejecuta localmente en el navegador, salvo cuando necesita datos externos. En ese caso, los solicita al servidor [3]. Los recibe en [4] en diversos formatos: XML o binario. La aplicación consultada en el servidor web puede estar escrita en cualquier lenguaje. Solo importa el formato de la respuesta.
Hemos descrito la arquitectura de ejecución de una aplicación Flex para que el lector pueda comprender la diferencia entre esta y la de una aplicación web tradicional, en la que las páginas no incorporan código (JavaScript, Flex, Silverlight, etc.) que el navegador ejecutaría. En esta última, el navegador es pasivo: simplemente muestra páginas HTML creadas en el servidor web que se las envía.
14.1. Arquitectura de aplicación cliente/servidor
La arquitectura cliente/servidor implementada aquí es similar a la de las versiones 6 y 8:
![]() |
En [1], la capa web de ASP.NET se sustituye por una capa web de Flex escrita en MXML y ActionScript. El cliente [C] será generado por el IDE de Flex Builder. Cabe señalar aquí que esta arquitectura incluye dos servidores web que no se muestran:
- un servidor web ASP.NET que ejecuta el servicio web [S]
- un servidor web Apache que ejecuta el cliente web [1]
14.2. El proyecto del cliente Flex 3
Creamos el cliente Flex utilizando el IDE Flex Builder 3:
![]() |
- En Flex Builder 3, creamos un nuevo proyecto en [1]
- le damos un nombre en [2] y especificamos en [3] en qué carpeta se va a generar
![]() |
- en [4], nombramos la aplicación principal (la que se ejecutará)
- en [5], el proyecto una vez generado
- en [6], el archivo MXML principal de la aplicación
- Un archivo MXML contiene una vista y el código para gestionar sus eventos. La pestaña [Fuente] [7] permite acceder al archivo MXML. Allí encontrará etiquetas <mx> que describen la vista, así como código ActionScript.
- La vista se puede crear gráficamente utilizando la pestaña [Diseño] [8]. Las etiquetas MXML que describen la vista se generan automáticamente en la pestaña [Fuente]. Lo contrario también es cierto: las etiquetas MXML añadidas directamente en la pestaña [Fuente] se reflejan gráficamente en la pestaña [Diseño].
14.3. Vista n.º 1
Crearemos gradualmente una interfaz web similar a la de la Versión 1 (véase la Sección 4). En primer lugar, crearemos la siguiente interfaz:
![]() |
- en [1], la vista cuando la conexión al servicio web se ha realizado correctamente. A continuación, se rellena el menú desplegable de empleados.
- en [2], la vista cuando falla la conexión con el servicio web. A continuación, se muestra un mensaje de error.
El archivo principal del cliente [main.xml] es el siguiente:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
<mx:VBox width="100%">
<mx:Label text="Feuille de salaire" fontSize="30"/>
<mx:HBox>
<mx:VBox>
<mx:Label text="Employés"/>
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Heures travaillées"/>
<mx:TextInput id="txtHeuresTravaillees"/>
</mx:VBox>
<mx:VBox>
<mx:Label text="Jours travaillés"/>
<mx:NumericStepper id="joursTravailles" minimum="0" maximum="31" stepSize="1"/>
</mx:VBox>
<mx:VBox>
<mx:Label text=""/>
<mx:Button id="btnSalaire" label="Salaire"/>
</mx:VBox>
</mx:HBox>
<mx:TextArea id="msg" minWidth="400" minHeight="100" editable="false" visible="true" enabled="true" horizontalScrollPolicy="auto" verticalScrollPolicy="auto" x="0" y="0" maxHeight="100" maxWidth="400"/>
</mx:VBox>
<mx:WebService ...>
...
</mx:WebService>
<mx:Script>
<![CDATA[
...
// données
[Bindable]
private var employes : ArrayCollection;
private function init():void{
...
}
]]>
</mx:Script>
</mx:Application>
En este código, hay que distinguir varios elementos:
- la definición de la aplicación (líneas 2-3)
- la descripción de su vista (líneas 4-25)
- los controladores de eventos de ActionScript dentro de la etiqueta <mx:Script> (líneas 31-42)
- la definición del servicio web remoto (líneas 27-29)
Comencemos comentando la definición de la propia aplicación y la descripción de su vista:
- Líneas 2-3: define:
- la disposición de los componentes dentro del contenedor de la vista. El atributo layout="vertical" indica que los componentes se colocarán uno debajo del otro.
- el método que se ejecutará cuando se haya instanciado la vista, es decir, cuando se hayan instanciado todos sus componentes. El atributo creationComplete="init();" indica que se debe ejecutar el método init de la línea 38. creationComplete es uno de los eventos que puede emitir la clase Application.
- Las líneas 4–25 definen los componentes de la vista
- Líneas 4-25: un contenedor vertical: los componentes se colocarán uno debajo del otro
- Línea 5: define un componente de texto
- Líneas 6-23: un contenedor horizontal: los componentes se colocarán horizontalmente dentro de él.
- Líneas 7-10: un contenedor vertical que contendrá texto y una lista desplegable
- Línea 8: el texto
- línea 9: la lista desplegable en la que se mostrará la lista de empleados. La etiqueta dataProvider="{employees}" especifica la fuente de datos que debe rellenar la lista. En este caso, la lista se rellenará con el objeto «employees» definido en la línea 36. Para poder escribir dataProvider="{employees}", el campo «employees» debe tener el atributo [Bindable] (línea 35). Este atributo permite hacer referencia a una variable de ActionScript fuera de la etiqueta <mx:Script>. El campo employees es de tipo ArrayCollection, un tipo de ActionScript que permite almacenar listas de objetos, en este caso una lista de objetos de tipo Employee.
- Líneas 11-14: un contenedor vertical que contendrá texto y un campo de entrada
- línea 12: el texto
- Línea 13: el campo de entrada para las horas trabajadas.
- Líneas 15-18: un contenedor vertical que contendrá texto y un contador
- línea 16: el texto
- línea 17: el contador para introducir los días trabajados
- Líneas 19-22: un contenedor vertical que contendrá texto y un botón que activará el cálculo del salario de la persona seleccionada en el menú desplegable.
- Línea 20: el texto
- línea 21: el botón.
- línea 23: fin del contenedor horizontal iniciado en la línea 6
- Línea 24: un área de texto en un componente TextArea. Mostrará mensajes de error.
- Línea 25: fin del contenedor vertical iniciado en la línea 4
Las líneas 4–25 generan la siguiente vista en la pestaña [Diseño]:
![]() |
- [1]: generado por el componente Label de la línea 5
- [2]: fue generado por el componente ComboBox de la línea 9
- [3]: fue generado por el componente TextInput en la línea 13
- [4]: fue generado por el componente NumericStepper en la línea 17
- [5]: fue generado por el componente Button en la línea 21
- [6]: fue generado por el componente TextArea en la línea 24
Ahora examinemos la declaración del servicio 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>
- línea 1: el servicio web es un componente con el identificador «pam» (atributo id)
- línea 2: el URI del archivo WSDL del servicio web (véase la sección 9.2)
- línea 3: el método que se ejecutará en caso de error durante la comunicación con el servicio web: el método wsFault.
- línea 4: solicita que se muestre un indicador para indicar al usuario que se está produciendo un intercambio con el servicio web.
- Líneas 5-10: una de las operaciones que ofrece el servicio web remoto. En este caso, el método GetAllIdentitiesEmployees.
- línea 7: el método que se ejecutará cuando la llamada a este método se complete con éxito, es decir, cuando el servicio web devuelva correctamente la lista de empleados
- Línea 8: El método que se ejecutará cuando la llamada a este método termine con un error.
- Línea 9: los parámetros para la operación GetAllEmployeeIDs. Sabemos que este método no espera ningún parámetro. Por lo tanto, dejamos la etiqueta <mx:request> vacía.
Veamos ahora el código ActionScript vinculado al servicio web:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// données
[Bindable]
private var employes : ArrayCollection;
private function init():void{
// on note les coordonnées de la zone de message
msgHeight=msg.height;
msgWidth=msg.width;
// on cache la zone de message
hideMsg();
// requête au service web distant pour avoir la liste simplifiée des employés
pam.GetAllIdentitesEmployes.send();
}
private function wsFault(event:Event):void{
// on signale l'erreur
msg.text="Service distant indisponible";
showMsg();
}
private function loadEmployesCompleted(event:ResultEvent):void{
// remplissage combo des employés
employes=event.result as ArrayCollection;
}
private function displayEmploye(employe:Object):String{
// identité d'un employé
return employe.Prenom + " " + employe.Nom;
}
private function loadEmployesFault(event:FaultEvent):void{
// affichage msg d'erreur
msg.text=event.fault.message;
// formulaire
showMsg();
}
// gestion des blocs
private var msgWidth:int;
private var msgHeight:int;
private function hideMsg():void{
msg.height=0;
msg.width=0;
}
private function showMsg():void{
msg.height=msgHeight;
msg.width=msgWidth;
}
]]>
</mx:Script>
- Línea 11: El método init se ejecuta al iniciar la aplicación porque hemos escrito:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
- líneas 13-14: almacenamos la altura y la anchura del área de mensajes. Utilizamos dos métodos, hideMsg (líneas 48-51) y showMsg (líneas 53-56), para ocultar o mostrar el área de mensajes, respectivamente, dependiendo de si se ha producido un error o no. El método hideMsg oculta el área de mensajes estableciendo su altura y anchura en 0. El método showMsg muestra el área de mensajes restaurando la altura y la anchura almacenadas en el método init.
- Línea 16: El área de mensajes está oculta. Inicialmente, no hay ningún error.
- Línea 18: Se llama al método GetAllIdentitesEmploye (línea 6 del servicio web) del servicio web pam (línea 1 del servicio web). La llamada es asíncrona. La línea 7 del servicio web indica que se ejecutará el método loadEmployesCompleted si esta llamada asíncrona se completa con éxito. La línea 8 del servicio web indica que se ejecutará el método loadEmployesFault si esta llamada asíncrona falla.
- Línea 27: El método loadEmployesCompleted, que se ejecuta si la llamada al servicio web de la línea 18 se completa con éxito.
- Línea 29: Sabemos que el servicio web devuelve una respuesta XML. Resulta útil volver a consultar esto para comprender el código ActionScript:
![]() |
- en [1], la página del servicio web [Service.asmx]
- en [2], el enlace a la página de prueba para el método [GetAllIdentitesEmployes]
- en [3], se realiza la prueba. No se esperan parámetros.
- En [4]: La respuesta XML contiene una matriz de empleados. Para cada empleado, hay cinco datos encerrados entre las etiquetas <Id>, <Version>, <SS>, <LastName> y <FirstName>. Si la respuesta XML se almacena en una matriz `employees` de tipo `ArrayCollection`:
- employees.getItemAt(i): es el elemento i-ésimo de la matriz
- employees.getItemAt(i).SS: es el número de la Seguridad Social de este empleado.
- employees.getItemAt(i).LastName: es el apellido de ese empleado
- ...
Volvamos al código de ActionScript:
- línea 29: event.result representa la respuesta XML del servicio web. El método GetAllIdentitesEmployes devuelve una matriz de empleados. event.result representa esta matriz de empleados. Se almacena en una variable de tipo ArrayCollection, un tipo que generalmente representa una colección de objetos. Esta variable, denominada employees, se declara en la línea 9. Recuerda que esta variable es la fuente de datos del cuadro combinado de empleados:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
Para cada empleado de su fuente de datos, el cuadro combinado llamará al método displayEmploye (atributo labelFunction) para mostrar al empleado. Las líneas 32-34 muestran que este método muestra el nombre y los apellidos del empleado.
- Línea 37: El método loadEmployesFault, que se ejecuta si falla la llamada al servicio web de la línea 18. event.fault.message es el mensaje de error devuelto por el servicio web.
- Línea 39: Este mensaje de error se coloca en el cuadro de mensaje
- Línea 41: Se muestra el cuadro de mensaje.
Una vez compilada la aplicación, su código ejecutable se encuentra en la carpeta [bin-debug] del proyecto Flex:
![]() |
Arriba,
- el archivo [main.html] representa el archivo HTML que el navegador solicitará al servidor web para obtener el cliente Flex
- el archivo [main.swf] es el binario del cliente Flex que se incrustará en la página HTML enviada al navegador y que luego ejecutará el complemento Flash Player del navegador.
Ya estamos listos para ejecutar el cliente Flex. En primer lugar, debemos configurar el entorno de ejecución que requiere. Volvamos a la arquitectura cliente/servidor que probamos:
![]() |
Del lado del servidor:
- Iniciar el servicio web ASP.NET [S]
Lado del cliente:
- Inicie el servidor Apache que alojará la aplicación Flex.
Aquí estamos utilizando la herramienta Wamp. Con esta herramienta, podemos asignar un alias a la carpeta [bin-debug] del proyecto Flex.
![]() |
- El icono de Wamp se encuentra en la parte inferior de la pantalla [1]
- Haz clic con el botón izquierdo del ratón en el icono de Wamp, selecciona la opción Apache [2] / Directorios de alias [3, 4]
- Selecciona la opción [5]: Añadir un alias
![]() |
- En [6], asigne un alias (cualquier nombre) a la aplicación web que se ejecutará
- En [7], especifica la raíz de la aplicación web que utilizará este alias: se trata de la carpeta [bin-debug] del proyecto Flex que acabamos de crear.
Repasemos la estructura de la carpeta [bin-debug] del proyecto Flex:
![]() |
El archivo [main.html] es el archivo HTML de la aplicación Flex. Gracias al alias que acabamos de crear para la carpeta [bin-debug], se podrá acceder a este archivo a través de la URL [http://localhost/pam-v10-flex-client-webservice/main.html]. Accedemos a esta URL en un navegador con el complemento Flash Player versión 9 o superior:
![]() |
- en [1], la URL de la aplicación Flex
- en [2], el menú desplegable de empleados cuando todo funciona correctamente
- en [3], el resultado cuando el servicio web está detenido
Quizás te interese ver el código fuente de la página HTML recibida:
- El cuerpo de la página comienza en la línea 25. No contiene HTML estándar, sino un objeto (línea 28) de tipo «application/x-shockwave-flash» (línea 41). Se trata del archivo [main.swf] (línea 31) que se encuentra en la carpeta [bin-debug] del proyecto Flex. Es un archivo de gran tamaño: aproximadamente 600 KB para este sencillo ejemplo.
14.4. Vista n.º 2
Añadiremos un nuevo contenedor VBox a la vista actual:
![]() |
![]() |
- En [4,5], establecimos [main2.mxml] como la nueva aplicación predeterminada. Esta es la que ahora se compilará.
- En [6], la aplicación predeterminada se indica con un punto azul.
El contenedor [1] mostrará información sobre el empleado seleccionado en el cuadro combinado [2]. Duplicamos [main.xml] como [main2.xml] [3] para crear la nueva vista. Ahora trabajaremos con [main2.xml].
![]() |
El cambio realizado en el proyecto anterior es la adición del contenedor en la línea 26 anterior, que contiene el código MXML para el contenedor [1] de la vista. Le asignamos el identificador employe para poder manipularlo mediante código. Este contenedor debe poder ocultarse o mostrarse utilizando la misma técnica empleada anteriormente para el área de mensajes.
Volvamos al diseño visual de la vista:
![]() |
Identifiquemos los diferentes contenedores para la nueva información mostrada:
- V1: contenedor vertical para todos los componentes: la etiqueta «Empleado [1]» y los contenedores horizontales [H1] y [H2]
- H1: contenedor horizontal para la información de Apellidos, Nombre y Dirección
- V2: contenedor vertical para la etiqueta «Apellido» y la visualización del apellido del empleado.
- H2: contenedor horizontal para la información de Ciudad, Código postal e Índice
El código completo para el contenedor «employe» es el siguiente:
<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>
El código se explica por sí mismo. Veamos brevemente el contenedor vertical que muestra el nombre del empleado, por ejemplo:
- líneas 4–9: el contenedor vertical
- línea 5: la etiqueta «Nombre»
- Líneas 6-8: un contenedor vertical que mostrará el nombre del empleado (línea 7). Queremos dar a los campos que muestran la información del empleado un color de fondo diferente. El componente Text no ofrece esta opción (o quizá no busqué lo suficiente). Se puede establecer el color de fondo de un contenedor. Por eso se ha utilizado aquí.
- Línea 7: el componente Texto que mostrará el nombre del empleado. Le hemos establecido una altura y una anchura mínimas.
Utilizaremos el contenedor «employee» para mostrar la información del empleado que el usuario seleccione en el menú desplegable de empleados, independientemente del botón [Salario], que se utilizará más adelante para calcular el salario una vez que se haya introducido toda la información necesaria.
Para gestionar el cambio en la selección del cuadro combinado «empleados», su código MXML cambia de la siguiente manera:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>
El evento de cambio es activado por el cuadro combinado cuando el usuario cambia su selección. El controlador de este evento será el método displayInfosEmploye.
Repasemos los métodos expuestos por el servicio web remoto:
// liste de toutes les identités des employés
public Employe[] GetAllIdentitesEmployes();
// ------- le calcul du salaire
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);
Aquí queremos mostrar la información (apellidos, nombre, etc.) del empleado seleccionado en el menú desplegable. El servicio web no expone ningún método para recuperar esta información. Sin embargo, podemos utilizar el método GetSalary pasando el número de la Seguridad Social del empleado seleccionado y 0 para las horas y los días trabajados. Se realizará un cálculo redundante del salario, pero el método GetSalary devolverá un objeto PayStub que contiene la información que necesitamos.
La declaración actual del servicio web se modifica para incluir la definición del 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>
- líneas 11-19: la definición del método GetSalary del servicio web
- línea 12: define el método que se ejecutará cuando la llamada al método GetSalaire tenga éxito
- línea 13: define el método que se ejecutará cuando falle la llamada al método GetSalaire
- líneas 14-18: el método GetSalaire espera tres parámetros. Se definen dentro de una etiqueta <mx:request> con el formato <param1>valor1</param1>. El identificador param1 no puede ser arbitrario. Debe utilizar los nombres esperados por el servicio web:
![]() |
- en [1], la página del servicio web [http://localhost:1077/Service1.asmx]
- en [2], el enlace a la página de prueba del método [GetSalaire]
- en [3], los parámetros que espera el método. Estos son los nombres que deben utilizarse como etiquetas secundarias de la etiqueta <mx:request>.
Volvamos a la declaración del servicio 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>
- Línea 5: el parámetro ss. Recuerda que, al iniciarse la aplicación Flex, la matriz de todos los empleados se almacenó en una variable llamada employees de tipo ArrayCollection.
- employees.getItemAt(i): es el empleado número i de la matriz
- employees.getItemAt(i).SS: es el número de la Seguridad Social de este empleado.
- cmbEmployees.selectedIndex: es el índice del elemento seleccionado en el cuadro combinado de empleados cmbEmployees.
En el código anterior, ¿cómo sabemos que SS es el número de la seguridad social de un empleado? Para responder a esto, debemos remitirnos a la respuesta enviada por el método GetAllIdentitiesEmployees:
![]() |
- en [1], la página del servicio web [Service.asmx]
- en [2], el enlace a la página de prueba del método [GetAllIdentitesEmployes]
- en [3], se realiza la prueba. No se esperan parámetros.
- En [4]: La respuesta XML contiene una matriz de empleados. Esta matriz se almacena en la variable `employees`. Como se muestra en [5], `SS` es, efectivamente, la etiqueta utilizada para almacenar el número de la Seguridad Social.
Concluyamos nuestro análisis del servicio 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>
- línea 6: el número de horas trabajadas vendrá dado por una variable horasTrabajadas
- línea 6: el número de días trabajados se proporcionará mediante una variable daysWorked
Estas variables deben declararse dentro de la etiqueta <mx:Script> con el atributo [Bindable], lo que permite que los componentes MXML hagan referencia a ellas (líneas 7-10 a continuación).
<mx:Script>
<![CDATA[
...
// données
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
...
</mx:Script>
El código de gestión de eventos de la vista cambia de la siguiente manera:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// données
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
private function init():void{
// on note les hauteur / largeur de # blocs
employeHeight=employe.height;
employeWidth=employe.width;
// on cache certains éléments
hideEmploye();
...
}
private function displayInfosEmploye():void{
// formulaire
hideEmploye();
// on calcule un salaire fictif
heuresTravaillees=0;
joursDeTravail=0;
pam.GetSalaire.send();
}
private function getSalaireCompleted(event:ResultEvent):void{
...
}
private function getSalaireFault(event:FaultEvent):void{
...
}
// vues partielles -------------------------------------------------
private var employeHeight:int;
private var employeWidth:int;
private function hideEmploye():void{
employe.height=0;
employe.width=0;
}
private function showEmploye():void{
employe.height=employeHeight;
employe.width=employeWidth;
}
]]>
</mx:Script>
- Línea 15: El método init, que se ejecuta al iniciar la aplicación Flex, almacena la altura y la anchura del contenedor vertical de empleados para que pueda restaurarse (líneas 50-53) tras haber sido ocultado (líneas 45-48).
- Línea 24: El método displayInfosEmploye se ejecuta cuando el usuario cambia su selección en el cuadro combinado de empleados.
- Línea 26: El contenedor de empleados se oculta si antes estaba visible
- Línea 30: Se llama de forma asíncrona al método GetSalary del servicio web. Sabemos que espera tres parámetros:
<ss>{employes.getItemAt(cmbEmployes.selectedIndex).SS}</ss>
<heuresTravaillees>{heuresTravaillees}</heuresTravaillees>
<joursTravailles>{joursDeTravail}</joursTravailles>
- Línea 1: El parámetro ss será el número de la Seguridad Social del empleado seleccionado en el cuadro combinado de empleados
- línea 2: el método displayInfosEmploye asigna el valor 0 a la variable horasTrabajadas (línea 28)
- línea 3: el método displayInfosEmploye asigna el valor 0 a la variable daysWorked (línea 29)
El método GetSalaireCompleted se ejecuta si el método GetSalaire del servicio web se completa con éxito:
private function getSalaireCompleted(event:ResultEvent):void{
// hide error msg
hideMsg();
// you receive a payslip
var feuilleSalaire:Object=event.result;
// display
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indice;
showEmploye();
}
- Línea 3: Ocultamos el cuadro de mensaje en caso de que se muestre.
- Línea 5: Recuperamos el recibo de nómina devuelto por el método GetSalary
Para saber exactamente qué devuelve el método GetSalaire, volvemos a la página del servicio web:
![]() |
- [1] es la página del servicio web [Service.asmx]
- en [2], el enlace que lleva a la página de prueba del método [GetSalaire]
- en [3], proporcionamos los parámetros
- en [4], el XML resultante.
Volvamos al método getSalaireCompleted:
private function getSalaireCompleted(event:ResultEvent):void{
// hide error msg
hideMsg();
// you receive a payslip
var feuilleSalaire:Object=event.result;
// display
lblNom.text=feuilleSalaire.Employe.Nom;
lblPreNom.text=feuilleSalaire.Employe.Prenom;
lblAdresse.text=feuilleSalaire.Employe.Adresse;
lblVille.text=feuilleSalaire.Employe.Ville;
lblCodePostal.text=feuilleSalaire.Employe.CodePostal;
lblIndice.text=feuilleSalaire.Employe.Indemnites.Indice;
showEmploye();
}
- Línea 5: PayrollSheet = event.result representa el flujo XML [4] devuelto por el método GetSalary. A partir de este flujo, podemos ver que:
- payrollSheet.Employee es el flujo XML de un empleado
- sheetSalary.Employee.Name es el nombre de este empleado
- ...
- líneas 7-12: el feed XML Payroll.Employee se utiliza para rellenar los distintos campos del contenedor del empleado.
- línea 13: se muestra el contenedor del empleado.
El método getSalaireFault se ejecuta si falla el método GetSalaire del servicio web:
private function getSalaireFault(event:FaultEvent):void{
// error msg display
msg.text=event.fault.message;
// form
showMsg();
}
- Línea 3: El mensaje de error event.fault.message se coloca en el cuadro de mensaje
- línea 5: se muestra el cuadro de mensaje
Con esto concluyen los cambios necesarios para esta nueva versión. Al guardarlo, si la sintaxis es correcta, se genera la versión ejecutable en la carpeta [bin-debug] del proyecto:
![]() |
Arriba, [main2.html] es la página HTML que incrusta el binario de la aplicación Flex [main2.swf], que será ejecutado por Flash Player.
Podemos probar esta nueva versión:
- el servicio web ASP.NET debe estar en ejecución
- el servidor Apache debe estar en ejecución para el cliente Flex
Suponiendo que el alias [pam-v10-flex-client-webservice] utilizado en la versión anterior sigue existiendo, solicitamos la URL [http://localhost/pam-v10-flex-client-webservice/main2.html] al servidor Apache en un navegador:
![]() |
![]() |
- en [1], la URL solicitada
- en [2], el menú desplegable de empleados
- en [3], cambia la selección en el menú desplegable para activar el evento de cambio
- en [4], el resultado obtenido: el perfil de Justine Laverti.
14.5. Vista n.º 3
La vista 3 se encarga de la validación del formulario. Aquí solo se comprueba el campo de entrada «txtHeuresTravaillees». Mientras el formulario no sea válido, el botón «btnSalaire» permanecerá desactivado.
Para añadir esta funcionalidad, duplicamos [main2.mxml] en [main3.mxml]:
![]() |
A partir de ahora, trabajaremos con [main3.mxml], que estableceremos como la aplicación predeterminada (véase este concepto en la sección 14.4). En primer lugar, añadimos un atributo al componente «txtHeuresTravaillees»:
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
Cada vez que cambia el contenido del campo de entrada «txtHeuresTravaillees», se llama al método validateForm. Se trata de un método local escrito por el desarrollador. En él, podríamos verificar que el contenido del campo de entrada «txtHeuresTravaillees» sea efectivamente un entero positivo. Procederemos de otra manera utilizando un componente de validación:
<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"/>
- línea 1: El componente <mx:NumberValidator> verifica que otro componente contenga un número entero o un número real.
- línea 1: El atributo id asigna un identificador al componente.
- línea 1: source es el ID del componente que está siendo validado por el componente NumberValidator. En este caso, se está validando el campo de entrada «txtHeuresTravaillees».
- Línea 1: property es el nombre de la propiedad del componente source que contiene el valor que se va a validar. En última instancia, es el valor source.property el que se valida, en este caso txtHeuresTravaillees.text.
- Línea 2: precision establece el número máximo de decimales permitidos. precision=0 significa que el número introducido debe ser un entero.
- Línea 2: allowNegative especifica si se permiten o no los números negativos
- Línea 7: required especifica si la entrada es obligatoria o no.
Cuando no se cumple una condición de validación, se muestra un mensaje de error en una información sobre herramientas cerca del componente no válido. De forma predeterminada, estos mensajes están en inglés. Puede definir estos mensajes usted mismo:
- (continuación)
- invalidCharError: el mensaje de error cuando el texto contiene un carácter que no puede aparecer en un número
- precisionError: el mensaje de error cuando el número de decimales es incorrecto en relación con el atributo precision
- negativeError: el mensaje de error cuando el número es negativo pero el atributo allowNegative="false" está activado
- requiredFieldError: el mensaje de error cuando no se ha proporcionado ninguna entrada a pesar de que el atributo requiredField="true" está establecido
- invalidFormatCharsError: ¿el mensaje de error que aparece cuando el texto contiene caracteres no válidos o tiene un formato incorrecto?
Volvamos al componente «txtHeuresTravaillees»:
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
El método validateForm podría ser el siguiente dentro de la etiqueta <mx:Script>:
private function validateForm(event:Event):void
{
// validate hours worked
var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
// successful validation?
btnSalaire.enabled=evt.type==ValidationResultEvent.VALID;
}
- Línea 4: Se ejecuta el validador «heuresTravailleesValidator». Devuelve un resultado de tipo ValidationResultEvent.
- Línea 6: evt.type es de tipo String e indica el tipo de evento. evt.type tiene dos valores posibles para el tipo ValidationResultEvent: «invalid» o «valid», representados por las constantes ValidationResultEvent.INVALID y ValidationResultEvent.VALID. Si, en la línea 4, la validación se ha realizado correctamente, evt.type debe tener el valor ValidationResultEvent.VALID. En este caso, el botón btnSalaire está habilitado; de lo contrario, está deshabilitado.
Esto es suficiente para comprobar la validez de las horas trabajadas.
![]() |
Anteriormente, al compilar el proyecto se generaron los archivos [main3.html] y [main3.swf]. Introducimos la URL [http://localhost/pam-v10-flex-client-webservice/main3.html] en un navegador y comprobamos si se producen diversos casos de error:
![]() |
![]() |
- un campo incorrecto tiene un borde rojo [1, 2, 3], un campo correcto tiene un borde azul [4].
- En [4], fíjate en que el botón [Salario] está activo porque el número de horas trabajadas es correcto.
14.6. Vista n.º 4
La vista 4 completa el formulario de cálculo del salario. Para ello, duplicamos [main3.xml] en [main4.xml] y ahora trabajamos con main4, que establecemos como la aplicación predeterminada (véase la sección 14.4).
![]() |
Los cambios realizados en [main4.xml] [1] son los siguientes:
- se ha añadido un nuevo contenedor vertical a la vista [2] para mostrar los componentes salariales del empleado
- se añade un componente para dar formato a los valores monetarios [3]
- la visualización de los componentes salariales se gestiona mediante el controlador asociado al evento «click» del botón «btnSalaire».
La vista evoluciona de la siguiente manera:
![]() |
El nuevo contenedor sigue el mismo principio que el anterior. Se trata de un contenedor VBox vertical [V1] que contiene cuatro contenedores HBox horizontales [Hi]. Los contenedores horizontales H1 a H3 están formados por contenedores verticales que contienen dos etiquetas, la segunda de las cuales se encuentra a su vez dentro de un contenedor vertical para proporcionar un color de fondo.
Pregunta 1: Escribe el contenedor «salary». En lo sucesivo se le denominará «complements».
Pregunta 2: Escribe los métodos para ocultar/mostrar el contenedor «complements». Inspírate en lo que se hizo anteriormente para el contenedor «employee».
Asociamos un controlador al evento «click» del botón «btnSalaire»:
<mx:Button id="btnSalaire" label="Salaire" click="calculerSalaire()"/>
El método *calculerSalaire* es el siguiente:
private function calculerSalaire():void{
// form preparation
affichageSalaire=true;
msg.text="";
// salary calculation parameters
heuresTravaillees=Number(txtHeuresTravaillees.text);
joursDeTravail=int(joursTravailles.value);
// the salary is requested from the web service
pam.GetSalaire.send();
}
- Línea 3: La variable booleana displaySalary se utiliza para indicar si se debe mostrar o no el contenedor de complementos, que muestra los detalles del salario. El método getSalaryCompleted se ejecuta en dos casos:
- cuando se cambia de empleado en el menú desplegable de empleados para mostrar su información sin el salario. En este caso, se establece salaryDisplay=false.
- cálculo del salario
- Línea 6: El texto del campo de entrada txtHeuresTravaillees se convierte en un número real.
- Línea 7: El valor del contador daysWorked se convierte en un entero.
- Línea 9: Se llama al método remoto GetSalary. Tenga en cuenta que este método espera tres parámetros, incluidos los parámetros hoursWorked y daysWorked inicializados en las líneas 6 y 7. Tenga en cuenta también que si la llamada asíncrona al método GetSalary:
- tiene éxito, se llamará al método getSalaireCompleted
- falla, se llamará al método getSalaireFault
Pregunta 3: Completa el método actual «getSalaireCompleted» para que muestre el salario del empleado si se ha pulsado el botón «btnSalaire».
Actualmente, las partidas salariales se muestran sin el símbolo del euro. Puedes incluirlo en el código o utilizar un formateador. Este es el enfoque que estamos adoptando ahora. El formateador será el siguiente:
<mx:CurrencyFormatter id="eurosFormatter" precision="2"
currencySymbol="€" useNegativeSign="true"
alignSymbol="right"/>
- Línea 1: id es el identificador del formateador, precision es el número de decimales que se deben mantener.
- Línea 2: currencySymbol es el símbolo de la moneda que se va a utilizar. useNegativeSign indica si se debe utilizar o no el signo menos para los valores negativos.
- Línea 3: alignSymbol especifica dónde colocar el símbolo de la moneda en relación con el número.
Este formateador se utiliza en el código del script de la siguiente manera:
- eurosFormatter es el ID del formateador que se va a utilizar
- format es el método que se debe llamar para dar formato a un número. Devuelve una cadena.
- feuilleSalaire.Indemnites.BaseHeure es el número que se va a formatear.
- lblSH es el nombre de un componente de texto.
Pregunta 4: Modifica el método getSalaireCompleted para que utilice el formateador de moneda.





























