domingo, 24 de mayo de 2009

Ejemplo Cairngorm - Agregar Contacto


Continuando la explicacion de Cairngorm realizada en la entrada anterior "Empezando con Cairngorm - Desarrollando aplicaciones Flex" en este post vamos a realizar un ejemplo de agregar un usuario (contacto) a una agenda de contactos.

Para empezar debemos descargarnos los códigos del ejemplo en el siguiente vinculo, y cargarlos a nuestro entorno de desarrollo (En mi caso Eclipse). Una vez hecho esto se encontraran la siguiente ruta de carpetas dentro del directorio source: com.adobe.cairngorm.samples.addcontact. Ubiquense en ese directorio y van a encontrar que el ejemplo esta dividido en varias carpetas: Businees, Command, Control, Model, View, VO. Estas divisiones se hace para darle un poco mas de claridad al codigo, yo agregaria una mas llamada "events" donde colocaria los eventos, pero eso es cuestion de gustos. Vamonos a la carpeta "View" y hacemos doble click sobre la vista AddContactPanel.mxml.


<?xml version="1.0" encoding="utf-8"?>
<mx:Panel
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:view="com.adobe.cairngorm.samples.addcontact.view.*"
title="MyContacts"
horizontalAlign="left" height="350">
<mx:Script>
<![CDATA[

import com.adobe.cairngorm.control.CairngormEventDispatcher;
import com.adobe.cairngorm.samples.addcontact.control.AddContactEvent;
import com.adobe.cairngorm.samples.addcontact.model.AddContact;
import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;
import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;
import mx.collections.ArrayCollection;


[Bindable]
public var addcontact : AddContact;

[Bindable]
public var contacts : ArrayCollection;

public function addContact() : void
{
var contactVO : ContactVO = new ContactVO();
contactVO.fullname = fullname.text;
contactVO.emailaddress = emailaddress.text;

var event : AddContactEvent = new AddContactEvent( contactVO );
CairngormEventDispatcher.getInstance().dispatchEvent( event );
}
]]>
</mx:Script>
<mx:HBox height="100%">
<mx:VBox>
<mx:Form id="addcontactForm">
<mx:HBox width="100%" horizontalAlign="left">
<mx:Text text="Add a Contact:"/>
</mx:HBox>
<mx:FormItem label="Name: ">
<mx:TextInput id="fullname"/>
</mx:FormItem>
<mx:FormItem label="Email: ">
<mx:TextInput id="emailaddress" displayAsPassword="true"/>
</mx:FormItem>
<mx:HBox width="100%" horizontalAlign="right">
<mx:Button label="AddContact" enabled="{ !addcontact.isPending }" click="addContact()"/>
</mx:HBox>
</mx:Form>
<mx:Text
text="{ addcontact.statusMessage }"
textAlign="center"/>
</mx:VBox>
<mx:VRule height="100%" strokeColor="#DDDDDD"/>
<mx:VBox paddingTop="15" paddingLeft="15" paddingRight="15" paddingBottom="15">
<mx:Text text="List of Contacts"/>
<mx:List wordWrap="true" dataProvider="{contacts}" width="200" height="220"></mx:List>
<mx:Button label="DeleteContact" textAlign="right"/>
</mx:VBox>
</mx:HBox>
</mx:Panel>



Esta vista contiene el formulario para que el usuario final introduzca los datos del contacto que quiere agregar, lo importante a destacar en este archivo son las siguientes lineas:
public function addContact() : void
{
var contactVO : ContactVO = new ContactVO();
contactVO.fullname = fullname.text;
contactVO.emailaddress = emailaddress.text;

var event : AddContactEvent = new AddContactEvent( contactVO );
CairngormEventDispatcher.getInstance().dispatchEvent( event );
}

La funcion addContact se encarga de crear un objeto ContactVO llenandolo con la información introducida en el fomulario por el usuario. Luego crea y despacha un evento Cairngorm llamado AddContactEvent. Vamos a ubicarnos con el mismo esquema de la entrada "Empezando con Cairngorm - Desarrollando aplicaciones Flex":

En este momento nos encontramos en el paso numero 1 del flujo. Todos los eventos despachados en Cairngorm deben heredar de la clase CairngormEvent, y, al momento de crearlos, debemos especificar en el controlador con que comando va asociado este evento, veamos el codigo de la clase AddContactEvent.cs:

package com.adobe.cairngorm.samples.addcontact.control
{
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

/**
* El evento se dispara cuando el usuario presiona "agregar contacto"
*/
public class AddContactEvent extends CairngormEvent
{
/**
* El contacto que se desea agregar
*/
public var contactVO : ContactVO;

/**
* El contructor, te obliga a especificar el contacto
*/
public function AddContactEvent( contactVO : ContactVO )
{
super( AddContactControl.EVENT_ADD_CONTACT );
this.contactVO = contactVO;
}
}
}

Como pueden ver es un codigo bastante sencillo. Lo unico que debemos tomar en cuenta es heredar de la clase CairngormEvent y de especificar cualquier información que deseamos que este disponible en el comando o delegado o servicio (en este caso nos interesa tener el contacto que se va a agregar).

Para hacer el vinculo entre el comando y el evento ahora debemos ir a la clase AddContactControl para realizar las especificaciones de la siguiente manera:

package com.adobe.cairngorm.samples.addcontact.control
{
import com.adobe.cairngorm.control.FrontController;
import com.adobe.cairngorm.samples.addcontact.commands.AddContactCommand;

public class AddContactControl extends FrontController
{
public function AddContactControl()
{
addCommand( AddContactControl.EVENT_ADD_CONTACT, AddContactCommand );
}

public static const EVENT_ADD_CONTACT : String = "addcontact";
}
}

En la linea addCommand( AddContactControl.EVENT_ADD_CONTACT, AddContactCommand ); realizamos el verdadero vinculo entre nuestro evento y el comando. Ahora debemos proceder a crear un comando para manejar las peticiones al servidor. En este momento nos encontramos en el paso dos del flujo Cairngorm, recuerden que es obligatorio hacer al menos una instancia del controlador en la aplicacion, ademas, este controlador debe heredar de la clase FrontController.

Ahora debemos pasar al siguiente paso (crear un comando). Los comandos implementan las interfaces Command y Responder. Por ello deben tener tres metodos principales: execute, onResult y onFault. El primero de ellos es ejecutado automaticamente por el controlador cuando se dispara alguna evento que este enlazado con este comando, el segundo se ejecuta automaticamente una vez que el delegado ha concluido la ejecucion del servicio satisfactoriamente, el ultimo de estos metodos es igual al segundo, con la salvedad que se ejecuta cuando ocurrio algun error no controlado durante la ejecucion del servicio. Ese comando debe tener una instancia del modelo para poder insertar la informacion obtenida del servicio durante el paso 9 del diagrama de flujo.
A continuacion el codigo del comando:

package com.adobe.cairngorm.samples.addcontact.commands
{
import mx.rpc.events.ResultEvent;
import com.adobe.cairngorm.business.Responder;
import com.adobe.cairngorm.commands.Command;
import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.samples.addcontact.business.AddContactDelegate;
import com.adobe.cairngorm.samples.addcontact.control.AddContactEvent;
import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;
import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

public class AddContactCommand implements Command, Responder
{
private var model : ModelLocator = ModelLocator.getInstance();

public function execute( event : CairngormEvent ) : void
{
model.addcontact.isPending = true;
var delegate : AddContactDelegate = new AddContactDelegate( this );
var addcontactEvent : AddContactEvent = AddContactEvent( event );
delegate.addcontact( addcontactEvent.contactVO );
}

public function onResult( event : * = null ) : void
{
model.addcontact.contactVO = ContactVO( event );
model.addcontact.isPending = false;

//Simplify - Used an array of strings instead of ContactVO's
model.contacts.addItem(ContactVO(event).fullname+" "+ContactVO(event).emailaddress);

}

public function onFault( event : * = null ) : void
{
model.addcontact.statusMessage = "Could not send contact information to the server.";
model.addcontact.isPending = false;
}
}
}

Para el siguiente paso debemos crear nuestra clase delegate (Delegado). Los delegados son los encargados de ejecutar los servicios que se encuentren definidos en el Localizador de servicio (ServiceLocator).

Para crear nuestro delegado debemos crear una clase que tenga los siguientes atributos globales:
private var responder : Responder;
private var service : Object;
El primero de ellos es una instacia del Comando que acabamos de agregar en el paso anterior, nos permitirar llamar a los metodos onResult y onFault dependiendo de la ejecucion del servicio. Debemos crear un metodo para cada servicio asociado con este delegado, en el ejemplo actual no tenemos ningún servicio asociado en el Localizador de Servicios, por lo que vamos a finjir la ejecucion de uno, utilizando un timer de 2 segundos (2000 Milisegundos). A continuacion el codigo de la clase AddContactDelegate.cs:

package com.adobe.cairngorm.samples.addcontact.business
{
import com.adobe.cairngorm.business.Responder;
import com.adobe.cairngorm.business.ServiceLocator;
import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;

import flash.utils.setTimeout;

import mx.rpc.AsyncToken;
import mx.rpc.events.ResultEvent;

public class AddContactDelegate
{
private var responder : Responder;
private var service : Object;

public function AddContactDelegate( responder : Responder )
{
//this.service = ServiceLocator.getInstance().getService( "addcontactService" );
this.responder = responder;
}

public function addcontact( contactVO : ContactVO ): void
{
//var token : AsyncToken = service.addcontact( contactVO );
//token.resultHandler = responder.onResult;
//token.faultHandler = responder.onFault;

//for demo purpose: simulate remote service result
setTimeout( addcontactResult, 1000, contactVO );
}

private function addcontactResult( contactVO : ContactVO ): void
{
if( 1 )
{
responder.onResult( contactVO );
}
else
{
responder.onFault();
}
}
}
}

Por ultimo nos queda el archivo Services.mxml que hereda de la clase ServiceLocator, este, es el Localizador de servicios de la aplicacion, debemos tener todos nuestros objetos remotos, metodos remotos, etc. Definidos en este archivo. En el este ejemplo se encuentra comentada una declaracion de un objeto remoto como ejemplo.

<?xml version="1.0" encoding="utf-8"?>
<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cairngorm="com.adobe.cairngorm.business.*">
<!--commented for demo-->
<!--
<mx:RemoteObject
id="addcontactService"
destination="addcontactService"
showBusyCursor="true"
result="event.token.resultHandler( event );"
fault="event.token.faultHandler( event );">
</mx:RemoteObject>
-->
</cairngorm:ServiceLocator>

Tambien debemos hacer una instancia de este ServiceLocator en el inicio de ejecucion del proyecto. En resumen debemos hacer instancias de los siguientes componentes antes de iniciar la ejecucion de las funcionalidades del proyecto:
  • Controlador: AddContactControl en este ejemplo.
  • ServiceLocator: Services en este ejemplo.
  • ModelLocator: Se llama ModelLocator tambien en este proyecto.
