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...

domingo, 15 de marzo de 2009

Modificando el HAccordion del FlexLib


El los ultimos dias he tenido la nacesidad de utilizar el accordion horizontal que trae la librería flexlib que mencione en el artículo anterior, una vez hecho esto me di cuenta de que por problemas de diseño del framework de flex la cabecera (heather) del acordion no pued ser muy personalizada, nada mas alla de colocarle un texto y un icono deseado, a pesar de que tiene una propiedad llamada headerRenderer para colocarle tu propio renderer. Esto se debe, a que la este renderer debe ser un Button.

Para solucionar esto la gente de Flexlib (Especificamente, Doug McCune) tubo una brillante idea y desarrollaro un Canvas que se comporta como botton para poder colocar cualquier cosa que queramos dentro de la cabecera del Vertical u Horizontal accordion, el CanvasButtonAccordionHeader, pero todavia hay un problema, el headerRenderer es único para todo el HAccordion. ¿Qué pasa si yo desearía que cada cabecera, de cada sección del accordion, fuera diferente? Diferentes colores, fondo, controles, etc. No unicamente un texto o un icono diferente.

Para solucionar eso he decidido colocar un ViewStack con todas las posbles cabeceras del HAccordion y dependiendo de cual quiero visualizar en cada una de las secciones le paso en la propiedad "label" del boton (recordemos que supuestamente la cabecera es un botón) el numero de View que deseo visualizar en ese caso (la propiedad selectedIndex del ViewStack). El codigo del CanvasButtonAccordionHeather quedaria de la siguiente manera:





















Si se fijan, he colocado tres HBox dentro del ViewSatck, esto se debe a que mi acordion va a tener 3 secciones diferentes (V0, V1 y V2). Aunque esta no es la solucion mas ideal por lo menos me resulve de mis apuros, lo ideal sería tener un ViewStackButtonAccordionHeather, es decir, un ViewStack que se comporte como boton para que yo pueda presonalizarlo desde la aplicacion principal y no tener que crear un componente con todo el codigo cableado. Pero bueno, es un comienzo.

El ejemplo definitivo de la pagina que estaba realizando lo pueden ver en este link, de todas maneras aqui les dejo el ejemplo para que se lo descarguen.

Continuar leyendo...

viernes, 20 de febrero de 2009

FlexLib Librería - Open Source con componentes para Flex


Todos conocemos la importancia que tienen los componentes a la hora de realizar un diseño web (viene siendo uno de los aportes mas importantes de Flex sobre Flash). La comunidad desarrolladora de Flex se unó para realizar una librería llamada FlexLib con los mejores componentes de los mejores programadores de flex conocidos. Entre algunos de los componentes (solo con los que yo he trabajado) que se incluyeron se encuentran:

  1. Horizontal Accordion: Es similar al Vertical Accordion del framework original de flex pero permite deslizar los contenedores de izquierda a derecha en lugar de arriba hacia abajo.
  2. DragScrollingCanvas: Algo parecido a lo que sucede en google maps, podemos arrastrar el contenido interno del canvas sin la necesidad de usar un ScrollBar o la ruedad del mouse, solo haciendo un drag sobre el contenido.
  3. ButtonScrollingCanvas: Coloca botones en los extremos del contenedor, al presionarlos, colocar el mouse sobre ellos, etc. Se realiza un Scroll del contenido.
  4. CanvasButton: Es un contenedor canvas que hereda de la clase Button, por lo que permite ser utilizado como HeatherRenderer de un Horizonal o Vertical Acorrdion.
En Fin, si quieren conocer todos los componentes que tiene la librería los invito a entrar a la siguiente direccion de FlexLib en Google Codes. De todas maneras les dejo un vinculo para descargar la librería.


Continuar leyendo...

lunes, 16 de febrero de 2009

Utilizando un ItemEditor para modificar las filas de un DataGrid


Este post lo voy a realizar como una continuacion del post anterior Utilizando un ItemRender en un DataGrid pero en este caso vamos a ampliar el ejemplo utilizando una propiedad llamada itemEditor.

Un itemEditor es un componente que se utiliza para modificar informacion, a diferencia de un itermRenderer (que se utiliza unicamente para mostrar informacion) el itemEditor es ideal para realizar modificaciones a datos utilizando los componentes mas idoneos para facilitar la modificacion. La mejor manera de que se vea lo que trato de decir es mostrando el ejemplo que vamos a realizar:
Como pueden notar al hacer click sobre alguna de las celdas los datos que se visualizaban ahora se renderizan de otra manera ideal para su manipulacion.

Para lograr esto debemos colocar en cada columna una propiedad llamada itemEditor que contendrá la ruta hacia la clase o componente que queremos utilizar para renderizar los datos en el momento de la modificacion de la celda. En el caso de este ejemplo la estructura del DataGrid queradía de la siguiente manera.

<mx:DataGrid id="dg" x="10" y="10" columnWidth="20" dataProvider="{datos}" rowHeight="25" editable="true" width="357">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" editable="false" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" editorDataField="value" itemEditor="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image" itemEditor="mx.controls.TextInput"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" editable="false" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" editable="true" itemEditor="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>

Si no queremos que una columna en especifica se pueda editar debemos asignarle la propiedad editable=false.

Bueno esto es todo en este ejemplo, como pueden ver es muy facil utilizar estos itemEditors para modificar las filas del DataGrid. Cualquier duda no duden en escribirme al correo aalejo@gmail.com.


Continuar leyendo...

domingo, 15 de febrero de 2009

Utilizando un ItemRender en un DataGrid

En este ejemplo vamos llenar un DataGrid como lo hacemos normalmente, es decir, a través de su propiedad dataProvider. Lo que tendrá de particular será la manera como mostraremos la informacion dentro del DataGrid. Los datos seran mostrados de 5 maneras diferentes (Una manera por cada columna) utilizando la propiedad ItemRenderer de las columnas del DataGrid (DatagridColumns).

Los ItemRenderer que utilizaremos para mostrar la información serán los siguientes: RadioButton, NumericStepper, Image, CheckBox, TextInput. Si quieres saber como utilizar un ItermRender propio y mas complejo te recomiendo el tutorial Datagrid con un ItemRenderer Propio (Personalizado).

Bueno para empezar vamos a crear un nuevo proyecto en flex llamado DataGridSimpleItemrenderer, dentro de esta aplicacion vamos a agregar un nuevo Datagrid que va a contener 5 columnas, a cada una de las columnas le vamos a colocar la propiedad itemRenderer con la ruta a la clase de flex que vamos a utilizar para renderizar, por ejemplo, si se trata de un RadioButton debemos colocar mx.controls.RadioButton. Tambien debemos colocar la propiedad dataField con el nombre del atributo que va a contener la data que va a utilizar ese RadioButton para renderizarse, recuerden que el DataGrid se va a llenar con un arreglo de objetos (cada objeto va a tener un atributo que se va a user para renderizar una columna, ese atributo será el que colocaremos como dataField segun sea el caso). Por ultimo llenamos la pripiedad headerText con el mobre que le queremos dar a esa columna.

Una vez creado el DataGrid y colocados estos atributos en las columnas del DataGrid tendremos el siguiente codigo:

<mx:DataGrid id="dg" x="10" y="10" dataProvider="{datos}" rowHeight="25" width="288">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" itemRenderer="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" itemRenderer="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>

Ahora debemos crear una funcion llamada "init" que llamaremos cuando ocurra el evento CreationComplete de la aplicación, esta función se encargara de llenar el arreglo dataProvider que contendrá el DataGrid, recuerden que los objetos que le vamos a insertar a este dataProvider deben tener atributos iguales a los que mencionamos como dataField en las columnas del DataGrid. Al final tendremos todo el codigo de la aplicación como lo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;

[Bindable] public var datos : ArrayCollection = new ArrayCollection();

public function init() : void
{
var obj : Object = new Object();
obj.checkBoxValue = true;
obj.radioValue = false;
obj.numeriValue = 7;
obj.textValue = "Hola Mundo";
obj.url = "http://aalejo.googlepages.com/idea.jpg";

datos.addItem(obj);
}
]]>
</mx:Script>
<mx:DataGrid id="dg" x="10" y="10" dataProvider="{datos}" rowHeight="25" width="288">
<mx:columns>
<mx:DataGridColumn headerText="RadioButton" dataField="radioValue" itemRenderer="mx.controls.RadioButton"/>
<mx:DataGridColumn headerText="NumericStepper" dataField="numeriValue" itemRenderer="mx.controls.NumericStepper"/>
<mx:DataGridColumn headerText="Image" dataField="url" itemRenderer="mx.controls.Image"/>
<mx:DataGridColumn headerText="CheckBox" dataField="checkBoxValue" itemRenderer="mx.controls.CheckBox"/>
<mx:DataGridColumn headerText="TextInput" dataField="textValue" itemRenderer="mx.controls.TextInput"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>

Bueno esto es todo, de todas maneras aqui les dejo el codigo de la aplicacion para que lo puedan descargar, y tambien pueden ver el ejemplo en funcionamiento. Cualquier duda me pueden escribir a mi email aalejo@gmail.com.



Continuar leyendo...

sábado, 14 de febrero de 2009

Carrito de compras con PHP (Orientado a Objetos)


En este tutorial vamos a crear y utilizar las clases necesarias para manejar un carrito de compras en PHP. Las funciones que esta clase "Carrito" tendrá serán:
  • Agregar un producto.
  • Eliminar un producto.
  • Modificar un producto.
  • Imprimir el carrito.
Por fines didácticos quiero utilizar el patrón de diseño Iterator en esta clase "Carrito", ese patrón de diseño nos permitie recorrer los arreglos, collecciones, etc. Sin exponer sus estructuras, es decir, vamos a estandarizar la manejar de recorrer ese tipo de objetos, siempre que queramos recorrer un arreglo, coleccion, etc. Vamos a hacerlo de la misma manera sin importar que clase es.

Tambien vamos a trabajar el carrito creado como una variable de sesion, para que este presente durante toda la aplicación (mientras el usuario realiza sus compras).

Creando la clase Producto

Esta clase se encargará de guardar la informacion relativa a cada producto en especifibo, cada que que queramos un nuevo producto debemos crear una instancia de esta clase y pasarle sus atributos: codigo (para la identificacion unica), cantidad, precio, nombre, etc.

Archivo Producto.php

class Producto
{
var $codigo;
var $nombre;
var $cantidad;
var $precio;
}

Creando la clase Carrito

La clase Carrito no es mas que una collección de objetos Producto y los metodos para manejar esos productos:

Archivo Carrito.php

/**
* Esta clase permite manejar funciones basicas de un carrito de
* compras, esta desarrollada usando el patron de diseño iterator
* para orfecer mayor encapsulamiento y orden en los datos
* para utilizarla deben tener una clase producto que contenga
* el atributo "codigo" representando la identificacion del producto
*
* Cualquier duda pueden escribir a aalejo@gmail.com
* o entrar en la explicacion de esta clase en
* www.internetdeveloping.blogspot.com
*/
require('Collection.php');
require('Producto.php');

Class Carrito
{
/**
* Viene representando la lista de productos del carrito
* @var Collection
*/
var $productos;

function Carrito()
{
$this->productos = new Collection('Producto');
}

/**
* Esta funcion devuelve un producto a partir del codigo
* @param $codigo
* @return Producto
*/
public function getProducto($codigo)
{
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

if($prod->codigo == $codigo)
{
return $prod;
}

$iterator->next();
}

return null;
}

/**
* Permite agregar un producto al carrito de compras
* @param $producto
* @return null
*/
public function agregarProducto($producto)
{
$prod = $this->getProducto($producto->codigo);
if($prod) $prod->cantidad++;
else
$this->productos->add($producto);
}

/**
* Elimina un producto del carrito, a partir de un codigo
* @param $codigo
* @return null
*/
public function eliminarProducto($codigo)
{
$existe = false;
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();
if($prod->codigo == $codigo)
{
if($prod->cantidad > 1)
$prod->cantidad--;
else
{
$this->productos->remove($iterator->key());
}
}
$iterator->next();
}
}

/**
* Se utiliza solo con fines de desarrollo, imprime la lista
* de productos del carrito, se puede modificar para imprimir
* toda la informacion de cada producto
* @return String
*/
public function trace()
{
$out = "";
$iterator = $this->productos->getIterator();
while($iterator->valid())
{
$prod = $iterator->current();

$out .= "Producto ".$prod->nombre." -> ".$prod->cantidad."
";

$iterator->next();
}

return $out;
}

}

