Comunidad de diseño web y desarrollo en internet

Data Binding en Flex con la clase ChangeWatcher

Al desarrollar RIAs siempre nos vemos en la necesidad de realizar alguna acción cuando un dato cambia al ser cargado desde el servidor. Flex nos ofrece el metatag [Bindable] para refrescar los cambios que sufren los datos, pero es limitado que sólo nos refresque la información en algunos casos.

Para dejar todo claro veamos un ejemplo de un Binding clásico.

Código :

package com.otakurzo.models
{
import flash.events.TimerEvent;
import flash.utils.Timer;

import mx.collections.ArrayCollection;

[Bindable]
public class UserModel
{
public var acList:ArrayCollection = new ArrayCollection();

//-- loadData(); sumilará el tiempo de respuesta de un servidor en 1.5 segundos
private var _timer:Timer;
public function loadData():void
{
_timer = new Timer(1500,1);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE,onTimerComplete,false,0,true);
_timer.start();
}
private function onTimerComplete(e:TimerEvent):void
{
// Removemos el evento, lo detenemos y destruimos el Timer
_timer.removeEventListener(TimerEvent.TIMER_COMPLETE,onTimerComplete);
_timer.stop();
_timer = null;
// --

acList.source = new Array('Otaku RzO','Eldervaz','Elecash','Zguillez','Xklibur','Fernando','eParada','Freddier');
}
//--

//-- Singleton
private static var _instance:UserModel=null;
public function UserModel(e:Enforcer){
trace('new instance of UserModel created');
}
public static function getInstance():UserModel{
if(_instance==null){
_instance=new UserModel(new Enforcer());
}
return _instance;
}
//--
}
}
class Enforcer{}

Esta clase UserModel es un Singleton que me permitirá consumir los datos desde cualquier parte de mi aplicación y tiene el Metatag [Bindable] para ver cambios que sufran sus propiedades públicas.
En acList cargaremos los datos y con la función loadData() usaremos un Timer para simular el retardo de la carga de los datos que se depositarán en acList. Como es de tipo Singleton para acceder hasta acList pondríamos:

Código :

UserModel.getInstance().acList


La interfaz:

Código :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="199" height="324">
<mx:Script>
<![CDATA[
import com.otakurzo.models.UserModel;

private function loadUsers(e:Event):void
{
btnLoadUsers.enabled = false;
btnLoadUsers.label = "Cargando...";

//Llamamos a la función que cargará los datos
UserModel.getInstance().loadData();
}
]]>
</mx:Script>
<mx:Panel width="173" height="295" layout="absolute" styleName="opaquePanel" horizontalCenter="0" verticalCenter="0" title="Ejemplo 1">
<mx:List height="212" id="lstUsers" width="133" dataProvider="{UserModel.getInstance().acList}" x="10" y="10"></mx:List>
<mx:Button id="btnLoadUsers" label="Cargar Usuarios" click="loadUsers(event);" width="133" x="10" y="228"/>
</mx:Panel>
</mx:Application>

Importamos nuestra anterior clase, preparamos el click del botón para que cargue la data y asociamos el dataProvider del componente List con el acList de nuestra clase así:

Código :

UserModel.getInstance().acList
De esta manera los datos se actualizarán cada vez que se produzca un cambio en ellos.

Es poco código y vemos que trabaja correctamente, pero nos falto algo. El botón para cargar los datos quedo deshabilitado y debió habilitarse cuando terminaron de cargarse los datos.

Para solucionarlo podríamos usar una clase de tipo Observer o usar el Tag <mx:Binding /> o incluso una función que pondríamos así: dataProvider="{ cambio(UserModel.getInstance().acList)}" , y esa función se ejecutaría cada que cambie la data pero se pierde el binding directo hacia el dataProvider o quizás nos de algún error.

Por suerte tenemos una forma más elegante de detectar cuando se produjo un cambio y hasta cuando debemos escuchar o no esos cambios. Para lograrlo usaremos la clase ChangeWatch.

Código :

ChangeWatcher.watch(obj:Object,property:String,handler:Function):ChangeWatcher
Tiene otros parámetros opcionales pero para el uso que le daremos con estos nos basta.

Usaremos la función watch para escuchar los cambios que nos retornará una instancia de ChangeWatcher.
Parámetros:
  • obj: Colocaremos el objecto base donde se encuentra la variable/propiedad a detectar.
  • property: Colocaremos la variable/propiedad a detectar.
  • handler: la función a llamar después de haber sufrido un cambio la variable/propiedad a detectar.

Cuando obtengamos la instancia de ChangeWatcher podemos usar la función unwatch para dejar de escuchar cambios.

Ahora veamos el mismo código del ejemplo anterior aplicando ChangeWatcher:

Código :

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="199" height="324">
<mx:Script>
<![CDATA[
import mx.binding.utils.ChangeWatcher;
import com.otakurzo.models.UserModel;

private var cwUsers:ChangeWatcher;

private function loadUsers(e:Event):void
{
btnLoadUsers.enabled = false;
btnLoadUsers.label = "Cargando...";

//Vaciamos la data para notar los cambios
UserModel.getInstance().acList.source = new Array();

//Empezamos a escuchar cambios en la propiedad source del
//objecto acList que esta dentro de la Clase UserModel
cwUsers = ChangeWatcher.watch(UserModel.getInstance().acList,'source',onUserChange);
//--

//llamamos a la función que cargará los datos
UserModel.getInstance().loadData();
}
private function onUserChange(e:Event):void
{
//Dejamos de escuchar cambios y liberamos la variable
cwUsers.unwatch();
cwUsers = null;
//--

btnLoadUsers.enabled = true;
btnLoadUsers.label = "Cargar Usuarios";
}
]]>
</mx:Script>
<mx:Panel width="173" height="295" layout="absolute" styleName="opaquePanel" horizontalCenter="0" verticalCenter="0" title="Ejemplo 1">
<mx:List height="212" id="lstUsers" width="133" dataProvider="{UserModel.getInstance().acList}" x="10" y="10"></mx:List>
<mx:Button id="btnLoadUsers" label="Cargar Usuarios" click="loadUsers(event);" width="133" x="10" y="228"/>
</mx:Panel>
</mx:Application>

El código esta bien comentado pero resumiendo se uso ChangeWatcher antes de la carga de los datos y se dejo de escuchar al inicio de la función que fue llamada cuando cambio el dato a detectar.

El resultado:



Nota importante:


Al usar ChangeWatch coloquen bien la propiedad que quieren detectar porque se da el caso en que quieren poner directamente:

Código :

ChangeWatcher.watch(UserModel.getInstance(),'acList',onUserChange);

en vez de:

Código :

ChangeWatcher.watch(UserModel.getInstance().acList,'source',onUserChange);
Ya que acList es un objecto y source es su propiedad.

Otro sería el caso en que la clase UserModel tenga una propiedad más como: public var login_status:Boolean;
Aquí sí es correcto usar:

Código :

ChangeWatcher.watch(UserModel.getInstance(),'login_status',onUserChange);

porque login_status es una propiedad del UserModel.

Recuerden que getIntance() la estamos usando porque la clase UserModel es de tipo singleton, pero el ChangeWatcher es aplicable a cualquier tipo de objecto que tenga propiedades.

Archivos del ejemplo: descargar.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate