14. La aplicación [SimuPaie] —versión 10— cliente Flex de un servicio web ASP.NET
Ahora presentamos un cliente Flex del servicio web ASP.NET de la versión 5. El IDE utilizado es Flex Builder 3. Se puede descargar una versión de demostración de este producto en URL [https://www.adobe.com/cfusion/tdrc/index.cfm?loc=fr_fr&product=flex]. Flex Builder 3 es un IDE Eclipse. Además, para ejecutar el cliente Flex, utilizamos un servidor web Apache de la herramienta Wamp [http://www.wampserver.com/]. Cualquier servidor Apache sirve. El navegador que muestra el cliente Flex debe contar con el complemento Flash Player, versión 9 como mínimo.
Las aplicaciones Flex tienen la particularidad de ejecutarse dentro del complemento Flash Player del navegador. En este sentido, se asemejan a las aplicaciones Ajax, que incorporan en las páginas enviadas al navegador scripts JavaScript que luego se ejecutan dentro del navegador. Una aplicación Flex no es una aplicación web en el sentido en que se entiende habitualmente: es una aplicación cliente de servicios proporcionados por servidores web. En este sentido, es análoga a una aplicación de escritorio que fuera cliente de esos mismos servicios. Sin embargo, difiere en un punto: se descarga inicialmente desde un servidor web en un navegador que cuente 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 parte de presentación: las vistas que se muestran en el navegador. Estas vistas tienen la riqueza de las ventanas de las aplicaciones de escritorio. Una vista se describe mediante un lenguaje de etiquetas llamado MXML.
- una parte de código que gestiona principalmente los eventos provocados por las acciones del usuario en la vista. Este código también puede escribirse en MXML o con un lenguaje orientado a objetos llamado ActionScript. Hay que distinguir entre dos tipos de eventos:
- el evento que requiere un intercambio con el servidor web: llenar una lista con datos proporcionados por una aplicación web, enviar los datos de un formulario al servidor, etc. Flex ofrece varios métodos para comunicarse con el servidor de manera transparente para el desarrollador. Estos métodos son asincrónicos por defecto: el usuario puede seguir interactuando con la vista mientras se realiza la solicitud al servidor.
- el evento que modifica la vista mostrada sin intercambio de datos con el servidor, por ejemplo, arrastrar un elemento de un árbol para colocarlo en una lista. Este tipo de evento se procesa completamente de manera 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. Esta incluye un archivo binario SWF (ShockWave Flash) que contiene la aplicación Flex completa: todas las vistas y el código de manejo de eventos de estas. Este archivo será ejecutado por el complemento Flash Player del navegador.
![]() |
- La ejecución del cliente Flex se realiza localmente en el navegador, excepto 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. Lo único que importa es el formato de la respuesta.
Hemos descrito la arquitectura de ejecución de una aplicación Flex para que el lector perciba la diferencia entre esta y la de una aplicación web convencional, en la que las páginas no incorporan código (JavaScript, Flex, Silverlight, etc.) que el navegador ejecutaría. En este último caso, el navegador es pasivo: simplemente muestra páginas HTML generadas en el servidor web que se las envía.
14.1. Arquitectura de la aplicación cliente/servidor
La arquitectura cliente/servidor implementada es similar a la de las versiones 6 y 8:
![]() |
En [1], la capa web ASP.NET se reemplaza por una capa web Flex escrita en MXML y ActionScript. El cliente [C] será generado por el Flex Builder IDE. Cabe recordar 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 Flex 3 del cliente
Creamos el cliente Flex con Flex Builder 3 (IDE):
![]() |
- En Flex Builder 3, creamos un nuevo proyecto en [1]
- le damos un nombre en [2] y especificamos en [3] en qué carpeta generarlo
![]() |
- En [4], se le da un nombre a la aplicación principal (la que se ejecutará)
- en [5], el proyecto una vez generado
- en [6], el archivo principal de la aplicación MXML
- un archivo MXML contiene una vista y el código de manejo de eventos de la misma. La pestaña [Source] [7] da acceso al archivo MXML. En él se encuentran las etiquetas <mx> que describen la vista, así como el código ActionScript.
- La vista se puede crear gráficamente utilizando la pestaña [Design] [8]. Las etiquetas MXML que describen la vista se generan automáticamente en la pestaña [Source]. Lo contrario también es cierto: las etiquetas MXML que se agregan directamente en la pestaña [Source] se reflejan gráficamente en la pestaña [Design].
14.3. La vista n.º 1
Vamos a construir gradualmente una interfaz web similar a la de la versión 1 (véase el párrafo 4). En primer lugar, construiremos la siguiente interfaz:
![]() |
- en [1], la vista cuando se ha podido establecer la conexión con el servicio web. En ese caso, el menú desplegable de empleados se completa.
- en [2], la vista cuando no se pudo establecer la conexión con el servicio web. En ese caso, 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[
...
// datos
[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 la vista de la misma (líneas 4-25)
- los controladores de eventos en lenguaje ActionScript dentro de la etiqueta <mx:Script> (líneas 31-42).
- la definición del servicio web remoto (líneas 27-29)
Para empezar, comentemos la definición de la aplicación en sí y la descripción de su vista:
- líneas 2-3: definen:
- el modo de disposición de los componentes en el 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, c.a.d. el momento en que 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 texto
- líneas 6-23: un contenedor horizontal: los componentes se colocarán horizontalmente en él.
- líneas 7-10: un contenedor vertical que contendrá un texto y una lista desplegable
- línea 8: el texto
- línea 9: la lista desplegable en la que se colocará la lista de empleados. La etiqueta dataProvider="{employes}" indica la fuente de los datos que deben completar la lista. Aquí, la lista se completará con el objeto employes definido en la línea 36. Para poder escribir dataProvider="{employes}", es necesario que el campo employes tenga el atributo [Bindable] (línea 35). Este atributo permite que se haga referencia a una variable ActionScript fuera de la etiqueta <mx:Script>. El campo employes es de tipo ArrayCollection, un tipo ActionScript que permite almacenar listas de objetos, en este caso una lista de objetos de tipo Employe.
- líneas 11-14: un contenedor vertical que contendrá un texto y un campo de entrada
- línea 12: el texto
- línea 13: el campo de entrada de las horas trabajadas.
- líneas 15-18: un contenedor vertical que contendrá un texto y un contador
- línea 16: el texto
- línea 17: el contador que permitirá ingresar los días trabajados.
- líneas 19-22: un contenedor vertical que incluirá un 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 cuadro de texto en un componente de tipo TextArea. Mostrará los mensajes de error.
- línea 25: fin del contenedor vertical iniciado en la línea 4
Las líneas 4 a 25 generan la siguiente vista en la pestaña [Design]:
![]() |
- [1]: fue 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 de la línea 13
- [4]: fue generado por el componente NumericStepper de la línea 17
- [5]: fue generado por el componente Button de la línea 21
- [6]: fue generado por el componente TextArea de la línea 24
Ahora veamos 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 tiene el identificador pam (atributo id)
- línea 2: el URI del archivo WSDL del servicio web (véase el párrafo 9.2)
- línea 3: el método que se debe ejecutar en caso de error durante los intercambios con el servicio web: el método wsFault.
- línea 4: solicita que se muestre un indicador para informar al usuario de que se está realizando 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 GetAllIdentitesEmployes.
- línea 7: el método que se ejecutará cuando la llamada a este método finalice normalmente, c.a.d, 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 de la operación GetAllIdentitesEmployes. Sabemos que este método no espera parámetros. Por lo tanto, dejamos la etiqueta <mx:request> vacía.
Ahora veamos el código ActionScript relacionado con el servicio web:
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;
// datos
[Bindable]
private var employes : ArrayCollection;
private function init():void{
// se anotan los datos del campo de mensaje
msgHeight=msg.height;
msgWidth=msg.width;
// se oculta el área de mensajes
hideMsg();
// solicitud al servicio web remoto para obtener la lista simplificada de empleados
pam.GetAllIdentitesEmployes.send();
}
private function wsFault(event:Event):void{
// se reporta el error
msg.text="Service distant indisponible";
showMsg();
}
private function loadEmployesCompleted(event:ResultEvent):void{
// Se completa el menú desplegable de empleados
employes=event.result as ArrayCollection;
}
private function displayEmploye(employe:Object):String{
// identidad de un empleado
return employe.Prenom + " " + employe.Nom;
}
private function loadEmployesFault(event:FaultEvent):void{
// Visualización del mensaje de error
msg.text=event.fault.message;
// formulario
showMsg();
}
// gestión de bloques
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 se escribió:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="init()">
- líneas 13-14: se guardan la altura y el ancho del área de mensajes. Se utilizan 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 hubo un error o no. El método hideMsg oculta el área de mensajes estableciendo su altura y ancho en 0. El método showMsg muestra el área de mensajes restableciendo su altura y ancho, tal como se almacenaron en el método init.
- línea 16: se oculta el área de mensajes. Al inicio, no hay ningún error.
- línea 18: se invoca el 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 asincrónica se completa con éxito. La línea 8 del servicio web indica que se ejecutará el método loadEmployesFault si esta llamada asincrónica 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. Es conveniente volver a ella para comprender el código ActionScript:
![]() |
- en [1], página del servicio web [Service.asmx]
- en [2], el enlace a la página de prueba del método [GetAllIdentitesEmployes]
- en [3], la prueba se ha realizado. No se espera ningún parámetro.
- en [4]: la respuesta XML contiene una tabla de empleados. Para cada uno de ellos, hay cinco datos encapsulados en las etiquetas <Id>, <Version>, <SS>, <Nom> y <Prenom>. Si la respuesta XML se coloca en una tabla employes de tipo ArrayCollection:
- employes.getItemAt(i): es el elemento n.º i de la tabla
- employes.getItemAt(i). SS: es el número de seguro social de este empleado.
- employes.getItemAt(i).Nombre: es el nombre de este empleado
- ...
Volvamos al código 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 asigna a una variable de tipo ArrayCollection, un tipo que, en general, representa una colección de objetos. Esta variable, llamada employes, se declara en la línea 9. Recordemos que esta variable es la fuente de datos del menú desplegable de empleados:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye"/>
Para cada empleado de su fuente de datos, el menú desplegable llamará al método displayEmploye (atributo labelFunction) para mostrar al empleado. En las líneas 32 a 34 vemos que este método muestra el nombre y el apellido del empleado.
- línea 37: el método loadEmployesFault, que se ejecuta si la llamada al servicio web de la línea 18 falla. event.fault.message es el mensaje de error devuelto por el servicio web.
- línea 39: este mensaje de error se coloca en el campo de mensaje
- línea 41: se muestra el área de mensajes.
Una vez compilada la aplicación, su código ejecutable se encuentra en la carpeta [bin-debug] del proyecto Flex:
![]() |
En el ejemplo anterior,
- 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 encapsulará en la página HTML enviada al navegador y luego se ejecutará mediante el complemento Flash Player de este.
Estamos listos para ejecutar el cliente Flex. Antes, debemos configurar el entorno de ejecución que necesita. Volvamos a la arquitectura cliente/servidor que hemos probado:
![]() |
Lado del servidor:
- iniciar el servicio web ASP.NET [S]
Del lado del cliente:
- iniciar el servidor Apache al que se le solicitará la aplicación Flex.
Aquí utilizamos la herramienta Wamp. Con esta herramienta, podemos asociar un alias a la carpeta [bin-debug] del proyecto Flex.
![]() |
- El ícono de Wamp se encuentra en la parte inferior de la pantalla [1]
- Al hacer clic con el botón izquierdo en el ícono Wamp, selecciona la opción Apache [2] / Alias Directories [3, 4]
- selecciona la opción [5]: Agregar un alias
![]() |
- en [6], asigna un alias (cualquier nombre) a la aplicación web que se ejecutará
- en [7], indica la raíz de la aplicación web que llevará este alias: es la carpeta [bin-debug] del proyecto Flex que acabamos de crear.
Recordemos 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 en la carpeta [bin-debug], este archivo se podrá abrir mediante URL [http://localhost/pam-v10-flex-client-webservice/main.html]. Lo abrimos en un navegador que cuente con el complemento Flash Player versión 9 o superior:
![]() |
- en [1], el URL de la aplicación Flex
- en [2], el menú desplegable de empleados cuando todo funciona correctamente
- en [3], el resultado obtenido cuando el servicio web está inactivo
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 el HTML clásico, sino un objeto (línea 28) de tipo «application/x-shockwave-flash» (línea 41). Es el archivo [main.swf] (línea 31) el que se puede ver en la carpeta [bin-debug] del proyecto Flex. Se trata de un archivo de gran tamaño: aproximadamente 600 K para este sencillo ejemplo.
14.4. La vista n.º 2
Vamos a agregar un nuevo contenedor de tipo VBox a la vista actual:
![]() |
![]() |
- En [4,5], establecemos [main2.mxml] como la nueva aplicación predeterminada. A partir de ahora, esta será la que se compile.
- En [6], la aplicación predeterminada se identifica con un punto azul.
El contenedor [1] mostrará la información sobre el empleado seleccionado en el menú desplegable [2]. Duplicamos [main.xml] en [main2.xml] y [3] para crear la nueva vista. A partir de ahora trabajaremos con [main2.xml].
![]() |
La modificación realizada en el proyecto anterior consiste en agregar el contenedor de la línea 26 anterior, que contiene el código MXML del contenedor [1] de la vista. Le asignamos el identificador employe para poder manipularlo mediante código. De hecho, este contenedor deberá poder ocultarse o mostrarse mediante la misma técnica utilizada anteriormente para el área de mensajes.
Volvamos al diseño de la vista:
![]() |
Identifiquemos los diferentes contenedores de la nueva información que se muestra:
- V1: contenedor vertical de todos los componentes: la etiqueta Employé, [1] y los contenedores horizontales [H1] y [H2]
- H1: contenedor horizontal para la información Nom, Prénom, Adresse
- V2: contenedor vertical para el texto Nom y la visualización del nombre del empleado.
- H2: contenedor horizontal para la información de Ville, código postal, Indice
El código completo del 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 entiende por sí mismo. Explicaremos simplemente el contenedor vertical que muestra el nombre del empleado, por ejemplo:
- líneas 4-9: el contenedor vertical
- línea 5: la etiqueta Nom
- líneas 6-8: un contenedor vertical que mostrará el nombre del empleado (línea 7). Queremos asignar un color de fondo diferente a los campos que muestran información sobre el empleado. El componente Text no ofrece esta posibilidad (o tal vez no lo busqué bien). Se puede establecer el color de fondo de un contenedor. Por eso se utilizó aquí.
- línea 7: el componente Text, que mostrará el nombre del empleado. Se le asignan una altura y un ancho mínimos.
Vamos a utilizar el contenedor «employe» para mostrar la información del empleado que el usuario seleccione en el menú desplegable de empleados, independientemente del botón [Salaire], cuya función será, más adelante, calcular el salario una vez que se haya ingresado toda la información necesaria.
Para gestionar el cambio de selección en el menú desplegable «employes», su código MXML cambia de la siguiente manera:
<mx:ComboBox id="cmbEmployes" dataProvider="{employes}" labelFunction="displayEmploye" change="displayInfosEmploye();"/>
El evento «change» es emitido por el menú desplegable cuando el usuario cambia su selección. El manejador de este evento será el método displayInfosEmploye.
Recordemos los métodos expuestos por el servicio web remoto:
// lista de todas las identidades de los empleados
public Employe[] GetAllIdentitesEmployes();
// ------- Cálculo del salario
public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles);
Aquí queremos mostrar la información (nombre, apellido, etc.) del empleado seleccionado en el menú desplegable. El servicio web no expone ningún método para obtenerla. Sin embargo, podemos utilizar el método GetSalaire pasando el número SS del empleado seleccionado y 0 para las horas y días trabajados. Se realizará un cálculo de salario innecesario, pero el método GetSalaire nos devolverá un objeto de tipo FeuilleSalaire en el que encontraremos 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 GetSalaire del servicio web
- línea 12: define el método que se ejecutará cuando la llamada al método GetSalaire se realice con é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. Estos se definen dentro de una etiqueta <mx:request> en el formato <param1>valor1</param1>. El identificador param1 no puede ser cualquiera. Se deben utilizar los nombres que espera 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 se deben utilizar 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. Recordemos que, al iniciar la aplicación Flex, la tabla con todos los empleados se almacenó en una variable «empleados» de tipo ArrayCollection.
- employes.getItemAt(i): es el empleado n.º i de la tabla
- employes.getItemAt(i).SS: es el número de seguro social de este empleado.
- cmbEmployes.selectedIndex: es el número del elemento seleccionado en el menú desplegable de empleados cmbemployes.
En el ejemplo anterior, ¿cómo sabemos que SS es el número de seguro social de un empleado? Para ello, hay que volver a la respuesta enviada por el método GetAllIdentitesEmployes:
![]() |
- en [1], página del servicio web [Service.asmx]
- en [2], el enlace a la página de prueba del método [GetAllIdentitesEmployes]
- en [3], la prueba se ha realizado. No se espera ningún parámetro.
- En [4]: la respuesta XML contiene una tabla de empleados. Es esta tabla la que se ha almacenado en la variable employes. En [5], vemos que SS es, efectivamente, la etiqueta utilizada para contener el número de seguro social.
Terminemos el 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 se proporcionará mediante una variable heuresTravaillees
- línea 6: el número de días trabajados se proporcionará mediante una variable joursDeTravail
Estas variables deben declararse en la etiqueta <mx:Script> con el atributo [Bindable], lo que permite que sean referenciadas por los componentes MXML (líneas 7 a 10 a continuación).
<mx:Script>
<![CDATA[
...
// datos
[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;
// datos
[Bindable]
private var employes : ArrayCollection;
[Bindable]
private var heuresTravaillees:Number;
[Bindable]
private var joursDeTravail:int;
private function init():void{
// se registran la altura y el ancho de # bloques
employeHeight=employe.height;
employeWidth=employe.width;
// se ocultan ciertos elementos
hideEmploye();
...
}
private function displayInfosEmploye():void{
// formulario
hideEmploye();
// se calcula un salario ficticio
heuresTravaillees=0;
joursDeTravail=0;
pam.GetSalaire.send();
}
private function getSalaireCompleted(event:ResultEvent):void{
...
}
private function getSalaireFault(event:FaultEvent):void{
...
}
// vistas parciales -------------------------------------------------
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 el ancho del contenedor vertical employe, con el fin de poder restaurarlo (líneas 50-53) después de haberlo ocultado (líneas 45-48).
- línea 24: el método displayInfosEmploye es el que se ejecuta cuando el usuario cambia su selección en el menú desplegable de empleados.
- línea 26: el contenedor employe se oculta si estaba visible
- línea 30: el método GetSalaire del servicio web se invoca de manera asíncrona. 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.º SS del empleado seleccionado en el menú desplegable de empleados
- línea 2: el método displayInfosEmploye asigna el valor 0 a la variable heuresTravaillees (línea 28)
- línea 3: el método displayInfosEmploye asigna el valor 0 a la variable joursDeTravail (línea 29)
El método GetSalaireCompleted se ejecuta si el método GetSalaire del servicio web finaliza correctamente:
private function getSalaireCompleted(event:ResultEvent):void{
// se oculta el mensaje de error
hideMsg();
// se recibe una nómina
var feuilleSalaire:Object=event.result;
// visualización
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: se oculta el área de mensajes en caso de que se muestre.
- línea 5: se recupera la hoja de salario devuelta por el método GetSalaire
Para saber qué devuelve exactamente el método GetSalaire, volvemos a la página del servicio web:
![]() |
- en [1], 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], se proporcionan los parámetros
- en [4], el resultado XML obtenido.
Volvamos al método getSalaireCompleted:
private function getSalaireCompleted(event:ResultEvent):void{
// se oculta el mensaje de error
hideMsg();
// se recibe una nómina
var feuilleSalaire:Object=event.result;
// visualización
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: feuilleSalaire=event.result representa el flujo XML [4] devuelto por el método GetSalaire. Según este flujo, se observa que:
- feuilleSalaire.Employe es el flujo XML de un empleado
- feuilleSalaire.Employe.Nom es el nombre de ese empleado
- ...
- líneas 7-12: el flujo XML feuilleSalaire se utiliza para completar los distintos campos del contenedor «empleado».
- línea 13: se muestra el contenedor «empleado».
El método getSalaireFault se ejecuta si el método GetSalaire del servicio web falla:
private function getSalaireFault(event:FaultEvent):void{
// visualización del mensaje de error
msg.text=event.fault.message;
// formulario
showMsg();
}
- línea 3: el mensaje de error event.fault.message se coloca en el campo de mensaje
- línea 5: se muestra el campo de mensaje
Aquí concluyen las modificaciones necesarias para esta nueva versión. Al guardarla, y siempre que la sintaxis sea correcta, se genera la versión ejecutable en la carpeta [bin-debug] del proyecto:
![]() |
En el ejemplo anterior, [main2.html] es la página HTML que incorpora el binario de la aplicación Flex [main2.swf], la cual será ejecutada por Flash Player.
Podemos probar esta nueva versión:
- el servicio web ASP.NET debe estar en ejecución
- se debe iniciar el servidor Apache para el cliente Flex
Si suponemos que el alias [pam-v10-flex-client-webservice] utilizado en la versión anterior aún existe, solicitamos al servidor Apache los URL y [http://localhost/pam-v10-flex-client-webservice/main2.html] en un navegador:
![]() |
![]() |
- en [1], se solicita el URL
- en [2], el menú desplegable de empleados
- en [3], se cambia la selección en el menú desplegable para provocar el evento change
- en [4], el resultado obtenido: el perfil de Justine Laverti.
14.5. La vista n.º 3
La vista n.º 3 realiza la validación del formulario. Aquí, solo se verifica el campo de entrada «txtHeuresTravaillees». Mientras el formulario esté incorrecto, el botón «btnSalaire» permanecerá desactivado.
Para agregar esta funcionalidad, duplicamos [main2.mxml] en [main3.mxml]:
![]() |
A partir de ahora trabajaremos con [main3.mxml], que estableceremos como aplicación predeterminada (véase este concepto en el párrafo 14.4). En primer lugar, agregamos un atributo al componente «txtHeuresTravaillees»:
<mx:TextInput id="txtHeuresTravaillees" change="validateForm(event)"/>
Cada vez que cambia el contenido del campo de entrada «txtHeuresTravaillees», se invoca el método validateForm. Se trata de un método local escrito por el desarrollador. En este método, podríamos verificar que el contenido del campo de entrada «txtHeuresTravaillees» sea efectivamente un número entero positivo. Sin embargo, 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> permite verificar que otro componente contenga un número entero o real.
- línea 1: el atributo id le da un identificador al componente.
- línea 1: source es el identificador del componente que verifica el componente NumberValidator. En este caso, se verifica el campo de entrada «txtHeuresTravaillees».
- línea 1: property es el nombre de la propiedad del componente source que contiene el valor a verificar. En definitiva, se verifica el valor source.property, en este caso txtHeuresTravaillees.text.
- línea 2: precision establece el número máximo de decimales permitidos. precision=0 sirve para verificar que el número ingresado sea un entero.
- línea 2: allowNegative indica si se permiten o no los números negativos
- línea 7: required indica si el campo es obligatorio o no.
Cuando no se cumple una condición de validación, se muestra un mensaje de error en un globo junto al componente con error. Por defecto, estos mensajes están en inglés. Es posible definir estos mensajes por cuenta propia:
- (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 con respecto al atributo precision
- negativeError: el mensaje de error cuando el número es negativo, aunque se tenga el atributo allowNegative="false"
- requiredFieldError: el mensaje de error cuando no se ha ingresado ningún dato, aunque el atributo requiredField="true" esté activado
- invalidFormatCharsError: ¿el mensaje de error cuando el texto contiene caracteres o un formato no válido?
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
{
// Se validan las horas trabajadas
var evt:ValidationResultEvent = heuresTravailleesValidator.validate();
// ¿Validación exitosa?
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 del 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 con éxito, evt.type debe tener como valor ValidationResultEvent.VALID. En este caso, el botón btnSalaire está activado; de lo contrario, está desactivado.
Esto es suficiente para verificar la validez de las horas trabajadas.
![]() |
En el ejemplo anterior, la compilación del proyecto generó los archivos [main3.html] y [main3.swf]. Abrimos URL y [http://localhost/pam-v10-flex-client-webservice/main3.html] en un navegador y verificamos diversos casos de error:
![]() |
![]() |
- un campo con error tiene un borde rojo ([1, 2, 3]), mientras que un campo correcto tiene un borde azul ([4]).
- En [4], se observará que el botón [Salaire] está activo porque el número de horas trabajadas es correcto.
14.6. La vista n.º 4
La vista n.º 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 aplicación predeterminada (véase el párrafo 14.4).
![]() |
Los cambios realizados en [main4.xml] y [1] son los siguientes:
- se agrega un nuevo contenedor vertical a la vista [2] para mostrar los componentes del salario del empleado
- se agrega un componente que permite dar formato a los valores monetarios en [3]
- La visualización de los componentes del salario está a cargo del controlador asociado al evento «clic» del botón «btnSalaire».
La vista cambia de la siguiente manera:
![]() |
El nuevo contenedor sigue el mismo principio que el anterior. Se trata de un contenedor vertical VBox [V1] que contiene cuatro contenedores horizontales HBox [Hi]. Los contenedores horizontales H1 a H3 están formados por contenedores verticales que contienen dos etiquetas, de las cuales la segunda se encuentra a su vez dentro de un contenedor vertical para tener un color de fondo.
Pregunta 1: escribe el contenedor del salario. A partir de ahora se llamará complements.
Pregunta 2: Escribe los métodos que permitan ocultar o mostrar el contenedor complements. Basate en lo que se hizo anteriormente para el contenedor employe.
Asignamos un manejador 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{
// preparación del formulario
affichageSalaire=true;
msg.text="";
// parámetros para el cálculo del salario
heuresTravaillees=Number(txtHeuresTravaillees.text);
joursDeTravail=int(joursTravailles.value);
// se solicita el salario al servicio web
pam.GetSalaire.send();
}
- línea 3: el valor booleano affichageSalaire sirve para indicar si se debe mostrar o no el contenedor complements, que muestra los elementos del salario. El método getSalaireCompleted se ejecuta en dos eventos:
- el cambio de empleado en el menú desplegable de empleados para mostrar su información sin el salario. En ese caso, se establecerá affichageSalaire=false.
- el 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 incrementador joursTravailles se convierte en un número entero.
- línea 9: llamada al método remoto GetSalaire. Cabe recordar que este método espera tres parámetros, entre los cuales se encuentran los parámetros heuresTravaillees y joursDeTravail, inicializados en las líneas 6 y 7. También cabe recordar que, si la llamada asíncrona al método GetSalaire:
- tiene éxito, se llamará al método getSalaireCompleted
- falla, se llamará al método getSalaireFault
Pregunta 3: completar el método actual getSalaireCompleted para que muestre el salario del empleado si se ha presionado el botón btnSalaire.
Actualmente, los elementos del salario se muestran sin el símbolo del euro. Se puede incluir este símbolo en el código o bien utilizar un formador. Esto es lo que se propone ahora. El formador será el siguiente:
<mx:CurrencyFormatter id="eurosFormatter" precision="2"
currencySymbol="€" useNegativeSign="true"
alignSymbol="right"/>
- línea 1: id es el identificador del formador, precision el número de decimales que se deben conservar.
- línea 2: currencySymbol es el símbolo monetario que se debe usar. useNegativeSign indica si se debe usar o no el signo - para los valores negativos.
- línea 3: alignSymbol indica dónde colocar el símbolo monetario con respecto al número.
Este formador se utiliza en el código del script de la siguiente manera:
- eurosFormatter es el identificador del formador que se debe utilizar
- format es el método que se debe llamar para formatear un número. Devuelve una cadena de caracteres.
- feuilleSalaire.Indemnites.BaseHeure es, en este caso, el número que se va a formatear.
- lblSH es el nombre de un componente de tipo Texto.
Pregunta 4: modifica el método getSalaireCompleted para que utilice el formador monetario.





























