sábado, 11 de julio de 2009

Nuevo enfoque de Cairngorm - Universal Mind

La gente de Universal Mind ha desarrollado un nuevo enfoque para el framework de contruccion de aplicaciones flex llamado cairngorm. Ahora existe la posibilidad, por ejemplo, de controlar el flujo de los datos y llamadas a servicios agregando nuestros propios callBacks para recibir los datos.

¿Como se come esto? Pues muy sencillo!
Si antes siempre tenias que recibir la respuesta de un servicio desde el comando para luego insertarla en el modelo y que el modelo actualizara la vista. Ahora no!! Ahora simplemente podriamos decidir donde recibir la respuesta del servicio, incluso en la misma vista! Y no tendriamos que pasar por el modelo ni nada por el estilo.

Aqui les dejor una explicacion de la nueva manera de trabajar del cairngorm de universal mind que ha publicado mi gran amigo Rene Ramirez: http://developyourdream.blogspot.com/2009/06/cairngorm-universal-mind-extension.html

Para los que no conozcan Cairngorm todavia aqui les dejo este link donde intento explicarlo mucho mas a fondo: http://internetdeveloping.blogspot.com/2009/04/empezando-con-cairngorm-desarrollando.html

Tambien he detallado la explicacion de cairngorm durante la realizacion de un ejemplo sencillo:
http://internetdeveloping.blogspot.com/2009/05/ejemplo-cairngorm-agregar-contacto.html

Continuar leyendo...

viernes, 12 de junio de 2009

Drag and Drop muy fácil con un DataGrid Flex


En el siguiente ejemplo vamos a ver como lograr de manera muy fácil la funcionalidad de pasar datos de un DataGrid hacia otro o viceversa con solo realizar el típico "drag and drop" del ratón (presionando el botón izquierdo del ratón sobre el dato que queremos pasar y arrastrándolo hasta el DataGrid que deseamos que reciba la información). Para empezar la explicación vamos a ver en funcionamiento del ejemplo que vamos a realizar:


Existen varias maneras de realizar esta funcionalidad, algunas de ellas requieren mayor dedicación y programación por parte del desarrollador y otras no, así como también las que requieren mayor programación también permiten un mayor control. En este voy a explicar la mas sencilla y fácil de implementar.

Todos los objetos que heredan de la clase ListBase (El DataGrid tambien, por heredar de esta clase) tienen 5 propiedades muy interesantes que permiten contemplar la funcionalidad Drag and Drop de una manera muy sencilla:

  1. dragEnabled: Esta propiedad especifica si el componente ListBase debe permitir que al seleccionar un dato se pueda iniciar el arrastre de la informacion hacia cualquier componente ListBase (incluyendo este mismo componente).
  2. dropEnabled: Esta propiedad especifica si el componente ListBase debe permitir que un dato entrante pueda ser soltado sobre este componente.
  3. dragMoveEnabled: Esta propiedad dice si cuando un objeto es arrastrado a partir de este componente se debe borrar la copia original que inicio el arraste. Es decir, si esta propiedad es falsa y hacemos un drag and drop de algun item de la lista, se mantendrá una copia del item en el lugar donde si inicio el movimiento.
  4. allowMultipleSelection: Con esta propiedad podemos realizar el drag and drop de multiples objetos (items) en un mismo movimiento.
  5. allowDragSelection: Esta propiedad nos indica si es posible realizar una seleccion sobre un item para realizar el drag and drop. Si es falsa no podremos seleccionar ningun item para realizar un drag and drop sobr el.
Entonces si en este ejemplo nosotros queremos realizar un drag and drop de el datagrid superior al datagrid inferior entonces debemos colocarle al datagrid superior las siguientes propiedades dragEnabled, allowDragSelection y dragMoveEnables como true. Y en el caso del datagrid inferior debemos colocarle la propiedad dropEnabled como true.

Si nosotros quisieramos podríamos reemplazar uno de los DataGrid utilizados en este ejemplo por cualquier otro componente que herede de la clase ListBase (El List, TitleList, Tree, etc.).

Bueno eso es todo! Como pueden ver es demasiado simple y fácil de realizar. Cualquier pregunta, si es que las tienen, no duden en realizarlas en los comentarios del post que yo siempre estoy pendiente de responderlas! Para descargar el código fuente del ejemplo presiona click aquí.


Continuar leyendo...

martes, 9 de junio de 2009

Listado de metatags y sus utilidades en flex


En el articulo anterior "Utilizando metadata para describir las clases en Flex" explique un poco la definicion de Metadata y di un ejemplo de su tipico uso para expecificar a flex un poco más información acerca de los datos, clases y propiedades que estamos utilizando en nuestra aplicación. En este caso voy a describirles un ejemplo de uso y explicación para cada tipo de metatag que podemos definir en Flex, sin hablar mucho más vamos a empezar con los metatags:

[ArrayElementType]
Para decir que tipo de datos debe contener el ArrayCollection. Por ejemplo:

[ArrayElementType("String")]
public var newStringProperty:Array;

[Bindable]
Para utilizar una propiedad como “data binding expression”, este tipo de propiedades son utilizadas para que cuando flex detecte un cambio en ellas actualice todos los destinos que tienen esta propiedad asiganada con el típico “propertyChangeEvent”. Por ejemplo:

[Bindable]
public var personas:ArrayCollection


<mx:DataGrid id="dg" dataProvider="{ personas }">
</mx:DataGrid>


[DefaultProperty]
Se refiere a la propiedad por defecto del componente, esta “defaultProperty” es la que se setea en mxml cuando se coloca entre los tags del componente. Por ejemplo:
<mx:DataGrid id="dg" dataProvider="{ personas }">
<mx:columns>
<mx:DataGridColumn dataField="email" headerText="Email"/>
</mx:columns>
</mx:DataGrid>
En este caso la defaultProperty del mx.Control.Datagrid seria un objeto de tipo Columns, que a su vez tiene una defaultProperty de tipo ArrayCollection de DatagridColumn.

[Deprecated]
Se utiliza para que el compilador arroje un warning para notificar al usuario que esa propiedad o clase esta obsoleta.


[Embed]
Le dice al compilador que incluya el recurso dentro de la película al momento de compilarla. Un recurso puede ser una imagen, sonido, video, icono, etc. Por ejemplo:
[Embed(source="com/assets/icono.png")]
private var icon:Class;


[Event]
Se utiliza para definir un evento que una clase arroja. Por ejemplo, el mx.Control.Button tiene como definido el evento MouseEvent.Click como metadato. De esta manera cuando estamos definiendo un botón en mxml podemos colocar un handler al evento click.
<mx:Button click="buttonClickHandler()" />


[Exclude]
Se utiliza para decirle al compilador que no muestre esta propiedad al momento de autocompletar, es como decir que el atributo es privado, por ejemplo:
[Exclude(name="label", kind="property")]


[ExcludeClass]
Le dice al compilador que excluya la clase al momento de autocompletar, es como decir que la clase es privada.


[IconFile]
Es el icono que va a tener la clase (componente). Por ejemplo:

[IconFile("MyButton.png")]
public class MyButton extends Button
{
...
}

[Inspectable]
Se utiliza para decirle al compilador que posibles valores puede tener una propiedad, de que tipo debe ser (String, int, Number, etc.) cual debe ser su valor por defecto, etc. Para más información revisar el siguiente vinculo: http://livedocs.adobe.com/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=metadata_141_11.html


[InstanceType]
Para decir de que tipo debe ser una propiedad, por ejemplo (Cobra sentido en el caso de que la propiedad haya sido definida como una interfaz o una clase muy general):


// Define a deferred property for the top component.
[InstanceType("mx.controls.Label")]
public var topRow:IDeferredInstance;

[NonCommittingChangeEvent]
Este metatag le especifica a flex que no debe lanzar validadiones de data (commits típicos del data binding) en el evento especificado, por ejemplo:


[Event(name="change", type="flash.events.Event")]
class MyText extends UIComponent {
...

[Bindable(event="valueCommit")]
[NonCommittingChangeEvent("change")]
function get text():String {
return getText();
}
function set text(t):void {
setText(t);
// Dispatch events.
}
}

En este caso cuando el texto (propiedad text) cambie no va a ocurrir ninguna validación, pero en el caso de que se presione la tecla Enter, ocurrirá un valueCommit y entonces si se dispararan las validaciones de datos.


[RemoteClass]
Especifica que esta clase va mapeada contra un objeto en el servidor, le dice a flex que debe incluir la información de la case al momento de serializarla. Por ejemplo:


[RemoteClass(alias="com.CctVO")]
public class Carrito {
public var id : String;
[Inspectable(defaultValue="sin pagar",enumeration="sin pagar,pagado")]
public var estado : String;
public var productos : ArrayCollection;

[Transient] public function get total() : int
{
return productos.length;
}
}

[Style]
Para especificar que una propiedad es de tipo “estilo” para un componente, por ejemplo:


[Style(name="verticalAlign", type="String", enumeration="bottom,middle,top", inherit="no")]
[Transient]

Le dice a flex que debe ignorar cierta propiedad a la hora de serializar una clase para ser mapeada contra un objeto del lado del servidor. Por ejemplo:


[RemoteClass(alias="com.CctVO")] public class Carrito { public var id : String; public var estado : String; public var productos : ArrayCollection; [Transient] public function get total() : int { return productos.length; }
}

En este caso no se debe enviar la propiedad “total” al servidor ya que es un atributo calculado en ejecución. Para ver mas información hacer click en el siguiente vinculo: http://livedocs.adobe.com/flex/3/html/help.html?content=metadata_3.html


Esos son todos los metatags que conozco hasta la fecha que se peuden definir a la hora de programar en flex, espero les sean de mucha utilidad asi como para mi lo han sido. Cualquier cosa les dejo un documento en PDF con las definiciones y ejemplo que he dado en este artículo.



Continuar leyendo...

jueves, 4 de junio de 2009

Utilizando metadata para describir las clases en Flex


Si nos vamos a la definicion de wikipedia la metadata no es mas que información "sobre la data", es decir, es describir como es la data. Una metadata esta compuesta de una serie de metatags, donde cada uno de estos ultimos se encarga de describir alguna caracteristica de la clase. En un metadato podemos decile al flex que tipos de datos debe contener un ArrayCollection, si una imagen se debe cargar dentro de la pelicula SWF o cargarla remotamente al momento de ejecutar la pelicula, cuales son los posibles valores que puede tomar una propiedad, etc. Supongamos, en el siguiente ejemplo, que tenemos la clase "Carrito" que viene definida de la siguiente manera:


package
{
import mx.collections.ArrayCollection;

public class Carrito
{
public var id : String;
public var estado : String;
public var productos : ArrayCollection;

public function get total() : int
{
return productos.length;
}
}
}

Esta clase no tiene ningun tipo de "Metatag"y lo unico que conocemos de ella es que tiene tres atributos, sus tipos y sus nombres. Ahora supongamos que queremos utilizar este objeto como un "objeto remoto" para utilizarlo en algun lenguaje del lado del servidor como java, php, c#, etc. Para eso debemos decirle al compilador que este objeto esta representado del lado de java y que debe conservar la informacion de la clase al momento de serializar, para eso utilizamos el metatag [RemoteClass]. Tambien debemos decirle entonces que la propiedad "total" no debe ser enviada al servidor por tratarse de una propiedad calculada (seria como por ejemplo la edad, al pasar la fecha de nacimiento se puede calcular la edad), para excluir propiedades de ser enviadas para el servidor utilizamos el metatag [Transient]. Ahora supongamos que queremos definir que el estado de el carrito solo puede ser o "pagado" o "sin pagar", para eso debemos utilizar el metatag [Inspectable] y definir dentro de el los dos valores que puede tomar ese atributo. Con estas tres definiciones la clase nos quedaría de la siguiente manera:

package
{
import mx.collections.ArrayCollection;

[RemoteClass(alias="com.CctVO")]
public class Carrito
{
public var id : String;

[Inspectable(defaultValue="sin pagar",enumeration="sin pagar,pagado")]
public var estado : String;

public var productos : ArrayCollection;

[Transient]
public function get total() : int
{
return productos.length;
}
}
}

Este es un gran ejemplo de metadatos que podemos utilizar en flex. Si quieres conocer mas sobre los metadatos de flex te invito a entrar e mi articulo "Listado de metatags y sus utilidades en flex" donde explico de manera mucho más detallada todas las posibles descripciones que podemos realizarle a una clase al momento de programarla. Cualquier pregunta no duden en escribirla en los comentarios de este articulo.

Continuar leyendo...

miércoles, 3 de junio de 2009

Extendiendo de el SuperTitleWindow para que todas tus ventanas tengan "Cerrar" y "Aceptar" de forma nativa


Desde que empece mi aprendizaje en el desarrollo de aplicaciones con flex, he tenido la necesidad de crear ventanas emergentes con un formato especifico, todas ellas debian tener un botón de "Aceptar" y "Cancelar" para cerrar la ventana en el momento que el usuario lo creyera conveniente. En este ejemplo vamos a ver como se puede estantadrizar el comportamiento de todas las ventanas emergentes de una aplicacion apartir de la clase SuperTitleWindow.cs que tiene inmerso el siguiente comportamiento:
  1. Metodo "Aceptar" cuando se presiona el botón "Enter" del teclado.
  2. Metodo "Cancelar" cuando se presiona el boton "ESC" del teclado.
  3. Metodo "Limpiar Pantalla" cuando se presionan las teclas "shift" + "Delete".
  4. Botón cerrar en la parte superior derecha de la ventana (al estilo windows).
  5. Obliga a definir el foco inicial del mouse dentro que se utilizara al desplegarse la ventana.
  6. Permite modificar la logica a realizar una vez despachado el evento "Cerrar" o "Aceptar".


Para que se entienda un poco mejor vamos a ver un ejemplo de ventana emergente que herede del SuperTitleWindow.

Si observamos el codigo de esta ventana vamos a notar unas pequeñas particularidades con respecto a las ventanas tradicionales:

Código LoginWindow.mxml

<?xml version="1.0" encoding="utf-8"?>
<SuperTitleWindow xmlns="popupClasses.*" xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="174" title="Inicio de sesión">
<mx:Script>
<![CDATA[
import popupClasses.SuperTitleWindowEvent;
import mx.managers.PopUpManager;

private function aceptarClick() : void
{
//Lanzo el evento SuperTitleWindowEvent.OK_WINDOW para que se llame el metodo okPressed
var ev : SuperTitleWindowEvent = new SuperTitleWindowEvent(SuperTitleWindowEvent.OK_WINDOW);
this.dispatchEvent(ev);
}

private function cancelarClick() : void
{
//Lanzo el evento SuperTitleWindowEvent.CANCEL_WINDOW para que se llame el metodo cancelPressed
var ev : SuperTitleWindowEvent = new SuperTitleWindowEvent(SuperTitleWindowEvent.CANCEL_WINDOW);
this.dispatchEvent(ev);
}

override protected function setWindowFocus():void
{
//Seteo el foco al text input llamado "usuario"
//si no implemento esta funcion entonces se disparará una excepcion
//de esta manera obligo a que siempre se defina el foco inicial de una ventana emergante
usuario.setFocus();
}

override protected function okPressed(event:SuperTitleWindowEvent):void
{
//aqui inserto mi logica de aceptar, por ejemplo una validacion.


PopUpManager.removePopUp(this);
}

override protected function cancelPressed(event:SuperTitleWindowEvent):void
{
super.cancelPressed(event);//Debo remover esto si deseo sobreescribir el comportamiento original al momento de presionar cacelar
}

override protected function clearPressed(event:SuperTitleWindowEvent):void
{
//aqui va la logica para limpiar la ventana, se despacha con las teclas "shift" + "delete"
super.clearPressed(event);
}
]]>
</mx:Script>
<mx:Label text="Ingrese el nombre de usuario y contraseña"/>
<mx:HBox width="100%">
<mx:Label text="Nombre de usuario:"/>
<mx:TextInput id="usuario"/>
</mx:HBox>
<mx:HBox width="100%">
<mx:Label text="Contraseña:"/>
<mx:TextInput/>
</mx:HBox>
<mx:ControlBar horizontalAlign="right">
<mx:Button label="Cancelar" click="cancelarClick()"/>
<mx:Button label="Aceptar" click="aceptarClick()"/>
</mx:ControlBar>
</SuperTitleWindow>

Lo primero que notamos es que la ventanta hereda de la clase SuperTitleWindow, luego notamos que existen 6 funciones principales que las podemos utilizar dependiendo de nuestros requerimientos:
  1. okPressed() : Esta funcion se dispará automaticamente cada vez que la ventana esta a punto de cerrarse, en esta funcion debo agregar la logica a realizar justo antes de cerrar.
  2. cancelPressed() : Ocurre igual que en la funcion okPressed pero en este caso cuando la ventana esta apunto de cerrarse.
  3. clearPressed() : Esta funcion se ejecuta automaticamente cada vez que la ventana debe limpiarse, se debe introducir aqui la logica de limpiado, por ejemplo en este caso la logica de limpiado es:
    usuario.text = "";
    clave.text = "";
  4. setWindowFocus(): esta funcion es oblicatoria de sobreescribir, esto se debe a que debemos colocar el foco del mouse sobre el primer campo del formulario que se encuentra en la ventana, de esta manera la aplicacion fluye sin la necesidad de la intervencion del mouse. Si no se sobreescribe esta funcion, entonces ocurre una excepción.
  5. cancelarClick() : Esta funcion la debemos crear en caso de que queramos colocar un botón de cancelar dentro de la ventana, esta funcion debe despachar al evento SuperTitleWindowEvent de tipo SCANCEL_WINDOW para que sea escuchado por el SuperTitleWindow y volvamos a caer en el metodo cancelPressed().
  6. aceptarClick() : Esta funcion la debemos crear en caso de que queramos colocar un botón de cancelar dentro de la ventana, esta funcion debe despachar al evento SuperTitleWindowEvent de tipo OK_WINDOW para que sea escuchado por el SuperTitleWindow y volvamos a caer en el metodo okPressed().
Por otro lado tenemos 3 propiedades configurables para la confirmacion de las tres acciones tipicas en la ventana Aceptar, Cancelar, Limpiar:
  1. askClear: Defino si debo o no debo confirmar la limpieza de la pantalla, por defecto es verdadera para obligar al prigramador a deshabilitarla en caso que lo crea pertienente.
  2. askOk: Defino si debo o no debo confirmar el cerrado de la pantalla, por defecto es verdadera para obligar al prigramador a deshabilitarla en caso que lo crea pertienente.
  3. askCancel: Defino si debo o no debo confirmar el cerrado de la pantalla, por defecto es verdadera para obligar al programador a deshabilitarla en caso que lo crea pertienente.
Al heredar de esta clase estamos teniendo varias ventajas, la primera de ellas es que estamos estandarizando la implementacion de las ventanas, de esta manera el usuario se acostumbrará a presionar el boton "Enter" para aceptar las ventanas emergentes y "ESC" para cancelarlas. Por otro lado se organiza y estructura el código, se fomenta la reutilizacion del codigo y si se necesita variar algun comportamiento se puede manipular a la SuperTitleWindow.cs en lugar de realizar modificaciones a todas las ventanas por separado.

Para descargar este ejemplo pueden hacer click en el siguiente vinculo, cualquier duda no duden en preguntarlo como comentario de este post, espero que les sea de tanta utilidad como a mi.



Continuar leyendo...

martes, 26 de mayo de 2009

JQuery - Framework de desarrollo para Javascript


Probablemente muchos de nosotros queremos entrar con entusiasmo en el mundo del desarrollo de aplicaciones ajax pero nos hemos encontrado con un gran muro debido a la complejidad e inmadurez del los IDE de desarrollo (Es muy dificil encontrar un software que permita debugear paso a paso) y la falta de un gran framework facil de usar como es el caso de otros lenguajes como ActionScript (Framework Flex), C# (.NET framework), JAVA (Java enterprice edition, etc.), PHP (Zend Framework), etc.

Bueno para todas las personas que coincidan conmigo ¡Les tengo buenas noticas! he conocido y empezado a desarrollar con un framework que me ha encantado, es super fácil de usar y con unas pocas lineas puedes crear cosas muy estilizadas, ajax y simples. Ese framework se llama JQuery ¡y los invito a todos a probarlo! Para muestra de lo que pueden lograr luego de unas pocas horas de desarrollo y aprendizaje:

Cuando se desarrolla bajo el framework de JQuery se tiene una arquitectura orientada a componentes al puro estilo de los componentes Flex o los ASP.NET Web Controls (Componentes ASP.NET). Para los que no conozcan ninguna de estas dos arquitecturas tan populares imaginen un diseño de arquitectura como un formulario web, donde cada uno de los objetos del formulario (TexInput, RadioButton, CheckButton, TextArea, Button, etc) seria un componente programable que puede ser reutilizado N veces en el futuro, podrias crear tus propios y componentes mas avanzados. Una muestra de componentes creados con JQuery y muy sencillos de utilizar son los siguientes:

Para comenzar a desarrollar y aprender sobre este grandioso Framework de Javascript debemos descargar la ultima version de JQuery e incluirla en el heather de nuestro projecto. Para descargar la ultima version pueden ingresar al siguiente directorio y seleccionar la version mas resiente o hacer click directamente en este link. De todas maneras pronto empezare a realizar tutoriales sobre el desarrollo sobre el framework de JQuery y la utilizacion detallada de algunos de los componentes que ya he probado.

Vinculos: Pagina oficial de JQuery.

Continuar leyendo...

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.



Continuar leyendo...

miércoles, 29 de abril de 2009

Empezando con Cairngorm - Desarrollando aplicaciones Flex

Probablemente todos los que hemos estado por un buen rato metidos en el mundo de Flex hemos escuchado algunas ves sobre el framwerok para desarrollar a aplicaciones Cairngorm.

Para los que no lo han escuchado dejenme explicarles un poco cual es su utilidad: Cuando desarrollas utilizando este framework te vez obligado a cumplir con una serie de normas (Patrones de diseño) que brindan una infinidad de ventajas a largo plazo dentro de tu aplicacion. Por ejemplo: El estar dividido en 8 capaz te permite llevar una mejor organizacion del codigo, permite que varias personas trabajen de manera colaborativa al separar eficazmente la capa de datos de la aplicacion (bien sea con objetos remotos, http service, xml, etc) de la capa de presentacion, permite que nuevos integrantes que conozcan el framework se incorporen mucho mas rapidamente al ser un estandar, se reutiliza mucho el codigo. En fin, muchisimas ventajas, dejen que lo empiezen a usar para que vean que al final no van a poder dejar de usarlo.

¿Como esta dividido Cairngorm?

Una aplicacion realizada bajo Cairngorm tiene 8 capaz principales: Vista, Evento, Controlador, Comando, Delegado, Ubicador de servicios, modelo y los servicios de datos:
  1. Vista: Son todos los formularios, la capa de presentacion de la aplicacion, lo que el usuario final ve.
  2. Evento: Recordemos que lex utiliza la programacion orientada a eventos, son el vinculo entre la vista y el comando.
  3. Controlador: Enruta los diferentes eventos posibles de la aplicacion con los diferentes comandos.
  4. Comando: Realiza 3 acciones principales, execute, que ejecuta el metodo del delegado deseado. "result" que se ejecuta cuando se recive la respuesta del servidor correctamente y "fault" que se ejecuta si ocurre alguna exepcion durante la ejecucion del delegado y servicio. En el metodo "result" se debe introducir los datos en el modelo para que las vistas sean actualizadas.
  5. Delegado: Existen muchos metodos para un objeto remoto, el delegado es el encargado de llamarlos, es un vinculo entre el comando y el servicio.
  6. Ubicador de servicios: aqui se realizan todas las definiciones de los objetos, metodos o servicios que se van a utilizar. Se coloca la ruta del servidor donde son expuestos estos servicioes, etc.
  7. Modelo: Contiene todos los datos de la aplicacion, un diagrama de clases, entidades.
  8. Los servicios: Deben ser creados en un servidor, lenguaje java, php, c#, etc. Ese servidor contiene la BD.
Ejemplo de caso de uso con Cairngorm:

Vamos a colocar el siguiente ejemplo basado en la grafica de arriba: El usuario desea guardar un paciente en la base de datos, para que eso suceda debe existir un formulario de introducccion de los datos del paciente (esa seria la "vista"), una vez que el usuario llena los datos del paciente oprime el botón "guardar" (en ese momento el programador debe disparar un "evento" con los datos del paciente), el "controlador" es el encargado de enrutar ese evento hacia el "comando" mas adecuado (El comando debe manejar las ejecuciones o fallos de los servicios) luego el comando llama a un "delegado"donde se hace un llamado al objeto remoto segun las definiciones que se hayan realizado en el "manejador de servicios". Luego de recibir la respuesta del servicio (recuerden que la respuesta la debe manejar el "comando"), el "comando" se encarga de agregar dentro del "modelo" la informacion recibida, y como el "modelo" esta sincronizado (binding) con la vista, entonces la vista se actualiza automaticamente sin requerir nungina accion del programador.

Probablemente no les haya quedado nada claro, es un poco dificil de entender al principio, y puede parecer que son demasiadas cosas para una accion tan simple como "insertar un paciente en la base de datos". Para entenderlo muchisimo mejor vamos a explicar el ejemplo pero esta vez con el codigo y su ejecucion. Pero eso lo haremos en otro tutorial por razones didacticas (para entrar a el tutorial "Ejemplo Cairngorm - Agregar Contacto" haz click aqui), no se preocupen que una vez que lo entiendan van a ver que es fácil y útil. Para bajarse la libreria de Cairngorm hagan click aqui y seleccionen la última version.

Continuar leyendo...

lunes, 27 de abril de 2009

Expandir, Contraer, Cerrar, cambiar tamaño y mover un Panel


Una de las funcionalidades mas buscadas en un panel son CERRAR, EXPANDIR, CONTRAER Y RESIZE. En este tutorial vamos a explicar su implementacion en un Panel clásico del framework de flex. Utilizando un una clase llamada SuperPanel hecha por Wietse Veenstra y publicada en su blog.

Para empezar debemos tener en nuestro proyecto la clase SuperPanel.as y cada vez que queramos utilizarla en vez de crear un panel de tipo mx.Containers.Panel debemos crear un panel tipo SuperPanel.

Es importante resaltar que la clase SuperPanel hereda directamente de la clase panel, por lo tanto es un panel en si mismo y Flex lo reconoce como un panel, con todos sus comportamientos naturales, mas lo que vamos a ver a continuacion.
Antes de empezar les coloco un demo de la aplicación y el link para que puedan descargar el codigo fuente que vamos a estar utilizando en este tutorial.


Lo pimero que debemos hacer es ir al metodo createChildren(), por ser este, el primer método que se invoca en el ciclo de vida del SuperPanel.

override protected function createChildren():void {
super.createChildren();
this.pTitleBar = super.titleBar;
this.setStyle("headerColors", [0xC3D1D9, 0xD2DCE2]);
this.setStyle("borderColor", 0xD2DCE2);
this.doubleClickEnabled = true;

if (enableResize) {
this.resizeHandler.width = 12;
this.resizeHandler.height = 12;
this.resizeHandler.styleName = "resizeHndlr";
this.rawChildren.addChild(resizeHandler);
this.initPos();
}

if (showControls) {
this.normalMaxButton.width = 10;
this.normalMaxButton.height = 10;
this.normalMaxButton.styleName = "increaseBtn";
this.closeButton.width = 10;
this.closeButton.height = 10;
this.closeButton.styleName = "closeBtn";
this.pTitleBar.addChild(this.normalMaxButton);
this.pTitleBar.addChild(this.closeButton);
}

this.positionChildren();
this.addListeners();
}

Lo primero que hacemos en este metodo es definir la apariencia del panel le colocamos un color al header y un color al borde, luego habilitamos que se pueda hacer dobleclick (hay que recordar que el evento doble click solo se dispara si la propiedad doubleClickEnabled es igual a true, por eso la estamos haciendo true) al hacer doble click sobre el titulo del panel, este ultimo se contraerá.

Luego pueden notar los dos condicionales, uno para saber si esta habilido el comportamiento resize y otro para verificar si esta habilitado el comportamiento de cerrar, espandir y contraer. En el caso de que enable resize este "verdadero" entonces se colocara el botón en la esquina inferior derecha del componente (en el metodo initPos se verifica cuales son las cordenadas de esa esquina). Si la propiedad showControls es verdadera entonces se colocan los botones cerrar y expandir sobre dentro de la barra de titulo del panel.

public function positionChildren():void {
if (showControls) {
this.normalMaxButton.buttonMode = true;
this.normalMaxButton.useHandCursor = true;
this.normalMaxButton.x = this.unscaledWidth - this.normalMaxButton.width - 24;
this.normalMaxButton.y = 8;
this.closeButton.buttonMode = true;
this.closeButton.useHandCursor = true;
this.closeButton.x = this.unscaledWidth - this.closeButton.width - 8;
this.closeButton.y = 8;
}

if (enableResize) {
this.resizeHandler.y = this.unscaledHeight - resizeHandler.height - 1;
this.resizeHandler.x = this.unscaledWidth - resizeHandler.width - 1;
}
}

public function addListeners():void {
this.addEventListener(MouseEvent.CLICK, panelClickHandler);
this.pTitleBar.addEventListener(MouseEvent.MOUSE_DOWN, titleBarDownHandler);
this.pTitleBar.addEventListener(MouseEvent.DOUBLE_CLICK, titleBarDoubleClickHandler);

if (showControls) {
this.closeButton.addEventListener(MouseEvent.CLICK, closeClickHandler);
this.normalMaxButton.addEventListener(MouseEvent.CLICK, normalMaxClickHandler);
}

if (enableResize) {
this.resizeHandler.addEventListener(MouseEvent.MOUSE_OVER, resizeOverHandler);
this.resizeHandler.addEventListener(MouseEvent.MOUSE_OUT, resizeOutHandler);
this.resizeHandler.addEventListener(MouseEvent.MOUSE_DOWN, resizeDownHandler);
}
}

El metodo "positionChildren" se encarga de ubicar a los botones de cerrar, expandir, contraer y cambiar tamaño dentro del panel.

El el segundo metodo addListeners() se encarga de iniciar los "Listeners" o "Escuchadores" que monitorearán cuando algun click, dobleclick o drag ocurra sobre los botones que recien acabamos de agregar en el metodo createChildren().

public function panelClickHandler(event:MouseEvent):void {
this.pTitleBar.removeEventListener(MouseEvent.MOUSE_MOVE, titleBarMoveHandler);
this.parent.setChildIndex(this, this.parent.numChildren - 1);
this.panelFocusCheckHandler();
}

public function titleBarDownHandler(event:MouseEvent):void {
this.pTitleBar.addEventListener(MouseEvent.MOUSE_MOVE, titleBarMoveHandler);
}

public function titleBarMoveHandler(event:MouseEvent):void {
if (this.width < screen.width) {
Application.application.parent.addEventListener(MouseEvent.MOUSE_UP, titleBarDragDropHandler);
this.pTitleBar.addEventListener(DragEvent.DRAG_DROP,titleBarDragDropHandler);
this.parent.setChildIndex(this, this.parent.numChildren - 1);
this.panelFocusCheckHandler();
this.alpha = 0.5;
this.startDrag(false, new Rectangle(0, 0, screen.width - this.width, screen.height - this.height));
}
}

public function titleBarDragDropHandler(event:MouseEvent):void {
this.pTitleBar.removeEventListener(MouseEvent.MOUSE_MOVE, titleBarMoveHandler);
this.alpha = 1.0;
this.stopDrag();
}

public function panelFocusCheckHandler():void {
for (var i:int = 0; i < this.parent.numChildren; i++) {
var child:UIComponent = UIComponent(this.parent.getChildAt(i));
if (this.parent.getChildIndex(child) < this.parent.numChildren - 1) {
child.setStyle("headerColors", [0xC3D1D9, 0xD2DCE2]);
child.setStyle("borderColor", 0xD2DCE2);
} else if (this.parent.getChildIndex(child) == this.parent.numChildren - 1) {
child.setStyle("headerColors", [0xC3D1D9, 0x5A788A]);
child.setStyle("borderColor", 0x5A788A);
}
}
}

Luego de iniciar todos los listeners y variables del panel tenemos los handlers (métodos) que se ejecutarán cuando ocurran los eventos que empezamos a escuchar. El primero de estos "panelClickHandler" se encarga de darle el foco al panel que el usuario le acaba de dar "click", lo trae al frente de los otros panels.

El segundo método "titleBarDownHandler" se ejecuta cuando el usuario hace un MOUSE_DOWN sobre el titlebar y se encarga de empezar a escuchar el MOUSE_MOVE para mover el panel junto con el mouse mientras se mantenga presionado el boton de este ultimo (una implementacion de drag and drop).

El tercer metodo "TitleBarMoveHandler" es el encargado de iniciar el drag para mover el panel, se ejecuta cuando el listener recien agregado en el "titleBarDownHandler" detecta un movimiento del mouse, este método tambien escucha un MOUSE_UP para terminar con el drag and drop y si usuario deja de presionar el botón del mouse.

El cuarto metódo "titleBarDragDropHandler" se encarga de terminer el drag and drop del panel, y se ejecuta cuando ocurre un MOUSE_UP. Por último el metodo "panelFocusCheckHandler" le camiba la apariencia al panel segun si tiene o no tiene el foco del mouse.

public function titleBarDoubleClickHandler(event:MouseEvent):void {
this.pTitleBar.removeEventListener(MouseEvent.MOUSE_MOVE, titleBarMoveHandler);
Application.application.parent.removeEventListener(MouseEvent.MOUSE_UP, resizeUpHandler);

this.upMotion.target = this;
this.upMotion.duration = 300;
this.upMotion.heightFrom = oH;
this.upMotion.heightTo = 28;
this.upMotion.end();

this.downMotion.target = this;
this.downMotion.duration = 300;
this.downMotion.heightFrom = 28;
this.downMotion.heightTo = oH;
this.downMotion.end();

if (this.width < height ="=" visible =" false;" visible =" true;">

En el código de arriba encontramos la implementación del efecto de minimizar cuando hacemos dobleclick sobre el título del panel. El método titleBarDoubleClickHandler se encarga de definir los valores iniciales para los efectos Resize que se definieron al inicio de la clase, estos efectos cambian la altura del panel hasta contraerlo completamente, quedando solo visible la barra del titulo, o visceversa.

public function normalMaxClickHandler(event:MouseEvent):void {
if (this.normalMaxButton.styleName == "increaseBtn") {
if (this.height > 28) {
this.initPos();
this.x = 0;
this.y = 0;
this.width = screen.width;
this.height = screen.height;
this.normalMaxButton.styleName = "decreaseBtn";
this.positionChildren();
}
} else {
this.x = this.oX;
this.y = this.oY;
this.width = this.oW;
this.height = this.oH;
this.normalMaxButton.styleName = "increaseBtn";
this.positionChildren();
}
}

public function closeClickHandler(event:MouseEvent):void {
this.removeEventListener(MouseEvent.CLICK, panelClickHandler);
this.parent.removeChild(this);
}

Estos dos metodos son los encarcados de las funciones de expandir, contraer y cerrar el panel, el primero de ellos se ejecuta cuando se hace click sobre el botón "normalMaxButton" y lo primero que hace es detectar si el panel se encuentra expandido o contraido y luego ejecuta el codigo para expandir o contraer el codigo segun el caso. El segundo metodo "closeClickHandler" se llama cuando el usuario hace click sobre el botón cerrar, este metodo elimina el listener sobre el evento click que habiamos definido durante la funcion create children y hace que el panel se auto elimine.

public function resizeOverHandler(event:MouseEvent):void {
this.resizeCur = CursorManager.setCursor(resizeCursor);
}

public function resizeOutHandler(event:MouseEvent):void {
CursorManager.removeCursor(CursorManager.currentCursorID);
}

public function resizeDownHandler(event:MouseEvent):void {
Application.application.parent.addEventListener(MouseEvent.MOUSE_MOVE, resizeMoveHandler);
Application.application.parent.addEventListener(MouseEvent.MOUSE_UP, resizeUpHandler);
this.resizeHandler.addEventListener(MouseEvent.MOUSE_OVER, resizeOverHandler);
this.panelClickHandler(event);
this.resizeCur = CursorManager.setCursor(resizeCursor);
this.oPoint.x = mouseX;
this.oPoint.y = mouseY;
this.oPoint = this.localToGlobal(oPoint);
}

public function resizeMoveHandler(event:MouseEvent):void {
this.stopDragging();

var xPlus:Number = Application.application.parent.mouseX - this.oPoint.x;
var yPlus:Number = Application.application.parent.mouseY - this.oPoint.y;

if (this.oW + xPlus > 140) {
this.width = this.oW + xPlus;
}

if (this.oH + yPlus > 80) {
this.height = this.oH + yPlus;
}
this.positionChildren();
}

public function resizeUpHandler(event:MouseEvent):void {
Application.application.parent.removeEventListener(MouseEvent.MOUSE_MOVE, resizeMoveHandler);
Application.application.parent.removeEventListener(MouseEvent.MOUSE_UP, resizeUpHandler);
CursorManager.removeCursor(CursorManager.currentCursorID);
this.resizeHandler.addEventListener(MouseEvent.MOUSE_OVER, resizeOverHandler);
this.initPos();
}

Estos últimos cuatro metodos se dedican a implementar la funcionalidad de resize para el panel: El primero de ellos se encarga de cambiar el icono del mouse cuando tengamos el cursor sobre la esquina inferior derecha (para indicarnos que podemos hacer un resize del panel). El segundo metodo es muy similiar pero lo que hace es devolver el icono del mouse a su estado natural (la flechita) cuando quitemos el curso de la esquina inferior derecha del panel.

El tercer metodo "resizeDownHandler" se ejecuta cuando presionamos el botón izquierdo del mouse sobre el boton "resizeHandler" que habiamos insertado en el createChildren(), este metodo se encarga de inicializar los valores X y Y de la esquina y de iniciar los listeners para el evento MOUSE_MOVE.

El metodo "resizeMoveHandler" se encarga de cambiar el tamaño del panel en funcion del movimiento del mouse. Solo se ejecuta mientras tengamos el boton derecho del mouse preisonado, ya que una de las primeras cosas que hace es agregar un listener para el evento MOUSE_UP para detectar cuando dejamos de apretar el botón.

El método "resizeUpHandler" el ultimo metodo de todos se encarga de eliminar el listener de MOUSE_MOVE para no continuar con el resize (cambio de tamaño) del panel.

¡Y eso es todo! Como pueden apreciar es una clase bastante sencilla y facil de usar, por eso me gusta tanto, aunque tiene algunos bugs que iran descubriendo y puliendo sobre la marcha. En los próximos tutoriales publicare modificaciones bastante interesantes que he realizado sobre este u otros panels. Espero que haya sido lo suficientemente claro, cualquier pregunta no duden en preguntarme.


Continuar leyendo...

sábado, 25 de abril de 2009

Tutorial HTTP Service con Flex y PHP

Existen dos grandes maneras de comunicarse remotamente a través de aplicaciones web, son dos enfoques que tienen diferencias muy marcadas, y que si aprendemos a utilizarlos bien, podemos explotar los mejores aspectos de cada método y lograr aplicaciones con una capa de datos robusta y veloz.

Uno de los enfoques (SOAP) es a través de exposiciones de metodos remotos para que puedan ser llamadas desde otras computadoras, si quieres aprender un poco mas sobre eso puedes entrar a mi tutorial Video tutorial WebService basico con php donde lo explico as detalladamente.

El otro enfoque, que utilizaremos en este tutorial, tiene que ver con los metodos mas primitivos que existen en el protocolo http, que seguramente los que han trabajado con formularios web han escuchado algunos de estos: POST, GET, PUT, DELETE, etc. Un ejemplo de arquitectura web que utiliza este enfoque es llamado REST.

Vamos a emprezar, la aplicación que vamos a desarrollar va a tener dos lados, cliente y servidor. Del lado del cliente se creará una aplicacion flex llamada myHTTPService.mxml que se utilizará para visualizar todos los usuarios insertados en una base de datos y existirá un formulario para la insercion de un nuevo usuario. Del lado del servidor se creará una archivo PHP llamado request.php que permitirá acceder a la base de datos para realizar las consultas e inserciones pedidas por el cliente flex.



El lado del Cliente
Código myHTTPService.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="send_data()" height="503">
<mx:Script>
<![CDATA[
import mx.utils.ObjectProxy;
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;

private var service:HTTPService


[Bindable]
private var usuarios : ArrayCollection = new ArrayCollection();

[Bindable]
public var cards: Array = ["PUT","POST","GET","DELETE"];

[Bindable]
public var selectedItem:Object = "GET";


public function send_data() : void
{
var obj : Object = new Object();
useHttpService(obj);
}

public function procesar() : void
{
if(nombre.text.length>0)
{
var obj : Object = new Object();
obj.nombre = nombre.text;
obj.id = codigo.text;

useHttpService(obj);
}
else
{
Alert.show("El nombre es obligatorio");
}
}

public function traspasar() : void
{
var a : Object = dg.selectedItem;
nombre.text = a.nombre;
codigo.text = a.id;
}

public function useHttpService(parameters:Object):void {
service = new HTTPService();
//service.destination = "http://localhost/tutoriales/httpservice/request.php";
service.url = "http://ejemplos.net78.net/httpservice/request.php";
service.method = "POST";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.useProxy = false;
service.send(parameters);
}


public function httpResult(event:ResultEvent):void {
var result:Object = event.result;
//Do something with the result.
if(result.users.user is ObjectProxy)
usuarios = new ArrayCollection([{nombre:result.users.user.nombre , id:result.users.user.id}]);
else
usuarios = result.users.user;
}


public function httpFault(event:FaultEvent):void {
var faultstring:String = event.fault.faultString;
Alert.show(faultstring);
}
]]>
</mx:Script>
<mx:DataGrid id="dg" x="10" y="10" dataProvider="{usuarios}" click="traspasar()"/>
<mx:Canvas x="10" y="182" width="260" height="98" backgroundColor="#FFFFFF">
<mx:Label x="10" y="10" text="Nombre:"/>
<mx:Label x="10" y="42" text="Id:"/>
<mx:TextInput x="90" y="8" id="nombre"/>
<mx:TextInput x="90" y="40" id="codigo" enabled="false"/>
<mx:Button x="173" y="66" label="Insertar" click="procesar()"/>
</mx:Canvas>


</mx:Application>




Ahora, que debemos destacar de esta aplicacion, la esencia de todo esta en la funcion useHttpService: Esta funcion se encarga de crear una peticion a un servicio http, para eso creamos un objeto de tipo HTTPService que va a simular un comportamiento parecido al que sucede cuando le damos a submit en un formulario web estandar, pero en este caso, en lugar de colocar varios input text, comboboxes, etc. Vamos a armar un objeto (en esta funcion lo llamamos "parameters") donde cada atributo de este objeto se comportara como una variable independiente en el lado de php, es decir, si al objeto le colocamos la propiedad nombre, entonces en php haremos $_POST['nombre'] para recibir el valor de esa propiedad.

Recuerden que en Flex un objeto de la clase Object puede tener cualquier atributo que nosotros queramos, solo debemos asignarle un valor a ese atributo inventado y a partir de ese momento el objeto contiene ese atributo con ese valor que le dimos.


Ahora vamos a analizar esa funcion paso por paso:

public function useHttpService(parameters:Object):void {
service = new HTTPService();
service.url = "http://ejemplos.net78.net/httpservice/request.php";
service.method = "POST";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
}

El objeto service que acabamos de crear debemos pasarle varios parametros:
  1. URL: la direccion de la pagina que va a recibir nuestro formulario.
  2. Method: puede ser POST, GET, PUT, DELETE, ETC.
  3. send(parameters): Recuerden que el objeto parameters contiene todas las variables que queremos pasar al lado de PHP.
  4. Por ultimo escuchamos dos eventos "result" y "fault", el primero de ellos se disparará si el servicio se invoca correctamente (si encontro el URL que pusimos arriba y si en el lado del servidor PHP nos devolvio un resultado coherente) y nos llevará a la funcion httpresult donde procesaremos los resultados, el segundo de los eventos se disparará si la llamada al servicio no puede ser procesada y nos llevará al método httpfault.
El lado del servidor

En el lado del servidor vamos a crear un archivo php llamado reques.php, este archivo deberá procesar correctamente las peticios POST que haga el lado del cliente y deberá devolver la lista de usuarios que han sido insertados dentro de la base de datos:

Código request.php


//connect to the database
$mysql = mysql_connect("localhost", "root", "");
mysql_select_db( "phprestsql" );


//if the username and email address are filled out
if( $_POST["nombre"])
{
//add the user
$Query = "INSERT INTO user VALUES ('".$_POST['nombre']."','')";
$Result = mysql_query( $Query );
}


//return a list of all the users
$Query = "SELECT * from user";
$Result = mysql_query( $Query );


$Return = "<users>";


while ( $User = mysql_fetch_object( $Result ) )
{
$Return .= "<user><id>".$User->id."</id><nombre>".$User->nombre."</nombre></user>";
}
$Return .= "</users>";
mysql_free_result( $Result );
print ($Return);



Como pueden ver el lado de php es muy basico y es similar al que se utilizaria si estubieramos trabajando con formularios comunes y corrientes. Debemos recibir la variable "nombre" que pasamos desde flex y luego realizar una insercion en la base de datos. Luego de realizar la insercion consultamos toda la tabla de usuarios para implimir un xml en la pantalla que sera lo que constituirá el objeto de respuesta de vuelta hacia flex, ese se recibira como parametro del metodo httpresult en caso de que la llamada al servicio se haya ejecutado correctamente.

Espero que hayan entendido todo prefectamente, trate de ser lo mas claro posible, cualquier pregunta no duden en preguntarme a través de los comentarios de este blog. Les dejo un ejemplo funcionando de la aplicacion y los codigos fuentes de ambos lados (cliente y servidor).





Continuar leyendo...