domingo, 12 de julio de 2009

Como hacer la pagina web perfecta

En esta oportunidad quiero explicar varios conceptios mientras desarrollo una aplicación para la creacion de encuestas o examenes en linea. En cada tutorial separado explicare más detalladamente cada uno de los puntos de esta entrada, ¿Que vamos a aprender mientras creamos esta aplicación? Muy interesante, voy a enseñarles todo lo que yo utilizo a la hora de crear una página web, las mejores prácticas a mi humilde criterio:

A la hora de crear la página vamos a tener las siguientes premisas fundamentales:

- Pensar todo el tiempo en el usuario: Vamos a evitar cualquier actividad extra para el usuario, crear ayudas en todo momento, prevenir en lugar de corregir, darle velocidad a la pagina, interfaz minimalista, etc.

- La simplicidad no tiene nada que ver con el tamaño: Tú puedes tener la página más grande del mundo pero que siga siendo muy simple desde todo punto de vista (arquitectura, organización, usabilidad, etc.)

- Estandarizar los procesos: Hay actividades que son muy repetitivas, hay que tratar de estandarizarlas o incluso tratar de automatizarlas para que cada vez se hagan mas rápido y sin errores.

- Patrones de diseño: Todos los problemas que se nos puedan presentar a la hora de crear una página web ¡ya se le presentaron a otra persona!, busca en internet, utiliza los patrones de diseño, tal vez trabajemos mas al principio pero luego estaremos volando y sin equivocaciones.



1. ¿Cómo diseñar una página con maquetación CSS?

Primero vamos a maquetar la pagina en contenedores

para utilizar estándares de CSS, así vamos a poder diseñar una página pensando en la información y el contenido y luego le daremos un diseño creando hojas de estilos que tendrán clases que se les aplicaran a los contenedores que definimos, algo así:

Fíjense que la pagina web HTML solo contiene contenedores de información que no tienen ningún diseño (la cabecera de la pagina, el contenido y el pie de la pagina), cuando se le aplica la capa de la derecha (la hoja de estilos) los contenedores cambian de color, tipo de letra, color de letra, sangrías para el texto, etc. Cuando nosotros queramos podemos cambiar la hoja de estilos y nuestra aplicación se verá muy diferente pero seguirá teniendo la misma estructura e información.

2. ¿Cómo utilizar plantillas para poder compartir la misma cabecera, menú y pie en todas las paginas sin tener que copiar y pegar el código?

La idea es crear una archivo “header.php” que será la cabecera de las paginas y lo importaremos cada vez que queramos renderizar o mostrar una página en especifico, de esta manera si realizamos un cambio en esta cabecera no tendremos que ir pagina por pagina realizando el cambio en todas las cabeceras sino que cambiaríamos el archivo header.php y el cambio se reflejaría en todas las paginas. Lo mismo ocurriría en el caso del menú (menu.php) y el pie de la pagina (footer.php).

3. ¿Cómo evitar recargar la pagina tan a menudo?

Con las nuevas tecnologías es posible recargar contenedores específicos de las páginas web en vez de recargar toda la pagina, de esta manera aumentamos mucha facilidad de uso y tiempo de espera. Además recargamos menos al servidor ya que realizamos menos peticiones y por ultimo vamos a ver cómo termina siendo incuso ¡Mucho más fácil que antes! Trabajar de esta manera. Nosotros utilizaremos el framework de javascript llamado JQuery para lograr estas funcionalidades.

4. ¿Cómo estructurar nuestra página de una manera sencilla y organizada para que siga siendo simple a pesar de que ser bastante grande?

Luego de buscar y probar la gran mayoría de los frameworks de desarrollo yo decidí crear mi propia arquitectura para mis páginas web, siguiendo algunos estándares y patrones de diseño pero sin volverla tan compleja y abstracta como me parece que ocurre en el caso de frameworks existentes como symfony.

5. ¿Cómo dividir en capas toda la pagina web para lograr la independencia de cada una de ellas?

En la arquitectura que diseñe la pagina web se divide en 6 capas donde cada una de ellas es completamente independiente, es decir, si quieres cambiar de base de datos solo tienes que cambiar la capa de comandos, si quieres cambiar el diseño de la pagina, solo tienes que cambiar la capa de diseño, etc.

En mi arquitectura yo planteo las siguientes capas:

I. Diseño: Renderiza los datos, les da colores, tamaño, etc. Puras hojas de estilo.

II. Vista: La estructura de los datos, contiene la información y no contienen prácticamente ninguna lógica.

III. Cliente: Creación de componentes DatePicker, Slider, etc. Validaciones, Evitar recargo de la pagina, ayuda de usuario, etc.

IV. Lógica: Transacciones (crudManager), Plantillas (templateManager), Permisos de usuario, Validaciones, etc.

V. Comando: Enlace entre el modelo y la lógica para independizar a la página de la estructura de los datos.

VI. Modelo: Puede ser una BD, unos web services, o cualquier otra forma de obtener y guardar datos.

6. ¿Cómo crear una ayuda de usuario sobre la pagina?

El problema con las ayudas es que siempre son una sección aparte de la aplicación y el usuario ni siquiera sabe cómo encontrar la ayuda para lo que está buscando. Otra de las ventajas que te da estructurar tu pagina en contenedores

es que tienes la posibilidad de mostrarlos y ocultarlos fácilmente. De esta manera si nosotros consideramos que la pagina actual que estamos creando debemos colocar un espacio de ayuda entonces podemos crear un contenedor con esa información y en cualquier momento el usuario podrá ocultarlo y continuar como si nada. Por ejemplo:

7. ¿Cómo prevenir en lugar de corregir?

La parte más importante a la hora de crear una web es como se plantea la interacción del usuario con la aplicación, la mayoría de las paginas utilizan los componentes estándares de formulario que tiene HTML para lograr esta interaccion, pero lamentablemente estos componentes son muy limitados y en un gran porcentaje de las veces dificultan el uso de la pagina. Para eso en la arquitectura que estamos planteando hay un espacio dedicado a la creación o importación de nuevos componentes javascript. ¿Porque introducir una fecha en un TextInputo en una seria de ComboBoxs? Lo más natural seria un calendario, ¿no les parece? Existen una serie de componentes como NumericStepper, Slider, DatePicker, etc. Que hacen que la experiencia de usuario se vuelva mucho más simple y práctica y además evitan muchísimas validaciones hacia nosotros porque no dejan espacio para los errores. Y si además colocas tremenda ayuda, ¡es excelente!

8. ¿Cómo hacer una página segura?

Lo primero que debemos tomar en cuenta a la hora de hacer una página segura es que la capa de javascript no puede ser tomada en cuenta para la seguridad en casi nada, ¿Por qué? Porque puede ser desactivada en cualquier momento por los hackers, incluso los navegadores más viejos ni siquiera soportan javascript. ¿Qué quiere decir esto? Pues muy fácil, las validaciones de datos que hacemos en javascript también las debemos hacer en PHP o en cualquier lenguaje que estemos usando del lado del servidor. Pero OJO: Igual debemos seguir haciendo las validaciones del lado del cliente para no recargar al servidor y para seguir con nuestra premisa de PREVENIR en lugar de CORREGIR.

Para hacer nuestra arquitectura segura yo decidí dividir toda la interacción con los datos en transacciones, si se fijan en la capa lógica existe el crudManager y el infoManager. El primero de estos se encarga de eliminar, modificar y insertar los datos. El segundo se encarga solo de consultaros, por lo tanto requieren permisos de usuario diferentes para ser utilizados. Los mismo ocurre con el templateManager que se encarga de armar las vistas que el usuario va a visualizar, si el usuario no tiene permisos para visualizarlas entonces lo lleva a una página de error.

¿Cómo se cargan los permisos? En variables de sesión, en la arquitectura que vamos a utilizar ya existe un modulo de autenticación que solo debemos realizarle pequeños cambios para adaptarlo a nuestra página, al momento de iniciar sesion te otorga permisos de usuario que luego tu controlas para acceder a las vistas y transacciones desde el crudManager, infoManager y templateManager.

9. ¿Cómo realizar los CRUD sin recargar la pagina?

Es demasiado común en una página web querer insertar, modificar, eliminar y consultar valores de una entidad en especifico (Persona, Paciente, Profesor, etc.). Con unos pocos conceptos es posible estandarizar estos procedimientos para hacerlos mas fáciles de programar y además se logra a la misma vez evitar recargar la pagina tan a menudo. Si estamos utilizando la arquitectura COCOAS (De la que he hablado durante toda esta entrada) solo debemos crear dos contenedores, uno para visualizar la información y otro para modificarla, por ejemplo: En el siguiente ejemplo se tiene una lista de anuncios publicada por un condominio, y se quiere modificar un anuncio en particular:

Este sería el código del contenedor para visualizar los datos:


<div id="showPost1" class="post">
<h1 class="title"><a href="#">Racionamiento de agua</h1>
<p>Los dias lunes y martes no entrará agua al edficicio de la calle, por lo tanto se racionará el agua del tanque en el siguiente horario: Lunes: de 8am a 12pm - de 2pm a 8pm. | Martes: de 8am a 12pm - de 2pm a 8pm.</p>
<p style="text-align:right;">
<a id="detallesLink" class="editLink" name="Post1>" href="#">editar</a>
<a class="deleteLink" name="Post" href="crudManager.php?action=delete_anuncio&codigo=1">Eliminar</a>
</p>
</div>

Y este seria el contenedor para modificar los datos:

<div id="editPost1" class="post" style="display:none;">
<form action="crudManager.php" method="post" id="anuncioForm">
<input name="action" type="hidden" value="edit_anuncio" />
<input name="codigo" type="hidden" value="1" />
<h1 class="title">Titulo: <input name="titulo" type="text" value=” Racionamiento de agua” /></h1>
<textarea name="mensaje" cols="" rows="" style="width:500px; height:100px;"> Los dias lunes y martes no entrará agua al edficicio de la calle, por lo tanto se racionará el agua del tanque en el siguiente horario: Lunes: de 8am a 12pm - de 2pm a 8pm. | Martes: de 8am a 12pm - de 2pm a 8pm.</textarea>
<p style="text-align:right;"><input name="" type="submit" value="Guardar" /></p>
</form>
</div>

Si estamos trabajando con COCOAS, debemos decirle cual contenedor es el que se utiliza para mostrar la información y cual contenedor es el que se utiliza para editar la información, esto lo hacemos con el campo ID de cada uno de los contenedores, colocando la palabra “show” o “edit” (segun el caso) seguida de el nombre de la entidad que estamos modificando en este caso “Post” seguida de el código de la entidad especifica que estamos modificando (can caso de que estemos en una lista de items de la misma entidad).

Siempre el contenedor que se creó para modificar la entidad debe tener por defecto en sus estilos la propiedad display:none para que la pagina se desempeñe mucho mejor, esta propiedad le dice al contenedor que por defecto el debe estar oculto.

Luego debemos irnos al vinculo que hemos definido que se le debe hacer click para modificar la entidad (ocultar el contenedor de visualizar y mostrar el contenedor de modificar) y colocarle en su propiedad class el valor “editLink”, y en su propiedad name el nombre de la entidad y código que este vinculo modifica (en este caso “Post1”) de esta manera COCOAS sabrá lo que tiene que hacer.

Por último debemos irnos al vinculo que hemos definido que se le debe hacer click para eliminar la entidad y colocarle en su propiedad class el valor “deleteLink” y de esta manera COCOAS sabrá lo que tiene que hacer.

Al final los dos contenedores se verían de la siguiente manera, por supuesto que no se verían al mismo momento, sino que al presionar “editar” se ocultaría el primer contenedor y se mostraría el segundo contenedor:

Bueno en las siguientes entradas explicare mucho mejor cada uno de los puntos de los que he hablado en esta oportunidad ya que todavía nos queda muuuuuucho de que hablar y buenas prácticas que seguir para conseguir mucha rapidez, de todas maneras para los que saben leer ingles aquí les dejo un pequeño documento donde explica un poco más detallada la arquitectura COCOAS.


Continuar leyendo...

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