19
Introduccion a Aplicaciones RIA para dispositivos móviles
19-09-11 12:00:00

 

Con la aceptación del estandar HTML5 por parte de los browsers mas importantes y el rápido crecimiento de las ventas de los smartphones y tablets todas las miradas se posaron en desarrollar aplicaciones RIA.

En Baires IT no sólo desarrollamos aplicaciones RIA con Sencha Ext que nos ofrece una excelente base, sino también soluciones específicas para dispositivos móviles. En este artículo mostraremos el empleo de Sencha Touch para este fin.

Introducción

Sencha Touch es un framework JavaScript Web RIA basado en Sencha ExtJS orientado a los dispositivos móviles y las prestaciones de estos. Entre sus características mas destacadas en encuentran el manejo de eventos táctiles, geolocalización, integración de datos (JSON, XML, YQL, etc), rápida construcción, multiplataforma, alta escalabilidad, etc.

Para introducirnos vamos a construir una aplicación que nos geolocalice en un mapa, y podamos consultar datos remotos sobre el estado actual del clima.

Además de Sencha Touch vamos a necesitar un servidor web y PHP, en nuestro caso utilizaremos Apache 2.2.17 y PHP 5.3.5.

Instalación

Para instalar el framework es tan simple como descargarlo desde la página oficial de Sencha, lo descomprimimos y lo colocamos dentro una carpeta de nuestro proyecto web. Para utilizarlo alcanza con refereciarlo en la sección de scripts de nuestro index.html.

Creación de una Aplicación

En nuestro proyecto web generaremos una estructura como la siguiente:

 

Debemos generar un archivo app.js en el que incluiremos el siguiente código para definir la aplicación:

GeoWeather = new Ext.Application({
    name: "GeoWeather",
    launch: function() {
        this.views.viewport = new this.views.Viewport();
    }
});

Para instanciar el Viewport y sus características deberemos crear el archivo Viewport.js:

GeoWeather.views.Viewport = Ext.extend(Ext.TabPanel, {
    fullscreen:true,
    tabBar: {
        dock: 'bottom',
        layout: {
            pack: 'center'
        }
    },
    defaults: {
        layout: {
           pack: 'fit'
        }
    },
    items: [
        GeoWeather.views.TabAbout
    ]
});

y finalmente el contenido de tabAbout.js:

GeoWeather.views.TabAbout = new Ext.Panel({
    title: "About",
    iconCls: "info",
    fullscreen: true,
    styleHtmlContent: true,
    html: "
Desarrollado por el Laboratorio de Investigación Tecnológica (LIT) de Baires IT
", dockedItems:[{ xtype: 'toolbar', dock: 'top', id: 'barraTabAbout', title: 'About...' }] });

Y finalmente referenciamos los nuevos archivos js en el index.html para que los cargue el browser:

<script src="app/views/tabAbout.js" type="text/javascript"></script>
<script src="app/views/Viewport.js" type="text/javascript"></script>
 ...

El resultado visto con Chrome y con un emulador de Android 2.3.1 es el siguiente:

 

Geolocalizando

HTML5 definió en su estandar que los browsers permitan geolocalizar al visitante y así aprovechar esta información con diferentes fines. Por supuesto Sencha adoptó esta característica y la extendió al asociarle métodos y eventos para así enriquecer las Aplicaciones Web RIA que se pueden lograr con este framework.

Agregaremos un subdirectorio ‘util’ al directorio ‘app’ de nuestro proyecto, y crearemos un archivo tools.js.

En este definiremos la función obtenerCoordenadasSencha() y la llamaremos justo antes de que se cree el Viewport:

function obtenerCoodenadasSencha(){
    var geo = new Ext.util.GeoLocation({
        listeners: {
            locationupdate: function (geo) {
                localStorage.setItem('HomeLatitud',geo.latitude);
                localStorage.setItem('HomeLongitud',geo.longitude);
                localStorage.setItem('latitud',geo.latitude);
                localStorage.setItem('longitud',geo.longitude);
                alert('Usted se encuentra en ' +
                        'Latitud:'+geo.latitude+', ' +
                        'Longitud:'+geo.longitude);
            },
            locationerror: function (   geo,
                                        bTimeout,
                                        bPermissionDenied,
                                        bLocationUnavailable,
                                        message) {
                if(bTimeout){
                    alert('Timeout occurred.');
                }
                else{
                    alert('Error occurred.');
                }
            }
        }
    });
}

Ya podemos observar que utilizamos ademas localStorage que es un almacenamiento definido por HTML5.

Hasta aquí el resultado visto con Chrome:

 

Consultar datos a otros Data Web Services

En los comienzos de la informática, las mayoria de las aplicaciones eras procesos batch que tenían una entrada de datos, un proceso y una salida. Hace unos años esto fue cambiando y las aplicaciones se convirtieron en productoras y consumidoras de datos de manera dinámica. Para las Aplicaciones Web RIA es fundamental este dinamismo.

Anteriormente destacamos como una de las características de Sencha Touch era el tener un potente paquete de manejo de Datos. Esta característica nos brinda muchas posibilidades a la hora de seleccionar la fuente, ya sea AJAX, JSON, YQL, etc y utilizarlos en nuestros Templates o Stores.

En esta sección vamos a enfocarnos en algunas de las posibilidades con las que podemos trabajar:

  • Hacer una peticion JSON remota y aplicarla directamente en un XTemplate:
function hacerPeticionJSON(lat, lon) {
    Ext.util.JSONP.request({
        url: 'http://free.worldweatheronline.com/feed/weather.ashx',
        callbackKey: 'callback',
        params: {
            key: 'fccb3b08c6141118112306',
            q: lat +','+ lon,
            format: 'json',
            num_of_days: 5
        },
        callback: function(result) {
            var weather = result.data.weather;
            if (weather) {
                var panel = GeoWeather.views.TabPronostico,
                            weatherTpl = panel.tplPronostico;
                panel.update('');
                panel.update(weatherTpl.applyTemplate(weather));
            } else {
                alert('No hay datos de Pronóstico para esta ubicación\n'
                    + ' lat:' + lat
                    + ' long:'+ lon);
            }
        }
    });
}
  • Definir y rellenar de manera estática un Store en la aplicación
// Definimos y registramos un Modelo
Ext.regModel('CodigoDescripcion', {
    fields: ['codigo', 'descripcion']
});

// Creamos el Store y le rellenamos los datos de manera estática
GeoWeather.views.descripcionStore = new Ext.data.JsonStore({
    model  : 'CodigoDescripcion',
    sorters: 'codigo',
    data: [
        { codigo: '113', descripcion: 'Despejado'},
        { codigo: '116', descripcion: 'Parcialmente Nublado'},
        { codigo: '119', descripcion: 'Nublado'},
        { codigo: '122', descripcion: 'Encapotado'},
        { codigo: '143', descripcion: 'Bruma'},
		...
    ]
});
  • Hacer un requerimiento de datos externos, guardarlos en JsonStore y usarlos cuando se desee
// Definimos y registramos un Modelo
Ext.regModel('CiudadesApi', {
    fields: ['areaName', 'country' ,'region' ,'latitude', 'longitude']
});

// Creamos el Store
GeoWeather.views.cityStore = new Ext.data.JsonStore({
    model  : 'CiudadesApi',
    sorters: 'areaName'
});

// definimos una funcion que hace un requerimiento de datos
// y el callback rellena el Store con los datos obtenidos.
function consultaCiudades(ciudad) {
    var error = false;
    Ext.util.JSONP.request({
        url: 'app/util/CityJson.php',
        callbackKey: 'callback',
        params: {
            key: 'fccb3b08c6141118112306',
            query: ciudad,
            format: 'json',
            num_of_results: 10
        },
        callback: function(result) {
            var error = false;
            if (result){
                ciudades = result.result;
                if (ciudades) {
                    GeoWeather.views.cityStore.add(ciudades);
                    showDesplegableCiudades();
                } else {
                    error=true;
                }
            } else {
                error = true;
            }
            Ext.getBody().unmask();
            if (error){
                alert('No se han encontrado resultados para su consulta');
            }
        }
    });
}

// Definimos una lista que hace uso del Store definido y llenado anteriormente
GeoWeather.views.PanelCiudadesList = new Ext.List({
    store:GeoWeather.views.cityStore,
    fullscreen: true,
    itemTpl : new Ext.XTemplate(
        '',
                '{[this.retornaValor(values.value)]}',
         '',
        '',
            ' - ({[this.retornaValor(values.value)]})',
        '',
        ' - ',
        '',
            '{[this.retornaValor(values.value)]}',
        '',
        {
        compiled: true,
        retornaValor: function(valor){
            return (valor);
        }}),
        onItemDisclosure: function(record){
            var latLon = record.data.latitude+','+record.data.longitude;
            localStorage.setItem('latitud',record.data.latitude);
            localStorage.setItem('longitud',record.data.longitude);
            moverMapa(record.data.latitude,record.data.longitude);
            cerrarDesplegableCiudades();
        }
});
  • También podemos almacenarla dentro de un localStorage complementando y extendiendo HTML5.
// Definimos y registramos un Modelo
Ext.regModel('ModeloVientos', {
    fields: [ 'id', 'siglaIngles', 'siglaEspaniol', 'direccion'],
    proxy: {
        type: 'localstorage',
        id  : 'vientos'
    }
});

// Creamos el Store
GeoWeather.views.vientoStore = new Ext.data.Store({
    model  : 'ModeloVientos',
    sorters: 'siglaIngles'
});

// Agregamos los datos que deseamos almacenar en la localStorage.
// Estos datos al estar guardados en la localStorage pueden ser accedidos de cualquier otro módulo del sitio
// no necesariamente programados en Sencha Touch/ExtJS ya que se guardaron en la cache del navegador.
GeoWeather.views.vientoStore.load();
GeoWeather.views.vientoStore.add({siglaIngles: 'N',     siglaEspaniol:'N',   direccion:'Norte'});
GeoWeather.views.vientoStore.add({siglaIngles: 'NNE',   siglaEspaniol:'NNE', direccion:'Nornoreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'NE',    siglaEspaniol:'NE',  direccion:'Noreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'ENE',   siglaEspaniol:'ENE', direccion:'Estenoreste'});
GeoWeather.views.vientoStore.add({siglaIngles: 'E',     siglaEspaniol:'E',   direccion:'Este'});
GeoWeather.views.vientoStore.add({siglaIngles: 'ESE',   siglaEspaniol:'ESE', direccion:'Estesureste'});
...
GeoWeather.views.vientoStore.sync();

Como vemos, las posibilidades son amplias, lo cual nos brinda una libertad de seleccionar la mas apropiada a nuestro requerimiento.

Agregando otras pestañas

El siguiente paso es agregar las demás pestañas, definirle los templates, rellenarlos, etc.

El mapa es un elemento provisto por Google, para lo cual debemos estudiarnos las API de ellos.

Resultado Final

Una vez pulidos los detalles menores vemos la aplicación en todo su esplendor.

 

Conclusiones:

Si bien podemos decir que con la llegada de HTML5 las Aplicaciones Web RIA se manifestaron mas que interesantes a la hora de una solución rápida y multiplataforma, no podemos dejar de decir que Sencha nos dió el impulso final para comenzar a desarrollar aplicaciones para dispositivos móviles permitiendonos desarrollar aplicaciones web móviles que se ven y se sienten nativas en iPhone, Android, y BlackBerry.

Referencias:


Los comentarios están cerrados.