Seguramente se preguntan porque la variables $productos es de tipo Collection, bueno esto es muy simple, para no trabajar con arreglos tediosos decidi utilizar una clase que encontre en internet llamada Collection.php, lo bueno de esta clase es que implementa la interaz IteratorAggregate que nos permitirá utilizar el patron de diseño Iterator que explique anteriormente, por otro lado, sus metodos son mucho mas intuitivos que los de un arreglo. A continuación les dejo el codigo de la clase Collection.php

Archivo Collection.php

class Collection implements Countable, IteratorAggregate, ArrayAccess
{
protected $_valueType;
protected $_collection = array();

/**
* Construct a new typed collection
* @param string valueType collection value type
*/
public function __construct($valueType)
{
$this->_valueType = $valueType;
}

/**
* Add a value into the collection
* @param string $value
* @throws InvalidArgumentException when wrong type
*/
public function add($value)
{
if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[] = $value;
}

/**
* Set index's value
* @param integer $index
* @param mixed $value
* @throws OutOfRangeException
* @throws InvalidArgumentException
*/
public function set($index, $value)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

if(!$this->isValidType($value))
throw new InvalidArgumentException('Trying to add a value of wrong type');

$this->_collection[$index] = $value;
}

/**
* Remove a value from the collection
* @param integer $index index to remove
* @throws OutOfRangeException if index is out of range
*/
public function remove($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

array_splice($this->_collection, $index, 1);
}

/**
* Return value at index
* @param integer $index
* @return mixed
* @throws OutOfRangeException
*/
public function get($index)
{
if($index >= $this->count())
throw new OutOfRangeException('Index out of range');

return $this->_collection[$index];
}

/**
* Determine if index exists
* @param integer $index
* @return boolean
*/
public function exists($index)
{
if($index >= $this->count())
return false;

return true;
}
/**
* Return count of items in collection
* Implements countable
* @return integer
*/
public function count()
{
return count($this->_collection);
}

/**
* Determine if this value can be added to this collection
* @param string $value
* @return boolean
*/
public function isValidType($value)
{
$baseType = gettype($value);
if($this->_valueType == $baseType)
return true;

if($baseType == 'object')
{
$class = get_class($value);

if($this->_valueType == $class)
return true;
}

return false;
}

/**
* Return an iterator
* Implements IteratorAggregate
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->_collection);
}

/**
* Set offset to value
* Implements ArrayAccess
* @see set
* @param integer $offset
* @param mixed $value
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}

/**
* Unset offset
* Implements ArrayAccess
* @see remove
* @param integer $offset
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}

/**
* get an offset's value
* Implements ArrayAccess
* @see get
* @param integer $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}

/**
* Determine if offset exists
* Implements ArrayAccess
* @see exists
* @param integer $offset
* @return boolean
*/
public function offsetExists($offset)
{
return $this->exists($offset);
}
}


Utilizando del carrito de compras


Bueno ya estamos listos, ahora solo falta probar nuestra clase, para eso vamos a crear un arrchivo llamado VistaCarrito.php donde vamos a crear una instancia de la clase Carrito y vamos a agregarle, eliminarle y modiicarle produtos.

Archivo VistaCarrito.php

session_start();
require("Carrito.php");

$carrito = new Carrito();
$_SESSION["carrito"] = $carrito;
//Creo dos productos para jugar con ellos
$prod = new Producto();
$prod->codigo = 1;
$prod->cantidad = 1;
$prod->nombre = "Cama";
$prod->precio = 50;

$prod2 = new Producto();
$prod2->codigo = 2;
$prod2->cantidad = 1;
$prod2->nombre = "Silla";
$prod2->precio = 23;

echo "Agregando dos productos...
";
$_SESSION["carrito"]->agregarProducto($prod);
$_SESSION["carrito"]->agregarProducto($prod2);

echo $_SESSION["carrito"]->trace();

echo "Modificando el producto de codigo 1...
";
$auxProd = $_SESSION["carrito"]->getProducto("1");
$auxProd->cantidad++;

echo $_SESSION["carrito"]->trace();

echo "Eliminando el producto de codigo 2...
";
$_SESSION["carrito"]->eliminarProducto("2");

echo $_SESSION["carrito"]->trace();

echo "Listo...";

Recuerden que antes de empezar a utilizar las variables de session siempre debemos llamar a la funcion session_start(). Creamos la variable carrito como un objeto de sesion para poder utilizarlo durante toda la paigna web.

