Browsing articles in "JavaScript"

Tutorial de Backbone.js – VI. Sincronización y persistencia

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

Y por fin llegamos al último tema del tutorial sobre Backbone.js, el cual dedicaremos a ver los mecanismos de persistencia y sincronización con el servidor que nos ofrece la librería. En el tema 2 donde abordamos los Modelos vimos cómo podíamos crear, modificar y borrar modelos en nuestra aplicación, pero en ningún momento dijimos cómo podríamos enviar esa información al servidor para poder almacenarla o procesarla.

Como veremos en este tema, Backbone define un comportamiento por defecto que nos ayudará a mantener sincronizados los datos entre el cliente y el servidor.

Sincronizando modelos con el servidor

A la hora de sincronizar un modelo, Backbone hace uso internamente de la función Backbone.sync() que por defecto realizará una llamada Ajax al servidor apoyándose en el método $.ajax() de jQuery o Zepto.js. Esa llamada Ajax será una solicitud RESTful en la que Backbone serializará todos los atributos del modelo en una cadena JSON para su envío. Si todo es correcto, el servidor devolverá otra cadena JSON con aquellos atributos que hayan sido modificados desde el servidor, y Backbone procederá a actualizar el modelo en el cliente para estar sincronizado con el estado del servidor.

Para que Backbone pueda realizar este proceso, necesita saber la URL de la colección asociada al modelo, ya que el comportamiento por defecto de la librería es mapear las operaciones CRUD (create, read, update, delete) a las siguientes direcciones REST:

create → POST /collection
read → GET /collection[/id]
update → PUT /collection/id
delete → DELETE /collection/id

Es decir, lo primero que debemos hacer es asociar al modelo la URL base de la colección (la parte ‘/collection’ del listado anterior) sobre la que realizar estas operaciones. Esta URL la espera obtener de la propiedad url del modelo, la cual puede ser una cadena o una función. Si no especificamos nada en dicha propiedad, la implementación por defecto recogerá la URL base de la colección a la que pertenece el modelo.

Usuarios = Backbone.Collection.extend({
    url: '/usuarios/'
});
var usuarios = new Usuarios();
Usuario = Backbone.Model.extend();
var usuario = new Usuario({'nombre': 'Alfonso Marin'});
usuarios.add(usuario);

Tanto la creación del modelo como su modificación utilizando el método set() no llevan implícita la llamada a Backbone.sync(), así que si queremos sincronizar nuestro modelo tras estas operaciones tendremos que utilizar el método save([attributes],[options]) del modelo. Esta función es parecida al método set() ya que actualizará aquellos atributos que se le pasen como primer parámetro, pero además invocará a Backbone.sync() para que realice la sincronización con el servidor. Además podremos utilizar el segundo parámetro de save() para especificar opciones de la llamada $.ajax() interna que se realiza:

usuario.save({}, {  // se genera POST /usuarios  - contenido: {nombre:'Alfonso'}
    success:function(){
        alert("Usuario guardado con exito");
    }
});

En este ejemplo la operación save() genera un POST ya que se trata de una operación de creación (‘create’). Backbone interpreta que es una operación de creación siempre que intentemos sincronizar una instancia de modelo que no contenga ningún valor en su atributo identificador, que por defecto es el atributo id o aquel que hayamos designado como identificador a través del atributo idAttribute cuando definimos el modelo.

Si el servidor devuelve algún objeto JSON éste será utilizado para actualizar el modelo. Esto es típico que suceda precisamente en las operaciones de creación, donde el servidor nos devolverá el identificador del nuevo elemento creado.

usuario.save({}, {  // se genera POST /usuarios  - contenido: {nombre:'Alfonso'}
    success:function(){
        // Suponiendo que el servidor ha devuelto el objeto {"id": 1}
        alert(usuario.id);  // imprime 1
    }
});

En este ejemplo hemos supuesto que el servidor devuelve un objeto JSON con el identificador asignado a la nueva instancia sincronizada, así que Backbone aplica dicho objeto sobre la instancia del modelo.

Una vez que el modelo ya tiene su identificador, cualquier operación posterior sobre la instancia de modelo provocará una operación de actualización (‘update’) sobre el servidor:

usuario.save({apellidos: 'Marin Marin'}); // se genera PUT /usuarios/1 - contenido {id: 1, nombre: 'Alfonso', apellidos: 'Marin Marin'}

Como muestra el ejemplo, la operación de actualización envía todo el contenido del objeto al servidor a través de un PUT a la URL específica del modelo.

Aparte del método save() existen otros dos métodos de modelo que realizan sincronizaciones con el servidor: fetch([options]) y destroy([options]). El método fetch() realiza una operación de lectura (‘read’) y sirve para refrescar los datos del modelo a partir de la copia existente en el servidor:

var usuario = new Usuario({id:1});  // Creamos una instancia inicializando el ID del objeto que queremos recuperar
usuarios.add(usuario);              // Añadimos la instancia a la colección para que Backbone sepa la url base de la colección
usuario.fetch({                     // se genera GET /usuarios/1
    success:function(){
        alert(JSON.stringfy(usuario.attributes)); // imprime {"id":1, "nombre": "Alfonso", "apellidos": "Marin Marin"}
    }
});

Por otra parte sirve para generar una operación de borrado (‘delete’) y eliminar la instancia del objeto del servidor :

var usuario = new Usuario({id:1}); // Creamos una instancia inicializando el ID del objeto que queremos recuperar
usuarios.add(usuario);             // Añadimos la instancia a la colección para que Backbone sepa la url base de la colección
usuario.destroy();                 // se genera DELETE /usuarios/1

Al igual que ocurría con save() ambas funciones permiten definir como parámetro las opciones a utilizar en la llamada Ajax.

Como hemos visto en todos estos ejemplos, siempre hemos introducido la instancia del modelo que queríamos sincronizar dentro de una colección, como mecanismo para que Backbone pudiese recuperar la URL base de la colección y así formar las operaciones REST para cada tipo de operación CRUD. Si por cualquier motivo deseamos trabajar con instancias de modelo sin tener que asociarlas a ninguna colección, podemos hacer uso del atributo urlRoot en la definición del modelo, donde definiremos precisamente la ruta base:

Usuario = Backbone.Model.extend({
    urlRoot: '/usuarios/'
});
var usuario = new Usuario({id: 1});
usuario.fetch(); //  genera GET /usuarios/1

Como vemos, definiendo urlRoot no será necesario asociar a un modelo a ninguna colección para poder sincronizarlo con el servidor, obteniendo exactamente le mismo comportamiento que si lo estuviera.

Por último, si queremos que un modelo en particular no tenga este comportamiento por defecto y queremos asociarlo a una URL final para todas las operaciones del modelo, podremos definir directamente la propiedad url en la definición del modelo:

Usuario = new Backbone.Model.extend({
    url: '/usuario';
});
var usuario = new Usuario({id: 1});
usuario.fetch({                                // genera GET /usuario
    success: function(){
        usuario.save({'nombre': 'Antonio'}, {  // genera PUT /usuario
            success:function(){
                usuario.destroy();             // genera DELETE /usuario
            }
        });
    }
});

De esta forma Backbone siempre realizará las operaciones CRUD sobre la misma URL fija, sin añadir o quitar el id, esté o no esté dentro de una colección, y tenga o no tenga definido algún valor en urlRoot.

Colecciones y sincronización

Como hemos visto en el apartado anterior el concepto de sincronización de modelos está muy ligado al concepto de colección debido a la propia naturaleza de las interfaces REST. En los ejemplos hemos visto que definiendo la URL base de la colección los modelos incorporados a ella serán capaces de realizar las operaciones de sincronización.

Aparte de los métodos de sincronización existentes en los modelos, las colecciones disponen de un método capaz de recuperar una colección entera desde el servidor: el método fetch([options]):

Usuarios = Backbone.Collection.extend({
    url: '/usuarios'
});
var usuarios = new Usuarios();
usuarios.fetch({    // genera GET /usuarios
    success: function(){
        alert('Recuperados ' + usuarios.length + ' usuarios');
    }
});

Este método realiza una operación GET sobre la URL base de la colección, y espera recibir un objeto JSON consistente en un array de instancias de modelos, que se incluirán en la colección. Esta operación generará el evento Backbone refresh.

Personalizando la sincronización

Como se comentó al principio, cada operación de sincronización realmente la ejecuta la función Backbone.sync(), cuya implementación por defecto sincroniza los datos con el servidor a través de llamadas Ajax. Si lo deseamos, podemos remplazar esta función y cambiar a otra estrategia de sincronización, como por ejemplo utilizar alguna de las funcionalidades HTML5 como WebSockets o Local Storage. Por ejemplo, podemos sobrescribir esta función para que simplemente muestre por consola las operaciones que se van realizando:

Backbone.sync = function(method, model, options) {
    console.log(method, model, options);
    options.success(model);
};

Como vemos en el ejemplo, la función Backbone.sync() espera 3 parámetros:

  • method: el método CRUD que se va a realizar (‘create’, ‘read’, ‘update’, or ‘delete’)
  • model: El modelo a salvar (o la colección a ser leída)
  • options: Las opciones de la solicitud, donde se incluirán funciones callback de success y failure

Lo único que espera Backbone es que se invoquen las funciones callback options.success() u options.error() en los casos oportunos.

Sobrescribir la función Backbone.sync() nos permitirá cambiar la estrategia de sincronización de forma global, pero también es posible sobrescribir esta función únicamente en un determinado modelo o colección:

Usuarios.prototype.sync = function(method, model, options){ /* ... */ }

Un buen ejemplo de personalización de Backbone.sync() es este adaptador para Local Storage. No dejes de echarle un vistazo.

Conclusión

Con este tema doy por finalizado el tutorial de Backbone. Como dije al principio intentaría realizar una aplicación de ejemplo, aunque ya hay bastante material dentro de la sección de tutoriales de Backbone, aunque todos ellos en inglés. Si saco algo de tiempo la realizaré.

Intentaré mantener actualizado este tutorial con las nuevas funcionalidades que se vayan incorporando en sucesivas versiones de la librería.

 

Jan 12, 2012

Tutorial de Backbone.js – V. Routers

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

En la programación web tradicional es normal hacer routing en el servidor y según la URL solicitada ofrecer un contenido u otro. Por ejemplo, una URL como blog.com/post/5 podría significar que debemos mostrar una determinada entrada de un blog.

Las aplicaciones JavaScript basadas en una única página no tienen esta separación de contenidos ya que toda la aplicación se genera en la misma página, por lo que sería necesario disponer de algún mecanismo que nos permitiese emular dicho comportamiento y así poder enlazar secciones o contenidos específicos de nuestra aplicacion.

El mecanismo que se utiliza en estos casos es el uso de los fragmentos de página utilizando la almuadilla (#) como parte de la URL. Siguiendo el ejemplo anterior podríamos generar la siguiente URL: blog.com/#/post/5. En Backbone serán los routers los encargados de ofrecernos esta funcionalidad, llamados controladores en versiones previas a la 0.5 de la librería.

Definición de un Router

Podemos definir un router a través del método extend del objeto Backbone.Router. En él especificaremos el objeto hash routes que contendrá todas las rutas que se atenderán, debiendo contener al menos una. En este objeto hash introduciremos la ruta como key y una función asociada como valor que será la que se ejecutará cuando estemos en dicha ruta.

Enrutador = Backbone.Router.extend({
    routes:{                                     // Ejemplos de coincidencias:
        ""                  : "index"
        "help"              : "ayuda",           // #help
        "tag/:tagid"        : "muestraEtiqueta"  // #tag/perro
        "tag/:tagid/p:page" : "muestraEtiqueta"  // #tag/perro/p5
        "download/*file"     : "descargar"        // #download/path/to/file.txt
 },
    index: function() { /* ... */ },
    ayuda: function(){ /* ... */ },
    muestraEtiqueta: function(tagid, page){ /* ... */ },
    descargar: function(file){ /* ... */ }
 });

Si nos fijamos en el ejemplo anterior, podremos observar que hemos definido algunos parámetros dinámicos dentro de las rutas. Backbone ofrece dos tipos de variables para especificar variables dentro de una URL. Por una parte estarían las variables de tipo “:param” que captura todo el contenido de la URL desde su definición hasta la próxima barra (/) de la URL; por otra parte estarían las variables de tipo “*param” que capturan todo el contenido de la URL a partir de su definición, por lo que se deben utilizar siempre al final de la definición de la ruta. Las secciones capturadas por las variables se pasan como parámetros de la función asociada a la ruta.

Este mecanismo de definición de variables es bastante útil por su sencillez, pero si necesitamos más potencia a la hora de especificar una ruta podemos hacer uso de las expresiones regulares. Para definir una ruta mediante una expresión regular podemos utilizar el método route:

Enrutador = Backbone.Router.extend({
    initialize: function(){
        this.route(/post\/(\d+)/, 'id', function(pageId){/* ... */});
    }
});
var miRouter = new Enrutador;

Esta función permitirá definir una ruta tradicional o una expresión regular en su primer parámetro, asociándosela a la función definida como tercer parámetro. El segundo parámetro de esta función nos permite asociar un nombre de evento a la ruta, de forma que se lanzará el evento route:nombre cada vez que se solicite la ruta especificada.

Especificación Ajax Crawling

Antes de continuar me gustaría incidir en un problema tradicional existente en las aplicaciones JavaScript. Cuando queremos que nuestra aplicación sea interpretable por motores de búsqueda como Google nos encontramos con el problema de que éstos no son capaces de interpretar el contenido dinámico generado por nuestra aplicación, ya que los buscadores no son capaces de ejecutar javascript. Además, cuando utilizamos los fragmentos hash en nuestras URLs de aplicación ninguna de las rutas serán indexadas pues para el buscador se trata siempre de la misma URL.

Para solucionar este problema nació la especificación Ajax Crawling, que nos permite detectar cuando un contenido específico está siendo solicitado por un buscador y así tener la oportunidad de generar el correspondiente contenido estático desde el servidor.

Esta especificación exige que antepongamos el signo ! tras la amuadilla (#) y delante de cualquier ruta hash dinámica. Un ejemplo claro sería twitter:

http://twitter.com/#!/almarinero

Esta exclamación avisará al motor de búsqueda que nuestra aplicación está preparada para la especificación Ajax Crawling, y traducirá la solicitud a la siguiente URL:

http://twitter.com/?_escaped_fragment_=/maccan

De esta forma, desde el servidor podremos capturar la ruta que nos solicita el buscador examinando la variable GET _escaped_fragment_ y poder generar directamente la versión estática de dicha ruta.

Si decidimos seguir esta especificación, tendremos que tener en cuenta el signo ! en nuestras rutas a la hora de definir nuestro router.

Enrutador = Backbone.Router.extend({
    routes: {
        "!/post/:title": "post" // #!/post/tutorial-backbone-js
    }
    // ...
});

Backbone.history

Una vez definidos todos nuestros routers con todas sus rutas asociadas, es necesario que alguna entidad se encargue de monitorizar las solicitudes realizadas dentro de la aplicación para capturar los eventos hashchange (actualización de fragmento hash) del navegador y así poder aplicar la ruta oportuna y ejecutar las funciones asociadas. Esta entidad es Backbone.Hostory, y debemos crearla tras definir todos nuestros routers invocando al método start([options]):
Una vez que hayamos definido todas las rutas de nuestra aplicación en uno o más routers, debemos iniciar

Backbone.history.start();

Si nuestra aplicación no empieza desde el la URL raíz /, podemos indicárselo a través de la propiedad root:

Backbone.history.start({root: '/app/home'});

Ojo, para poder utilizar Backbone.history deberás haber creado al menos una instancia de los routers que hayas definido.

Resumen

En esta lección hemos aprendido a utilizar los routers para segmentar nuestra aplicación y poder asociar funcionalidades específicas a cada URL almuadilla, simulando el funcionamiento de una aplicación web tradicional y facilitando la navegación y el anclaje de contenidos. También hemos visto la especificación Ajax Crawling que nos indica cómo hacer hacer aplicaciones javascript accesibles por buscadores.

En el siguiente y último tema veremos cómo comunicarnos con el servidor y poder sincronizar nuestros modelos.

Tutorial de Backbone.js – IV. Vistas

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

Las vistas en Backbone son clases de control que nos ayudarán a representar nuestros modelos dentro de la interfaz de usuario de nuestra aplicación, detectando eventos de modificación para realizar las actualizaciones correspondientes.

Realmente las vistas no realizan un tratamiento directo sobre el HTML o CSS de tu aplicación, y Backbone espera que utilicemos algún sistema de templates que se encargarán de realizar dicho trabajo. Normalmente utilizaremos _.template de underscore.js o algún plugin de templates jQuery.

Creación de vistas

Como todo objeto Backbone que hayamos visto, lo primero que debemos hacer para crear una vista es definirla utilizando el método extend de Backbone.View:

FichaCliente = Backbone.View.extend({
    initialize: function(){ /* ... */},
    render: function(){ /* ... */}
});
var ficha = new FichaCliente();

Además de la función initialize que ya conocemos, también implementaremos otra función llamada render que se llamará cada vez que se necesite redibujar la vista.

La propiedad el

Cada instancia de una vista contendrá una propiedad llamada el, que será el objeto DOM que tendrá todo el contenido de la vista. Este elemento se crea automáticamente como un elemento div vacío, salvo que se indique lo contrario con las propiedades tagName y className:

FichaCliente = Backbone.View.extend({
    tagName: 'li',
    className: 'ficha'
});
alert( (new FichaCliente).el.className)); // 'hola'

Por defecto, dicho elemento se crea fuera de la estructura DOM del navegador, por lo que no será visible hasta que no lo insertemos en algún otro elemento existente en el documento:

$(document).ready(function(){
    Vista = Backbone.View.extend({
       render: function(){
           $(this.el).text('Hola Mundo');
           return this;
       }
    });
 
    var v = new Vista();
    $('body').append(v.render().el); //Insertamos el div dentro del body de la página
});

En este ejemplo hemos utilizado la función $.ready de jQuery para asegurarnos de que el código se ejecute cuando se haya cargado completamente el documento, cosa que debemos hacer siempre que queramos manipular el DOM. Nos hemos apoyado en jQuery como manipulador DOM ya que Backbone no realiza estas tareas, pero también podríamos haber utilizado Zepto.js.

Por otra parte, también podríamos enlazar la propiedad el con algún elemento existente. Esto lo podemos hacer en el momento de la definición de la vista si queremos que todas las instancias de dicha vista compartan el mismo el, o hacerlo en el momento de la creación de la instancia:

FichaCliente = Backbone.View.extend({
    el: $('.listaFichas')    // Todas las vistas FichaCliente compartirán el mismo el,
                             // salvo que se redefina al crear la instancia
});
var fichaActual = new FichaCliente({
    el: $('#fichaActual')  // Indicamos el elemento DOM de esta instancia en particular
});

También conviene indicar que si se utiliza jQuery o Zepto.js, cada instancia de vista tendrá una función local $ que permitirá realizar búsquedas locales dentro de la vista:

this.$('.nombreClase') == $('.nombreClase', this.el); // true

Dibujando vistas con plantillas

Como hemos dicho al principio, la función render será la encargada de dibujar nuestra vista, incluyendo dentro del elemento el de cada instancia todo el contenido HTML de la vista.

Aunque no es obligatorio, lo ideal es apoyarse en algún sistema de plantillas para mantener nuestro código javascript separado del contenido HTML. Backbone en sí mismo no ofrece ningún tipo de soporte para estas tareas, pero la librería underscore.js sí tiene un sistema básico de plantillas, y ya que Backbone depende de underscore.js podemos utilizar este sistema de plantillas para no tener que incluir ningún sistema externo.

Además de permitir separar nuestro HTML de nuestro código JavaScript, el uso de plantillas nos permitirá definir variables que podamos enlazar con los datos de instancias de modelos. Para que todo esto nos quede claro, veamos el siguiente ejemplo:

FichaCliente = Backbone.View.extend({
    template: _.template($('#ficha_template').html()),
    render: function(){
        $(this.el).html(this.template(this.model.toJSON()));
        return this;
    }
});
var cliente = new Cliente({nombre:'Alfonso', apellidos:'Marín Marín'});
var ficha = new FichaCliente({el:$('body'), model: cliente});
ficha.render();

En el ejemplo lo primero que hacemos es definir una plantilla identificada como ‘ficha_template’ donde se esperan dos variables a mostrar: nombre y apellidos. Posteriormente definimos nuestra vista donde guardamos la plantilla a utilizar en la propiedad template. Además implementamos el método render donde enlazaremos nuestra plantilla con la instancia de modelo que contendrá los datos, la cual se espera en la propiedad model y utilizando el método toJSON para obtener un objeto hash con los datos de la instancia.

Una vez definida la vista, creamos una instancia del modelo y otra instancia de la vista, asociándole la instancia de modelo en la propiedad model e indicando además que su elemento contenedor el será el body del documento. Por último llamamos a la función render para que cree todo el contenido de la vista bajo el contenedor body.

Delegado de eventos

Gracias al sistema de delegación de eventos de Backbone, una vista puede definir el comportamiento ante los eventos que ocurran dentro de los elementos existentes bajo el elemento contenedor el. Para ello se define un objeto hash llamado events con el siguiente formato en cada propiedad: {“event selector”: “callback”}. De esta forma indicaremos que para cada evento que ocurra bajo los elementos indicados por el selector se ejecutará la función callback.

FichaCliente = Backbone.View.extend({
    events:{
        "click .close": "cierraFicha",
        "click .sel" : "seleccionaFicha",
        "mouseover .title": "muestraToolTip"
    },
    seleccionaFicha: function(){
        this.model.set({'selected': true});
    }
    cierraFicha: function(){ /* ... */},
    muestraToolTip: function(){ /* ... */}
});

Si no se define ningún selector, el evento se asocia al propio elemento contenedor el. Una de las grandes ventajas de la delegación de eventos es que todas las funciones callback se invocan en el propio contexto de la vista, de forma que this apuntará a la propia instancia de la vista.

Enlazando vistas con modelos

Normalmente querremos sincronizar nuestras vistas con las instancias de modelo subyacentes. Para conseguir eso debemos enlazar los eventos de modificación de la instancia de modelo a nuestra vista, cosa que haremos normalmente dentro del método initialize:

FichaCliente = Backbone.View.extend({
    initialize: function(){
        this.model.bind('change', this.render, this);
        this.model.bind('destroy', this.remove, this);
    },
    render: function(){ /* ... */},
    remove: function(){ /* ... */}
});

Es importante cerciorarse de que enlazamos los eventos del modelo en el contexto apropiado. Como normalmente los métodos de vistas esperan ejecutarse en el contexto de la propia instancia de vista, en el método bind establecemos this como tercer parámetro, indicando así que queremos utilizar la propia instancia de vista como contexto.

Resumen

En este tema hemos analizado la creación de vistas Backbone y la importancia de la propiedad el. Hemos visto cómo hacer uso de plantillas para estructurar e independizar mejor nuestro código HTML, hemos analizado los eventos que intervienen en el sistema de vistas y por último hemos visto cómo enlazar nuestras vistas con los modelos de nuestra aplicación.

En el siguiente tema analizaremos otro componente de backbone: los Routers.

Tutorial de Backbone.js – III. Colecciones

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

Tras haber analizado los modelos en el tema anterior, en este tema vamos a ver las colecciones, que no son otra cosa que conjuntos ordenados de instancias de modelos.

Normalmente una colección contendrá instancias de un único modelo, aunque realmente no hay restricción alguna y una colección podría contener instancias de distintos modelos.

Creación de colecciones

Como ocurría en los modelos, podemos crear una colección extendiendo el objeto Backbone.Collection donde especificaremos el modelo que asociaremos con él:

Clientes = Backbone.Collection.extend({
    model: Cliente
});

Al igual que los modelos, las colecciónes también disponen de una función initialize que podremos implementar en la definición de la colección. De igual manera el método extend admite un segundo parámetro opcional para poder indicar ciertas propiedades a nivel de clase.

Indicar el modelo asociado en el comando extend sirve para que podamos añadir objetos a la colección indicando simplemente los atributos, y será la colección la que se encargará de convertirlos a instancias del modelo:

var clientes = new Clientes([{nombre:'Alfonso', apellidos:'Marín'}, 
                             {nombre:'Javier', apellidos:'Serrano'}]);

Como vemos, podemos crear una instancia de la colección indicando un array de objetos hash que serán convertidos a instancias del modelo asociado (Cliente) y formarán parte de la colección. Este array de objetos es opcional, pues podemos añadir objetos posteriormente utilizando add :

var clientes = new Clientes();
clientes.add([{nombre:'Alfonso', apellidos:'Marín'}, 
              {nombre:'Javier', apellidos:'Serrano'}]);

También podemos utilizar el método remove para eliminar objetos de la colección indicándole la instancia o array de instancias a eliminar.

Por último, existe la función reset que eliminará toda instancia existente en la colección y la sustituirá por las instancias que esta función reciba como parámetro. Si no se le pasa ningún array de instancias, este comando vacía completamente la colección.

Recuperar objetos de la colección

Para recuperar una instancia determinada podemos buscar por sus identificadores o por su posición:

//La función get buscará la instancia cuyo id sea el que le pasamos como parámetro
clientes.get("id-de-cliente");
// La función getByCid buscará la instancia cuyo cid sea el que le pasamos como parámetro
clientes.getByCid('valor-cid');
// La función at buscará la instancia situada en la posición pasada como parámetro
clientes.at(1)

Al igual que ocurría en los modelos con la propiedad attributes, los objetos Collection tienen una referencia directa al array de modelos asociados a la colección llamada models, siendo un candidato ideal sobre el que utilizar toda la potencia de los métodos de la librería underscore.js. Un ejemplo:

// filter: itera sobre el array o hash pasando los elementos a una función callback
// Devuelve un array o hash con los elementos que hayan devuelto 'true' en la función callback
var clientes_juvilados = _.find(clientes.models, function(cliente){ return cliente.edad > 65 });

Eventos

Las colecciones generan un evento add o remove cada vez que se añade o elimina un elemento a la colección, respectivamente. Además por convenio cada evento que se genere en alguno de los modelos de la colección se generará también en la colección directamente. De esta forma, una colección podría capturar los eventos change:atributo de forma que sería notificada cada vez que cambia dicho atributo en alguno de los modelos que la componen.

También se genera un evento reset cuando se ejecuta dicho comando sobre la colección.

Ordenación

Por defecto, las colecciones mantienen los modelos desordenados y los guardan según su orden de inserción. Si quisiéramos mantener ordenada la colección, podríamos definir la función comparator a la hora de definir el modelo. Esta función recibirá un modelo como parámetro y deberá devolver un número o una cadena de caracteres por la que se ordenará la colección, ya sea numéricamente o alfabéticamente.

Clientes = Backbone.Collection.extend({
    comparator: function(cliente){
        // ordenamos por el atributo nombre
        return cliente.nombre;
    }
    // ...
});

Si una colección tiene definida la función comparator permanecerá ordenada en todo momento ya que cada vez que se inserte una nueva instancia de modelo la colección ejecutará el proceso de ordenación. Normalmente se hace de forma automática, pero aún así existe un método llamado sort que forzaría la ejecución del proceso de ordenación. Este método también generaría el evento reset.

Resumen

Hemos descubierto las colecciones, aprendido cómo añadir, eliminar y buscar elementos. También hemos aprendido qué eventos generan las colecciones y que podemos ordenarlas si queremos. Todavía quedan algunas funciones por ver, pero están más relacionadas con la sincronización y la persistencia en el servidor. Por ese motivo los analizaremos más adelante.

En el siguiente tema abordaremos la parte más visual del Backbone: las vistas.

Tutorial de Backbone.js – II. Modelos

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

En este segundo tema vamos a seguir conociendo la librería Backbone explicando uno de sus componentes más importantes: los Modelos.

Los modelos son los encargados de almacenar los datos de tu aplicación, así como proporcionar un conjunto de funcionalidades comunes y conectarlos al sistema de eventos Backbone para notificarnos cuando un modelo ha sido creado, modificado o borrado.

El método extend

Para crear un en modelo Backbone utilizaremos el método Backbone.Model.extend():

var Cliente = Backbone.Model.extend({
    initialize: function(){
       alert('Esta funcion se llamará en la creación de cada instancia')
    }
});
 
var cliente = new Cliente();

Este método espera recibir como primer parámetro un objeto hash donde se definen las propiedades de cada instancia del modelo. Podremos así sobreescribir algunas propiedades predefinidas, como la función initialize que se invocará a la hora de crear cada una de las instancias del modelo.

Opcionalmente el método extend puede aceptar un segundo hash con propiedades que se definirán a nivel de clase, es decir, propiedades estáticas:

var Modelo = Backbone.Model.extend({
    propiedadDeInstancia: "Valor de instancia"
},{
    propiedadDeClase: "Valor de clase"
});
var modelo = new Modelo();
alert(modelo.propiedadDeInstancia); // Valor de instancia
alert(Modelo.propiedadDeClase); // Valor de clase (date cuenta de la mayúscula)

Siempre que utilizemos extend, los objetos Backbone se definirán a través de la cadena de prototipado de objetos JavaScript, lo que significa que podemos extender y crear subclases de modelos ya definidos:

Persona = Backbone.Model.extend({
// Definicion de persona
});
 
Cliente = Persona.extend({
});

Trabajando con atributos

Una vez definido un modelo, podremos crear una instancia utilizando new y pasarle como primer parámetro los atributos que queremos almacenar en el modelo:

var cliente = new Cliente({nombre: 'Antonio', apellidos: 'García Ros'});

Podemos especificar los atributos que queramos, y éstos pasarán a formar parte de los atributos del modelo. Además de los atributos, podemos pasar un segundo parámetro que contendrá otro objeto hash de opciones. Tanto el hash de atributos como el hash de opciones se pasarán como parámetros en la función initialize.

A la hora de crear una instancia de un modelo, podemos asociarle cualquier conjunto de atributos que deseemos, pero si queremos asegurarnos de que toda instancia de nuestro modelo contenga al menos un conjunto predefinido de atributos, podemos definir una sección default en las propiedades del método extend:

Cliente = Backbone.Model.extend({
    defaults: {
        empresa: 'Unknow',
        telefonos: []
    },
    initialize: function(attrs, opts){
    }
});

De esta forma nos aseguraremos de que cada instancia de nuestro modelo contenga un conjunto predefinido de atributos, así como unos valores por defecto.

Cada instancia de modelo almacena los atributos en una variable interna llamada attributes, pero no se recomienda trabajar con ella directamente. En vez de eso utilizaremos los métodos get y set, garantizando así que se realicen las validaciones y disparado de eventos oportunos de modificación. set(attrs, [options]) permite asignar un hash de atributos a la instancia, y get(attr) devuelve el valor del atributo pasado como parámetro:

cliente.set({edad: 31, soltero: false});
alert(cliente.get('edad')); // 31

También podemos incluir nuestras propias funciones de manipulación a la hora de definir nuestro modelo:

Cliente = Backbone.Model.extend({
    defaults: {
        empresa: 'Unknow',
        telefonos: []
    },
    nuevoTelefono: function( n_tel ){
        var telefonos_array = this.get("telefonos");
        telefonos_array.push( n_tel );
        this.set({ telefonos: telefonos_array });
    }
});
var cliente = Cliente({name: 'Alfonso Marin', company:'Universidad de murcia', telefonos:['968000000']});
cliente.nuevoTelefono('687000000');
var telefonos = cliente.get('telefonos'); // ['968000000', '687000000']

Cada vez que se establece o modifica un atributo, ya sea en la creación de la instancia o a través de la función set, Backbone invoca a la función validate del modelo. Por defecto esta función no está definida, y podemos establecerla en el momento de la definición del modelo:

Cliente = Backbone.Model.extend({
    validate: function(attrs){
        if (attrs.edad < 18){
            return "Demasiado joven para ser cliente";
        }
    }
});

Si el proceso de validación se pasa con éxito, la función no devolverá nada. En caso contrario, esta función deberá devolver una cadena de caracteres o una instancia de la clase Error. Además provocará que no se finalice la ejecución del método set generando un evento error. Para capturar dicho error, podremos definir la propiedad error del objeto hash de opciones del método set con la función que deseemos ejecutar en caso de error:

cliente.set({edad:15, {error:function(model, error){
  //...
}});

obviamente esto es bastante engorroso, pues cada vez que utilicemos set deberíamos asociarle la función de error. En vez de eso, lo que se suele hacer es enlazar los eventos error de un determinado modelo a una función callback, como ya vimos en el tema anterior:

cliente.bind('error', function(model, error){
    alert('Error de validación: ' + error);
});

Capturando eventos de modificación en un modelo

Cada vez que se modifica el valor de un atributo a través del método set, se generan dos eventos: uno llamado change y otro llamado change:nombre_de_atributo. Utilizando el mecanismo de enlazado (bind) podremos detectar cuando cambia alguno de los atributos del modelo o algún atributo en particular:

Cliente = Backbone.Model.extend();
var cliente = new Cliente({nombre:'Alfonso',apellidos:'Marín Marín'});
 
cliente.bind('change',function(target, options){
    //options es el mismo objeto hash que se pasa desde el comando set(attrs, [options])
    alert('Atributo modificado');
});
 
cliente.bind('change:nombre', function(target, valor, options){
   //Podemos acceder a la variable interna
   var old = this.previousAttributes().nombre;
   alert('Modificado nombre a ' + valor + '. Antiguo valor:' + old);
});
cliente.set({nombre:'Antonio'});

Como podemos ver en el ejemplo, podemos hacer uso del método previousAttributes para acceder al valor de todos los atributos del modelo en su estado previo al cambio, o utilizando previous(attr) para conocer el antiguo valor de un determinado atributo. Si por el contrario solo quisiéramos el subconjunto de atributos modificados, podremos hacer uso de changedAttributes:

var cliente = new Cliente({nombre:'Alfonso',apellidos:'Marín Marín'});
cliente.bind('change', function(){
    alert(JSON.stringify(this.changedAttributes()));
});
cliente.set({nombre:'Antonio', edad:'31'});// Alerta imprimiría: {"nombre":"Antonio","edad":"31"}

Existen otros métodos de un modelo que disparan el evento change:

  • unset(attr, [options]): elimina un atributo del modelo.
  • clear([options]): elimina todos los atributos del modelo

Identificadores de instancias: id y cid

Cada instancia de podrá tener un atributo identificador que corresponda con su identificador en el servidor. Por defecto dicho atributo se llama id, aunque podemos especificar el que nosotros queramos a través de la propiedad idAttribute:

Cliente = Backbone.Model.extend({
    idAttribute: 'dni'
});

Como veremos en futuros temas, dicho identificador será importante para las operaciones de sincronización, y debería ser único entre las instancias de un mismo modelo.

Por otra parte, cada vez que creamos una instancia internamente se le asignará otro identificador llamado cid, que es el identificador de cliente y será único entre todas las instancias de todos los modelos backbone creados en el cliente. Es especialmente útil cuando creamos instancias que todavía no se han guardado en el servidor y no tienen su propio identificador oficial. Este identificador seguirá el siguiente patrón de numeración: c0, c1, c2, … Podemos acceder a él a través de la propiedad cid:

Cliente = Backbone.Model.extend();
var cliente = new Cliente();
alert (cliente.cid); // c0

Resumen

Hemos visto cómo se pueden definir modelos y crear instancias de ellos. También hemos aprendido cómo podemos crearlos, validarlos y modificarlos, así como saber en todo momento qué atributos se están modificando.

Existen otros métodos interesantes relacionados con los modelos, pero la mayoría de ellos tienen que ver con su tratamiento dentro de las colecciones de modelos y con la sincronización y guardado de datos con el servidor. Los iremos viendo en sucesivos temas según vayamos necesitándolos.

En el siguiente tema abordaremos las colecciones de objetos.

Tutorial de Backbone.js – I. Eventos

Nota: esta entrada forma parte del tutorial Backbone.js que estoy realizando.

Introducción

En este primer tema me gustaría explicar lo que considero el componente más transversal de Backbone: la gestión de eventos. Entendiendo el sistema de eventos asentaremos las bases que nos ayudarán a entender mejor los distintos componentes que veremos en sucesivos temas.

Backbone.Event

La librería Backbone se apoya en el concepto de evento como método principal de comunicación entre componentes. Cada uno de los elementos de Backbone será capaz de emitir y capturar eventos para conectar los distintos elementos de la aplicación.

Backbone tiene un pequeño módulo de eventos que puede utilizarse para extender cualquier objeto de nuestra aplicación, dotándole de esta forma de la posibilidad de enviar y capturar eventos arbitrarios.

Para que un objeto posea la capacidad de capturar y enviar eventos, debe extender Backbone.Events con la función _.extend de underscore.js:

var mi_objeto = {};
MiObjeto=_.extend(mi_objeto, Backbone.Event);
 
// Con la funcion bind podemos enlazar un evento cualquiera con una
// función callback que se ejecutará cuando este evento ocurra en este objeto
mi_objeto.bind("un_evento", function(msg){
    alert('Vaya, a ocurrido un_evento con mensaje' + msg);
});
 
// Un objeto puede disparar un evento en el momento que desee
// utilizando la función trigger
mi_objeto.trigger('un_evento','de_ejemplo');

Un evento se identifica por una cadena de caracteres arbitraria, aunque por convenio, si una aplicación utiliza un número alto de eventos se recomienda separar identificadores entre símbolos de dos puntos para establecer espacios de nombres. Ejemplos: ‘actualizar:sidebar’ o ‘alerta:nivel1′. Más adelante veremos cómo el propio Backbone hace uso de este convenio para sus propios eventos.

Enlazado de eventos: binding

Como hemos visto en el ejemplo anterior, con la función bind(event, callback, [context]) podemos enlazar un evento a una función callback que se ejecutará cuando este evento se dispare en el contexto del objeto enlazado. Opcionalmente, podremos incluir un contexto sobre el que se ejecutará la función callback, ya que por defecto se ejecutará en el contexto del objeto que recibe el evento (en el ejemplo, this será mi_objeto).

Existe un evento especial llamado ‘all’ que nos permitirá enlazar todos los eventos de un objeto a una misma función callback, a la cual se le pasará como parámetro el nombre del evento:

mi_objeto.bind('all', function(nombre_evento){
  // Implementación
}

Un mismo objeto podrá enlazar tantas funciones como desee a un mismo evento:

mi_objeto.bind('un_evento', function(){
    alert('Función callback 1');});
mi_objeto.bind('un_evento', function(){
    alert('Función callback 2')});
mi_objeto.trigger('un_evento'); // Se ejecutan los dos alert;

Desenlazado de eventos: unbinding

Para desenlazar los eventos enlazados, utilizaremos unbind([evento], [callback]) donde opcionalmente aceptará un nombre de evento y una función callback desenlazar. Si no se especifica ninguna función callback, se desenlazarán todas las funciones asociadas al evento especificado, y si no se especifica ningún evento, se desenlazarán todas las funciones de todos los eventos del objeto:

mi_objeto.unbind('mi_evento', miFuncionCallback); // Desenlaza solo la función miFuncionCallback del evento mi_evento
mi_objeto.unbind('mi_evento');                    // Desenlaza todas las funciones callback del evento mi_evento
mi_objeto.unbind();                               // Desenlaza todas las funciones de todos los eventos

Generación de eventos: triggering

Un objeto también podrá disparar cualquier evento en cualquier momento haciendo uso de trigger(event, [*args]) a la que le indicaremos un nombre de evento y opcionalmente una cadena de parámetros que se pasarán como argumentos a la función callback enlazada:

mi_objeto.bind('mi_evento', function(arg1, arg2){
    alert('Evento que recibe 2 argumentos');
});
mi_objeto.trigger('mi_evento', 'primer argumento', 'segundo argumento');

Resumen

En este tema hemos visto lo fácil que es dotar a nuestros objetos de la capacidad de generar y recibir eventos gracias a Backbone.Event.Como veremos, este mecanismo será ampliamente utilizado en el resto de componentes de Backbone.

En el siguiente tema analizaremos uno de los principales componentes de backbone: los Modelos.

Tutorial de Backbone.js

Llevo un tiempo analizando y aprendiendo a trabajar con la librería Backbone.js y me parece una librería estupenda para desarrollar aplicaciones JavaScript. El mayor problema que estoy encontrando es la falta de tutoriales y documentación al respecto, y por ese motivo he decido crear un pequeño tutorial donde se analice el funcionamiento de la librería. Este tutorial lo iré desarrollando poco a poco en distintas entradas, intentando resaltar los aspectos más importantes y analizando cada uno de sus componentes.

Introducción

Backbone es una excelente librería para construir aplicaciones JavaScript que nos ofrece la posibilidad de estructurar nuestro código siguiendo el patrón MVC (Modelo, Vista, Controlador). Como su nombre indica, el objetivo de esta librería es formar parte de la columna vertebral de nuestra aplicación ofreciéndonos únicamente la funcionalidad necesaria para que puedas estructurarla correctamente. De hecho, esa es una de sus grandes virtudes pues se centra únicamente en proporcionarte conceptos básicos como modelos, eventos, colecciones, vistas, routing y persistencia. En tan solo 4KB Backbone proporciona un marco de trabajo estupendo que podremos utilizar como complemento a jQuery.

Una librería JavaScript como jQuery es extremadamente útil para seleccionar y modificar elementos del DOM, uso homogéneo de AJAX o generación de efectos y animaciones, pero a la hora de hacer una aplicación web de una única página donde gran parte de la funcionalidad reside en código JavaScript, rápidamente descubrirás que no es suficiente. En cuanto incluyas ciertas funcionalidades a tu aplicación te verás sobrepasado por una gran pila de selectores y callbacks sin estructura alguna que estarán lidiando directamente con los elementos DOM.

Como veremos, en Backbone los datos los introducimos en modelos que podremos crear, validar, destruir y salvar en el servidor, ofreciendo un conjunto de eventos que se dispararán cuando el modelo cambie. También se podrán asociar a vistas que serán notificadas de dichos cambios para que puedan actualizar su contenido; agruparemos los modelos en colecciones y podremos implementar las distintas funcionalidades de nuestra aplicación utilizando routers que nos mapearán distintas rutas de aplicación con funciones específicas.

Descarga y puesta a punto

La única dependencia que tiene Backbone es underscore.js, una librería llena de utilidades y funciones JavaScript de propósito general creada por la misma gente de Backbone, y entre las funcionalidades que ofrece se encuentran la manipulación de arrays, el enlazado de funciones (binding) o templates JavaScript. Por otra parte, Backbone no realiza ninguna operación de manejo y modificación del DOM o tratamiento de AJAX,  por lo que las vistas delegan dichas funcionalidades a librerías como jQuery o Zepto.js.

Por ese motivo, para la realización de los distintos ejemplos de este tutorial deberíamos crear una página HTML que se encargase de incorporar todos estos elementos:

    <script src="js/jquery.js"><!--mce:0--></script>
    <script src="js/underscore.js"><!--mce:1--></script>
    <script src="js/backbone.js"><!--mce:2--></script>
    <script src="js/examples.js"><!--mce:3--></script>

Como podrás imaginar, en el fichero examples.js será donde vayamos introduciendo nuestro propio código.

Partes de este tutorial

Como he comentado al principio, este tutorial lo voy a desarrollar a lo largo de varias entradas, una por cada tema a tratar. A continuación os muestro los distintos temas que abordaremos:

  1. Eventos
  2. Modelos
  3. Colecciones
  4. Vistas
  5. Routers
  6. Sincronización y persistencia

Al final del tutorial intentaré abordar la creación de una aplicación completa que pudiera de servir como ejemplo práctico, pero no me comprometo a ello :)

Actualizaciones

Este tutorial se basa en la versión 0.5.3 de Backbone. Mi intención es ir actualizándolo según vayan incluyendo nuevas funcionalidades en versiones posteriores.

¿Programas web? Apuesta por JavaScript

Cada día lo tengo más claro, la web se está programando en JavaScript.

La evolución de una tecnología

Originalmente JavaScript fue un lenguaje introducido en los navegadores web para poder dotar a las páginas web de ciertas funcionalidades “auxiliares”. Gracias a él se podían hacer cosas como validación simple de formularios, mostrar la hora en tiempo real o hacer que una línea de texto se moviese por la pantalla (ooooh!). Era un lenguaje que estaba ahí y al que nadie tomaba muy en serio, empezando por los navegadores que hacían implementaciones del mismo bastante particularizadas, lo que por otro lado provocaba que fuese bastante pesado ponerse con él.

Desde mi punto de vista fue Google quien nos empezó a mostrar las posibilidades reales de este lenguaje allá por 2004 mostrándonos joyas como GMail o Google Maps , donde conceptos como AJAX empezaron a hacerse muy populares en las comunidades de programadores web. También empezó a popularizarse el concepto web 2.0 donde los usuarios interactuaban mucho más con la web gracias a que éstas ofrecían un mayor número de funcionalidades y un mayor dinamismo. Todo ello conseguido gracias a JavaScript.

Todavía quedaba un gran problema a solventar, y era que la tecnología no había gozado de una estandarización inicial y era muy complicado hacer código compatible con cada uno de los navegadores de la época. Fue entonces cuando empezaron a surgir librerías que nos ayudarían a lidiar con esta complejidad, como son Prototype, jQuery, dojo, MooTools, y un largo etcétera. Gracias a estas librerías, la mayoría de desarrolladores web quedaron impresionados de lo fácil que era incluir funcionalidades realmente avanzadas de una forma relativamente sencilla y sin tener que preocuparse por las particularidades de cada navegador.

Tras estas librerías base han aparecido grandes framworks para la programación de aplicaciones web llenos de widgets listos para utilizar y que consiguen llevar la funcionalidad de las aplicaciones de escritorio a la web. Ejemplos de estos frameworks son ExtJS, jQuery UI o Dojo Toolkit. En este punto es donde descubrimos que gracias a este lenguaje y a estas librerías, podríamos trasladar una ingente cantidad de aplicaciones de escritorio a nuestro navegador.

Toda esta evolución nos ha puesto bien claro a cualquier desarrollador web que JavaScript puede dar mucho de sí. Aunque sea de forma maquillada como nos lo pueden ofrecer las librerías y framework mencionados, sabemos que gracias a JavaScript podremos crear auténticas aplicaciones dentro de nuestro navegador.

Pero esto no es nada comparado con lo que se avecina. Pienso que JavaScript va a tener muchísima más relevancia en el mundo del desarrollo web del que ha tenido nunca gracias a tres nuevos actores: HTML5 , node.js y aplicaciones móviles multiplataforma.

HTML5

Quien se haya adentrado un poco en la nueva tecnología HTML5, habrá comprobado que va más allá de una reestructuración del etiquetado. Es verdad que aporta nuevas formas de estructurar el contenido de la web, añadiendo nuevos controles de formulario y nuevas etiquetas para mejorar la información semántica de una web. Pero la especificación completa de HTML5 no se queda ahí.

HTML5 conlleva otras mejoras muchísimo más importantes y de mayor calado, como pueden ser la persistencia de datos, SQL en el navegador, tratamiento avanzado de ficheros, canvas, websockets, tratamiento de vídeo y audio, …. ¿y cómo se acceden a todas esas funcionalidades? A través de APIs expuestas vía JavaScript.

Gracias a HTML5, los ordenadores se están convirtiendo en auténticos sistemas operativos, y estos sistemas operativos se programan con JavaScript.

node.js

Esta tecnología sí que está marcando la diferencia de cómo vemos a día de hoy el lenguaje JavaScript. Si pensabas que únicamente podías utilizar este lenguaje en el lado del cliente, node.js nos permite llevarlo al lado del servidor.

Gracias a esta tecnología es posible hacer aplicaciones completamente en javascript, sin necesidad de apoyarse en otro lenguaje de programación. Pero no te confundas, esta tecnología va más allá de la simple “comodidad” de utilizar un único lenguaje de programación.

Gracias a node.js, es posible compartir ámbito de variables entre el servidor y el cliente, haciendo que ambos se comuniquen transparentemente y sin necesidad de utilizar variables de sesión ni cookies. Además, el servidor es realtime lo que nos permitirá sacar todo el partido a los websockets de HTML5 de forma directa. Esta funcionalidad no es trivial conseguirla en un típico servidor Apache.

Todavía le queda bastante camino por recorrer, pero hay que admitir que está siendo toda una revolución.

Aplicaciones móviles multiplataforma

Para terminar, un último campo en el que JavaScript está siendo muy relevante es en la aplicación de aplicaciones para móviles. No solo hablo de adaptar aplicaciones web a dispositivos móviles, cosa de la que se encargan muy bien librerías como jQuery mobile o Sencha Touch. Hablo de poder crear aplicaciones “nativas” utilizando JavaScript.

Existen soluciones como PhoneGap o Titanium que nos permiten desarrollar nuestra aplicación utilizando únicamente HTML, CSS3 y JavaScript, para luego poder generar aplicaciones para distintos sistemas móviles: Android, iOS, Blackberry,…

El truco consiste en que tu aplicación realmente es un navegador web donde incrustas todo el código, y estas plataformas se encargan de ofrecerte una API JavaScript para poder hacer uso del hardware específico como pueden ser los acelerómetros, la cámara, el GPS o los contactos.

Obviamente estas plataformas son más limitadas que las aplicaciones nativas reales de cada sistema operativo móvil, pero gracias a ellas se pueden conseguir aplicaciones multiplataforma utilizando tecnologías web, utilizando una vez más JavaScript.

Conclusión

Creo que queda claro mi punto de vista y creo que no me equivoco al pensar que JavaScript se está reinventando a sí mismo alcanzando cada vez un papel muy importante en el mundo del desarrollo. Si todavía no te has puesto con él, te recomiendo encarecidamente que empieces ya.

Si eres perezoso y ya eres bueno en otros lenguajes como Java, Python o Objective-C, existen compiladores que generan código java script a partir de estos lenguajes. GWT o DART para Java, CoffeeScript para Pyhton o Capucchino para Objective-C son algunos ejemplos.

Pero si lo que quieres es entender toda la potencia de este lenguaje, te recomiendo 3 libros esenciales:

 

 

Suscripción por email

Introduce tu dirección de correo para suscribirte a este blog y recibir notificaciones de nuevos posts por email: