Directivas en AngularJs (I)

Las directivas en angular pueden tener varios usos, normalmente se crean cuando tienes alguna funcionalidad que quieres reutilizar varias veces y que puede funcionar independientemente de la aplicación que la consume.
Las directivas permiten a angular realizar un comportamiento específico a una parte del DOM.

Las directivas pueden ser representadas dentro de HTML como etiquetas, atributos o incluso una clase css.

<person></person>  
<div person></div>  
<div class="person"></div>  

Otro uso común de las directivas es cuando tenemos alguna funcionalidad que requiere un uso muy fuerte del DOM luego veremos el por qué cuando definamos una.

¿Qué hará la directiva?

Vamos a crear una directiva para las imágenes de los superhéroes. En conexiones muy lentas puede tardar bastante en mostrarse las imágenes ya que están en una resolución muy alta.

Crearemos una directiva que creará una variable temporal de tipo Image y en esta variable cargaremos la imagen que nos devuelve el api. Luego en el método onload que se lanza cuando la imagen ya está lista pasaremos el source de la variable temporal al elemento del HTML.

¿Por qué una directiva?

Porque siempre que necesitemos manejar directamente el DOM debemos usar una directiva.

Creando la directiva

Crear una directiva es bastante sencillo, el siguiente código muestra lo básico para crear una directiva en angular

(function () {

    'use strict';

    angular
        .module('marvelApp')
        .directive('marvelImage', marvelImageDirective);

})();

Primero tenemos que indicar a qué aplicación angular queremos agregar la directiva, en este caso lo haremos dentro de la aplicación que ya tenemos con nombre marvelApp. Ojo que podemos crear una nueva aplicación y agregarla como dependencia de nuestra aplicación tal y como hicimos cuando agregamos material design a nuestra aplicación.

Luego llamamos al método directive que acepta dos parámetros, el primero es el nombre que tendrá la directiva, es importante usar camelCase para nombrar nuestra directiva ya que eso influirá en la forma como la consumimos en nuestro documento html.
Para el ejemplo usaré el nombre marvelImage, esto quiere decir que para que angular ejecute esta directiva tendrá que ser usada de la siguiente forma en el html:

<img src="loading.gif" marvel-image="{{hero.image}}" />  

La escritura camelCase le indica a angular como buscarla en el documento html, en este caso la separación entre minúscula y mayúscula es representada dentro del html con un guión.
Si el nombre fuera marvelImageHero en el html sería representado como marvel-image-hero.

Mientras que el primer parámetro es el nombre de nuestra directiva, el segundo recibe la función que se ejecutará cuando la directiva sea encontrada por angular en el documento html.

function marvelImageDirective () {

        return {

            restrict: 'A',

            link: function (scope, element, attr) {

                // Directive code here

            }

        }

    }

La función marvelImageDirective() retorna un objeto con dos propiedades.

1. restrict: Nos permite indicar de qué tipo será la directiva, al inicio de este post indicaba que las directivas pueden ser representadas de distintas manera, como tags html, atributos html y clases css. En este caso le indicamos el valor 'A' ya que vamos a usarlo como atributo dentro de una etiqueta img.

2. link: Esta función se ejecuta cuando la directiva se ejecuta y se debe usar siempre que queremos hacer algún tipo de manipulación en el DOM. La función recibe 3 parámetros, el primero es el scope de angular, el segundo es un objeto jqLite (conocido como el jQuery ligero creado por el equipo de angular) que representa al tag img. El tercero es un objeto que tiene todos los atributos de la etiqueta img con sus respectivos valores, tal como src, width, height, etc.

Una directiva puede contener muchas más propiedades, pero eso lo dejaré para otro artículo. Siguiendo con la directiva ahora si veamos el código que nos permite mostrar un gif que luego será reemplazado con la imagen real de los superhéroes cuando ya esté totalmente cargada en memoria.

Primero debemos modificar nuestro html, nuestra etiqueta img ahora usará un gif como valor de su atributo src y la url que contiene la imagen del superhéroe la pasaremos a la directive marvel-image. Luego veremos como acceder a este valor desde el código.

<img data-ng-src="loading.gif" marvel-image={{hero.image}}>  

En el código creamos una variable de tipo Image y le asignamos la url que tiene la imagen real del superhéroe. Para obtener este valor usamos el parámetro attr y su propiedad marvelImage.

link: function (scope, element, attr) {

    var tempImage = new Image();

    tempImage.src = attr.marvelImage;

}

Ya le asignamos la imagen real a la variable temporal, ahora necesitamos copiar esa imagen a la etiqueta img, pero solo en el momento que la imagen esté completamente cargada.
Para lograr eso debemos usar el evento onload de nuestra variable Image, este evento se ejecutará una vez la imagen ya esté lista.

var tempImage = new Image();

tempImage.onload = onLoadImage;

function onLoadImage () {

    element[0].src = tempImage.src;

}

tempImage.src = attr.marvelImage;  

Dentro del evento onload solo nos queda asignar a nuestro elemento img (que tenía como source un gif) el contenido de la variable temporal que ya tiene la imagen real del superhéroe. Para eso solo necesitamos modificar la propiedad src como se ve en el código.

El código completo de la directiva quedaría así:

(function () {

    'use strict';

    angular
        .module('marvelApp')
        .directive('marvelImage', marvelImageDirective);

    function marvelImageDirective () {

        return {

            restrict: 'A',

            link: function (scope, element, attr) {

                var tempImage = new Image();

                tempImage.onload = onLoadImage;

                function onLoadImage () {

                    element[0].src = tempImage.src;

                }

                tempImage.src = attr.marvelImage;

            }

        }

    }

})();

Listo, ya tenemos nuestra primera directiva, no tiene una funcionalidad muy elaborada, pero como primer ejemplo para saber cómo crear una directiva nos sirve bastante bien. Directivas en angular es un tema bastante extenso así que en proximos artículos crearemos otro tipo de directivas.

El código completo lo pueden encontrar en mi cuenta de github

https://github.com/eperedo/marvel-app/tree/step-04

Si deseas ver este minicurso desde el inicio puedes ver todos los post en este link:

http://blog.eperedo.com/tag/minicurso-de-angularjs/

Recuerda que en abakio estamos preparando un curso para crear aplicaciones web modernas usando angularjs, aprovecha el descuento hasta el 30 de septiembre.