Para que visualizen mejor como hacer las instancias que acabo de mencionar vamos a observar el codigo de la clase Applicacion del ejemplo:

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:view="com.adobe.cairngorm.samples.addcontact.view.*"
title="MyContacts"
horizontalAlign="left" height="350">
<mx:Script>
<![CDATA[

import com.adobe.cairngorm.control.CairngormEventDispatcher;
import com.adobe.cairngorm.samples.addcontact.control.AddContactEvent;
import com.adobe.cairngorm.samples.addcontact.model.AddContact;
import com.adobe.cairngorm.samples.addcontact.vo.ContactVO;
import com.adobe.cairngorm.samples.addcontact.model.ModelLocator;
import mx.collections.ArrayCollection;


[Bindable]
public var addcontact : AddContact;

[Bindable]
public var contacts : ArrayCollection;

public function addContact() : void
{
var contactVO : ContactVO = new ContactVO();
contactVO.fullname = fullname.text;
contactVO.emailaddress = emailaddress.text;

var event : AddContactEvent = new AddContactEvent( contactVO );
CairngormEventDispatcher.getInstance().dispatchEvent( event );
}
]]>
</mx:Script>
<mx:HBox height="100%">
<mx:VBox>
<mx:Form id="addcontactForm">
<mx:HBox width="100%" horizontalAlign="left">
<mx:Text text="Add a Contact:"/>
</mx:HBox>
<mx:FormItem label="Name: ">
<mx:TextInput id="fullname"/>
</mx:FormItem>
<mx:FormItem label="Email: ">
<mx:TextInput id="emailaddress" displayAsPassword="true"/>
</mx:FormItem>
<mx:HBox width="100%" horizontalAlign="right">
<mx:Button label="AddContact" enabled="{ !addcontact.isPending }" click="addContact()"/>
</mx:HBox>
</mx:Form>
<mx:Text
text="{ addcontact.statusMessage }"
textAlign="center"/>
</mx:VBox>
<mx:VRule height="100%" strokeColor="#DDDDDD"/>
<mx:VBox paddingTop="15" paddingLeft="15" paddingRight="15" paddingBottom="15">
<mx:Text text="List of Contacts"/>
<mx:List wordWrap="true" dataProvider="{contacts}" width="200" height="220"></mx:List>
<mx:Button label="DeleteContact" textAlign="right"/>
</mx:VBox>
</mx:HBox>
</mx:Panel>



Fijense que aparte de hacer todas las instancias necesarias, tambien le decimos al datagrid que debe enlazar (Binding) su dataprovider con el objeto "contacts" del ModelLocator (model), esto lo hacemos para que cada vez que actualizamos este modelo de datos, se actualice esta vista (el datagrid se llena con exactamente la misma data que tenga model.contacts).

Una vez llamado el servicio y ejecutado correctamente la respuesta de este, nos llegara automaticamente al comando que definimos (AddContactCommand.cs). La respuesta se vera reflejada en el metodo "onResult" donde debemos utilizar la instancia del modelo que tenemos para agregar los datos obtenidos en el modelo de datos, de esta manera la vista se actualizara automaticamente gracias al binding automatico. Los pasos 6,7,8 y 9 del flujo se ejecutan muy rápidamente como pueden darse cuenta, todo gracias a las configuraciones y métodos de programacion utilizados en la arquitectura. A continuacion les dejo el ejemplo funcionando y un vinculo para descargar el codigo fuente. Cualquier pregunta no duden de escribirla en los comentarios de este post.


8 comentarios:

  1. Alejandro, muy buen tutorial de ejemplo. Felicitaciones

    ResponderEliminar
  2. Muy buen esquema de flujo con la explicación paso a paso.

    Muchas gracias por vuestra dedicación y buen hacer.

    ResponderEliminar
  3. Muy bueno men, te felicito

    ResponderEliminar
  4. Saludos
    http://www.voluntadpopular.com/

    Antonio N

    ResponderEliminar
  5. Alejandro

    Gracias por el aporte, me fue muy util ya que estoy iniciandome en flex.. y hay pocos materiales en español al respecto.

    ResponderEliminar
  6. Que tal buenas tardes
    Un favor enorme me podrian decir como agregar el codigo de este proyecto al entorno de trabajo en eclipse??
    Yo lo hago de la siguiente manera en eclipse pero no me acepta de esa forma,
    import/Flexbuilder/flex project . . . le doy la ruta del zip pero me manda un error que no es un archivo valido.

    Saludos y muchas gracias por compartir. . . . . .

    ResponderEliminar
  7. Pues es muy interesante, pero tanta cantidad de codigo para una simple pantalla, la verdad a mi gusto no se me hace funcional, esa misma pantalla se haria con la mitad del codigo, aun no comprendo bien todo acerca de esto, talvez este yo mal.! seguire leyendo

    ResponderEliminar