Tutorial de AngularJS #3

En el tercer capítulo de este tutorial aprenderemos a almacenar información usando LocalStorage de HTML5 y JSON

Si recuerdan bien, lo que hicimos el capítulo anterior fue aterrizar una idea simple para poder ejecutar con AngularJS. El proyecto que iniciamos la semana pasada fue hacer una GifWallet. Algo así como una aplicación que nos permita acceder a nuestros gifs favoritos cuando queramos, así como tambien buscar nuevos y gestionarlos.

Lo último que hicimos en el código fue integrar la interfaz y hacer que nuestro controlador mostrara los datos de la variable images utilizando la directiva ng-repeat. Al final, el resultado fue el siguiente:

Si bien, el primer objetivo nos da una buena señal de lo que queremos lograr, no nos sirve para el objetivo final: Almacenar Gifs.

Ustedes se preguntarán ¿Y cómo puedo almacenar los gifs si recién estoy aprendiendo AngularJS?

Respuesta: Utilizando LocalStorage API HTML5 Double Rainbow Belong to Us. En realidad se llama LocalStorage; pero le puse más color para darle emoción.

LocalStorage

La definición técnica de LocalStorage es media compleja; pero la podemos explicar:

El almacenamiento DOM (DOM Storage) es el nombre dado al conjunto de [características relacionadas con el almacenamiento](http://www.whatwg.org/specs/web-apps/current-work/#storage) introducidas en la especificación de [aplicaciones web 1.0](http://www.whatwg.org/specs/web-apps/current-work/) y ahora detalladas por separado en su propia especificación [W3C Web Storage](http://dev.w3.org/html5/webstorage/ “http://dev.w3.org/html5/webstorage/”).

¿Qué significa esto? Significa que los exploradores modernos que soportan esta tecnología, especificada por la W3C, son capaces de guardar información de forma local.

En esta especificación encontramos 3 formas para almacenar información:

  • SessionStorage : Permite almacenar información durante la sesión de la página
  • GlobalStorage : Este objeto permite almacenar información a través de distintos sitios webs; pero está obsoleto.
  • LocalStorage : Este objeto permite almacenar información por largo tiempo, como GlobalStorage; pero con la diferencia de que está dentro del ámbito de HTML5 y es estándar.

En base a lo anterior nos damos cuenta que LocalStorage es la mejor opción ya que necesitamos que la información se almacene por largo tiempo, y que al momento de abrir la aplicación nos muestre los GIFs guardados.

Pero existe un pequeño detalle. Ocurre que la información solamente se puede guardar en formato de texto — técnicamente, formato String — y no podemos guardar objetos JSON, como lo habíamos definido en la variable images.

var images = [{
        name: 'Corgi Bailarín',
        url: 'http://media.giphy.com/media/12co3H23YQXLYk/giphy.gif',
        tags: ['corgi', 'baile', 'lol'],
        favorite: true
    }, {
        name: 'Michael Scott',
        url: 'http://media.giphy.com/media/zIPKUgO2Ln6XC/giphy.gif',
        tags: ['the office', 'michael scott', 'asap'],
        favorite: false
    }];

Como la estructura de esta variable es más compleja que una simple variable de texto, tendremos que meter manos al código y usar la función JSON que nos permitirá convertir un objeto a texto y viceversa.

Manos al código

Para comenzar, vamos a ir a nuestro archivo “app.js” y vamos a crear un servicio que se encargará de : guardar, obtener, eliminar y listar nuestros Gifs. Para esto, agregaremos bajo el controlador el siguiente código:

gifwalletApp.service('Storage', ['$window',
    function($window) {
        var images = [];

        this.save = function(image) {

        }

        this.get = function(key) {

        }

        this.remove = function(key) {

        }

        this.list = function() {
            return images;
        }
    }
]);

Como se darán cuenta, agregamos a nuestra variable “gifwalletApp” un método que nos permita agregar un servicio, el método “service“. En este método definimos el nombre del servicio como “Storage” y le entregamos a la función callback, la variable $window que es recuperada posteriormente en la función.

Además, volveremos a definir la variable images que luego reemplazaremos con los datos que sacaremos desde nuestro localStorage. Tambien definimos los métodos save, get, remove y list corresponden a las funciones que necesitamos para gestionar nuestros gifs.

Ahora, modificaremos nuestro controlador para que utilice este servicio, para eso, aprenderemos el concepto de Inyección de Dependencias.

Inyección de Dependencias

El concepto de Inyección de Dependencias en términos simples es la forma en que a una clase se le suministra un objeto para que utilice sus métodos, así de simple.

¡¿Cómo?! ¿No es tan simple? Mmm, veamos…

Imaginen que se compran una cámara fotográfica de buena calidad que viene con un lente por defecto y también se compran un gran angular para sacar fotos con mayor ángulo. Llega un momento que quieren usar las funciones que este gran angular ofrece y lo que hacen es cambiar el lente. Como resultado obtienen fotos de mejor calidad.

La moraleja de la historia es que al momento de que ustedes usaron el otro lente, le dieron nuevas funciones a la cámara fotográfica y la cámara siguió siendo la misma cámara que compraron. Haciendo un paralelo de conceptos, lo que hicieron fue inyectarle la dependencia de gran ángulo (objeto) a la cámara (clase) para sacar fotos en grán ángulo (método). ¿Se entendió?

La forma que tiene AngularJS para inyectar dependencias, por ejemplo, es la siguiente:

gifwalletApp.controller('Controlador', ['$dependencia1', 'Dependencia2', '...',
    function($dependencia1, Dependencia2, ...) {

    }
]);

En este ejemplo estamos inyectando dos dependencias antes de que se defina la función. Acá podemos ver la aparición de dos corchetes que encierran las dependencias y la función.

Las dependencias pueden ser objetos o variables, deben ser escritas con comillas simples antes de la función y sin comillas dentro de los parámetros de la función. La cantidad de dependencias y el orden de ellas debe ser respetado para que funcione.

Ahora que tenemos esto más claro, vamos a modificar nuestro controlador y vamos a inyectarle el servicio Storage que creamos anteriormente, y vamos a eliminar la variable images:

gifwalletApp.controller('GifListController', ['$scope', 'Storage',
    function($scope, Storage) {
        $scope.giflist = Storage.list();
    }
]);

Si volvemos a leer lo de inyección de dependencias y analizamos el cambio que hicimos, podemos notar que la variable $scope está siendo definida como primera dependencia y el servicio Storage como segunda dependencia.

Lo que también podemos notar es que eliminamos la variable images que mostraba la lista de gifs y la reemplazamos por lo que entrega el método “list” que se encuentra en nuestro servicio Storage.

Agregando una imagen

El siguiente paso es comenzar a agregar imágenes, hacer que se almacenen y que se listen después de ser almacenadas. Para esto, agregaremos la función “add” que permitirá agregar una URL, y luego que se presione aceptar se guardará en nuestro localStorage.

Para esto, agregaremos un nuevo controlador que estará en la sección del menú:

gifwalletApp.controller('MenuController', ['$scope', 'Storage',
    function($scope, Storage) {

        $scope.add = function() {
            var url = prompt('Ingrese una URL');
            if (url != null) {

                var image = {
                    'name': 'Ejemplo',
                    'url': url,
                    'tags': [],
                    'favorite': false
                };

                Storage.save(image);
            }
        };

    }
]);

Aquí, inyectamos $scope y Storage para poder agregar la función “add” y guardar nuestra imagen. Usaremos, mientras tanto, la función prompt de Javascript para preguntar la URL y crearemos un objeto “image” que enviaremos a almacenar usando el método “save“.

Para que esto funcione, tenemos que definir el lugar de la interfaz en donde nuestro controlador de menú va a operar. Así que lo que haremos ahora es usar la directiva “ng-controller” para definir que en el menú es donde va a operar este controlador:

<header ng-controller="MenuController">

Y después, agregamos la función “add” al botón “+” que está en el menú usando la directiva ng-click:

<li>
    <a href="#" ng-click="add()"><span class="glyphicon glyphicon-plus"></span></a>
</li>

Esta parte del código debería funcionar del siguiente modo:

Usando localStorage

Ahora, lo que nos resta es terminar nuestra función que guarda las imágenes. Para esto, modificaremos el servicio parte por parte.

Lo primero agregar, bajo la variable images, la pregunta del millon: ¿Tiene usted localStorage?

if (!$window.localStorage) {
    alert('No tienes localStorage activado');
} else {
    images = $window.localStorage.getItem('gifWallet');
}

En caso de que si tenga, usaremos la variable $window para obtener nuestros datos del item “gifWallet” usando la función localStorage.getItem()

Lo siguiente es modificar la funcionalidad “save” que nos permitirá guardar las imágenes en el localStorage:

this.save = function(image) {
    if (images == null) {
        images = [];
    }
    else {
        images = angular.fromJson(images);    
    }
    images.push(image);
    imagesString = JSON.stringify(images);
    $window.localStorage.setItem('gifWallet', imagesString);
}

En esta parte del código, pedimos como parámetro una imagen (parámetro image de la función). Preguntamos si es nulo, y si es nulo lo definimos como un array. En caso contrario, convertiremos la variable images, definida anteriormente, a un objeto JSON usando la función “angular.fromJson()“.

Luego, como es un array de objetos JSON, usaremos la función push para agregar esta imagen a la lista. Después, la convertiremos a texto (String) usando la función JSON.stringify() para poder guardarlo en el item “gifWallet” usando la función localStorage.setItem().

Finalmente, modificaremos la función list para que obtenga los datos desde localStorage:

this.list = function() {
    return angular.fromJson($window.localStorage.getItem('gifWallet'));
} 

Acá, retornaremos lo que hay en el item “gifWallet” usando localStorage.getItem() y nuevamente la función angular.fromJson() para poder convertir el contenido a un objeto JSON.

¿El resultado? Aca va.

Presionen “+”, agreguen esta URL: http://media0.giphy.com/media/13w5HmyiuaZ224/giphy.gif y actualicen la página para ver el resultado :

Si se dan cuenta, cada vez que agregamos una imagen se agrega el gif pero no podemos darle más detalles. Y por otra parte, no estamos actualizando la lista de imágenes, y tenemos que presionar F5 para ver si se agregó.

Todas estas nuevas funcionalidades las veremos en el siguiente capítulo, así que si tienen dudas de lo de ahora no duden en hacerlas acá abajo.

Tutorial de AngularJS #4

Blog, Tutorial, Tutorial Javascript
7 minutos