Bueno espero que todo haya quedado claro, aqui les dejo el codigo fuente de la clase para los que la quieran utilizar, cualquier duda me pueden escribir a mi correro aalejo@gmail.com.





Continuar leyendo...

jueves, 5 de febrero de 2009

Datagrid con un ItemRenderer Propio (Personalizado)


En esta oportunidad vamos a colocar un ItemRenderer personalizado dentro de un DataGrid y que sin embargo los datos se sigan pasando de manera automatica a través del dataSource y utilizando Binding.

El ItemRenderer que vamos a utilizar va a ser un componente flex creado completamente aparte y a nuestro gusto, y le vamos a pasar sus datos a renderizar a través de la propiedad Data que tienen todos los objetos que implementan la interfaz IDataRender.

Para empezar vamos a entender un poco sobre la interfaz IDataRenderer, cualquier objeto que la implemente contiene una propiedad llamada data, por donde recibe toda la información a renderizar (a pintar, graficar, como lo quieran llamar), la única informacion que un objeto de este tipo deberia recibir a través de otras propiedades seria la concerniente a su comportamiento.

En nuestro ejemplo la informacion a renderizar son dos datos:
  • url: Contiene la direccion donde se encuentra alojada la fotografia del producto.
  • nombre: El nombre del objeto.
El ItemRenderer por defecto en el DataGrid es el componente "Label", aunque a veces no nos damos cuenta. Si queremos cambiar el itemrenderer de una columna solo debemos setearle la propiedad "itemrenderer" igual a la dirección donde se encuentra nuestra clase (o componente), esta dirección debe incluir la ruta desde la raiz del proyecto. Una vez hecho esto el itemrenderer recibirá la información a través de la propiedad "data" de la que hablamos anteriormente.

Bueno basta de hablar tanto y vamos a empezar. Creamos el componente de flex que va a renderizar los datos:
Archivo pictureRender.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="112" height="126">
<mx:Script>
<![CDATA[

[Bindable]
public var nombre : String = "Ramon";
[Bindable]
public var url : String = "assets/1.jpg";

public override function set data(value:Object):void
{
nombre = value.data.nombre;
url = value.data.url;

super.data = value;
}
public override function get data():Object
{
return super.data;
}
]]>
</mx:Script>
<mx:Image x="10" y="10" width="89" height="89" source="{url}"/>
<mx:Label x="10" y="104" width="92" text="{nombre}"/>
</mx:Canvas>

El componente lo hemos creado a partir de la clase Canvas, como esta clase implementa la interfaz IDataRenderer, sobreescribi los metodos get y set de la propiedad data para aplicarle una logica especial a estos datos, ya que debemos extraer el campo "nombre" y "url" de este objeto para colocarlo en las respectivas variables del componente, estas variables deben ser declaradas como [Binding] en el momento de su definición, ya que de esta manera los datos que ellas contengan serán transferidos al objeto Image (en el caso de la url) y Label (en el caso del nombre) sin nosotros tener que ocuparnos de eso, es decir, en el momento en que cambie la variable url entonces cambiara el valor de la propiedad "source" del objeto Image. Para aprender un poco mas sobre el metatag [Binding] puede hacer click aqui.

Ahora procedemos a llenar el archivo de la aplicacion:
Archivo DatagridDataRender.mxml


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.core.IDataRenderer;
import mx.collections.ArrayCollection;

[Bindable]
public var renderData : ArrayCollection = new ArrayCollection( [
{url:'assets/1.png', nombre:'Monitor Daslex'},
{url:'assets/2.png', nombre:'Maletin Ladrix'}]);
[Bindable]
public var datosDG : ArrayCollection = new ArrayCollection();
public function init() : void
{
var obj1 : Object = new Object();
obj1.data = renderData.getItemAt(0);
obj1.descripcion = "Producto de buena calidad.";
var obj2 : Object = new Object();
obj2.data = renderData.getItemAt(1);
obj2.descripcion = "Producto de mala calidad.";

datosDG.addItem(obj1);
datosDG.addItem(obj2);
}
]]>
</mx:Script>
<mx:DataGrid x="10" y="10" dataProvider="{datosDG}" rowHeight="130" width="373" rowCount="2">
<mx:columns>
<mx:DataGridColumn headerText="Producto" dataField="data" itemRenderer="pictureRender" />
<mx:DataGridColumn headerText="Descripcion" dataField="descripcion"/>
</mx:columns>
</mx:DataGrid>

</mx:Application>
Este archivo tiene dos partes fundamentales, en la primera seccion definimos dos ArrayCollection llamados "renderData" y "datosDG". El primero de ellos contiene los objetos "data" que se van a utilizar para enviar informacion a los ItemRenderer, el segundo de ellos va a ser sincronizado con la propiedad dataProvider del DataGrid (contendrá una lista donde cada item será equivalente a una fila en el datagrid.)

La función init, que se llama cuando se dispara el evento creationComplete de la aplicación, se encarga de llenar el ArrayCollection datosDG, especificamente crea objetos con dos variables principales "data" y "descripcion", estas variables deben ser colocadas como "dataFields" en las columnas del DataGrid para que el DataGrid haga la carga de datos automaticamente. Si se fijan, la pripiedad data es llenada con la información a renderizar por el ItemRederer.

Bueno ya hemos finalizado nuestra aplicación, cualquier duda me pueden escribir a mi correo aalejo@gmail.com, si todo los hicieron correctamente su aplicacion debería verse de la siguiente manera:



Continuar leyendo...

Obtener una celda o dato de un Datagid


Un breve ejemplo de como obtener los datos de un datagrid por medio de su propiedad dataProvider.

En el ejemplo vamos a usar un ArrayCollection para llenar un Datagrid, luego vamos a obtener la fila deseada del Datagrid segun el "numero de fila". Tambien vamos a ser capaces de detectar cual es la fila que esta seleccionada y obtener su informacion con la propierar selectedIndex de la clase Datagrid.

Para empezar vamos a crear una clase Persona con los datos: Nombre, Apellido y cedula:

Archivo clase Persona.as


package com
{
public class Persona
{
public var nombre : String;
public var apellido : String;
public var cedula : String;


public function Persona()
{
}

}
}


Luego debemos en la aplicacion principal crear una funcion handler llamada "init" para el evento creationComplete, este evento se dispara apenas la aplicacion se acaba de terminar de crear y esta lista para empezar a ejecutarse, creamos un ArrayCollection y agregarle por ejemplo, dos personas cuales quiera:

public function init() : void
{
var persona1 : Persona = new Persona();
persona1.nombre = "Alejandro";
persona1.apellido = "Sanchez";
persona1.cedula = "17405689";

var persona2 : Persona = new Persona();
persona2.nombre = "Ramon";
persona2.apellido = "Ramos";
persona2.cedula = "14758243";

personas.addItem(persona1);
personas.addItem(persona2);
}

Recuerden colocar esta funcion como handler del evento creationComplete de la aplicacion.

Ahora debemos decirle al Datagrid que debe llenarse con la informacion de ese ArrayCollection, para eso utilizamos la propiedad dataProvider del Datagrid, ademas debemos decirle como manejar esa informacion, debemos crear tres columnas (DataGridColumn) dandole valores a las propiedades headerText (el nombre que queremos mostrar en el tope de esa columna) y dataField (el nombre de la propiedad de la clase persona que contiene el dato a mostrar):

<mx:DataGrid id="dg" x="10" y="10" dataProvider="{personas}">
<mx:columns>
<mx:DataGridColumn headerText="Nombre" dataField="nombre"/>
<mx:DataGridColumn headerText="Apellido" dataField="apellido"/>
<mx:DataGridColumn headerText="Cedula" dataField="cedula"/>
</mx:columns>
</mx:DataGrid>

Ahora procedemos a crear un pequeño formulario donde se van a mostrar los datos que vamos a obtener de la fila que se introduzca. Para eso vmos a crea el siguente fomulario:

<mx:TextInput id="nro" x="10" y="195" width="41"/>
<mx:Button x="59" y="195" label="Obtener" click="obtener()"/>
<mx:Label x="10" y="169" text="¿Qué fila deseas obtener?"/>
<mx:Canvas x="10" y="225" width="287" height="96" backgroundColor="#FFFFFF">
<mx:Label x="10" y="10" text="Nombre"/>
<mx:Label x="10" y="36" text="Apellido"/>
<mx:Label x="10" y="62" text="Cédula"/>
<mx:Label id="labelNombre" x="62" y="10" text="No definido"/>
<mx:Label id="labelApellido" x="62" y="36" text="No definido"/>
<mx:Label id="labelCedula" x="62" y="62" text="No definido"/>
</mx:Canvas>
<mx:Button x="140" y="195" label="La fila seleccionada" width="157" click="filaSeleccionada()"/>

Si se fijan hay dos cosas importantes aqui: La llamada a los metodos obtener() y filaSeleccionada() cuando el usuario haga click en cada uno de los botones, y que hemos identificado los controles que vamos a trabajar desde el codigo Action Script asignandoles una identificado String en la propiedad "id". Ahora debemos definir que van a realizar los metodos handlers que hemos colocado para manejar los eventos click.

public function obtener() : void
{
if(personas.length>Number(nro.text) && Number(nro.text)>=0)
{
var auxPersona : Persona = Persona(personas.getItemAt(Number(nro.text)));

labelApellido.text = auxPersona.apellido;
labelCedula.text = auxPersona.cedula;
labelNombre.text = auxPersona.nombre;
}
else
{
Alert.show('El indice excede el numero de filas o es negativo, recuerda que se empieza por el numero 0');
}

}

public function filaSeleccionada() : void
{
if(dg.selectedItem) nro.text = dg.selectedIndex.toString();
else Alert.show("Debes seleccionar un item");
}

El primer metodo (obtener) se encarga de obtener los datos del numero de fila que contenga el textInput llamado "nro". El segundo metodo obtiene el indice de la fila seleccionada (Si hay alguna, por supuesto).

Bueno al final el codigo de nuestro archivo aplicacion llamado ejemploDataGrid.mxml queda de la siguiente manera:
Archivo clase ejemploDatagrid.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.collections.ArrayCollection;
import com.Persona;

[Bindable]
public var personas : ArrayCollection = new ArrayCollection();

public function init() : void
{
var persona1 : Persona = new Persona();
persona1.nombre = "Alejandro";
persona1.apellido = "Sanchez";
persona1.cedula = "17405689";

var persona2 : Persona = new Persona();
persona2.nombre = "Ramon";
persona2.apellido = "Ramos";
persona2.cedula = "14758243";

personas.addItem(persona1);
personas.addItem(persona2);
}

public function obtener() : void
{
if(personas.length>Number(nro.text) && Number(nro.text)>=0)
{
var auxPersona : Persona = Persona(personas.getItemAt(Number(nro.text)));

labelApellido.text = auxPersona.apellido;
labelCedula.text = auxPersona.cedula;
labelNombre.text = auxPersona.nombre;
}
else
{
Alert.show('El indice excede el numero de filas o es negativo, recuerda que se empieza por el numero 0');
}

}

public function filaSeleccionada() : void
{
if(dg.selectedItem) nro.text = dg.selectedIndex.toString();
else Alert.show("Debes seleccionar un item");
}
]]>
</mx:Script>
<mx:DataGrid id="dg" x="10" y="10" dataProvider="{personas}">
<mx:columns>
<mx:DataGridColumn headerText="Nombre" dataField="nombre"/>
<mx:DataGridColumn headerText="Apellido" dataField="apellido"/>
<mx:DataGridColumn headerText="Cedula" dataField="cedula"/>
</mx:columns>
</mx:DataGrid>
<mx:TextInput id="nro" x="10" y="195" width="41"/>
<mx:Button x="59" y="195" label="Obtener" click="obtener()"/>
<mx:Label x="10" y="169" text="¿Qué fila deseas obtener?"/>
<mx:Canvas x="10" y="225" width="287" height="96" backgroundColor="#FFFFFF">
<mx:Label x="10" y="10" text="Nombre"/>
<mx:Label x="10" y="36" text="Apellido"/>
<mx:Label x="10" y="62" text="Cédula"/>
<mx:Label id="labelNombre" x="62" y="10" text="No definido"/>
<mx:Label id="labelApellido" x="62" y="36" text="No definido"/>
<mx:Label id="labelCedula" x="62" y="62" text="No definido"/>
</mx:Canvas>
<mx:Button x="140" y="195" label="La fila seleccionada" width="157" click="filaSeleccionada()"/>
</mx:Application>

El ejemplo en funcionamiento luce asi:




Continuar leyendo...