angular con dinosaurios 4: introducción a las directivas

Primera parte de dos sobre cómo crear directivas propias en angular js.

De Kooning

archivado en: JavaScript / 1 octubre, 2014 / taller:

En esta cuarta entrada de la serie angular con dinosaurios empezaremos a ver cómo hacer nuestras propias directivas, que es algo fundamental a la que hagamos algo con cierta complejidad, pero antes es importante recordar que los controladores solo deben utilizarse para manejar la lógica de negocio, es decir, para servir de puente, de guardia de tráfico, entre las vistas y los servicios (ie, las factorías) que hayamos asociado a los módulos. Dicho de otra forma: no debemos usar los controladores para manipular el DOM, que es una tarea que debe recaer en las directivas.

Una directiva veloz

Definir una directiva es tan sencillo como armarla en el módulo asociado a una vista:

var miModulo = angular.module('miAplicacion', []);

miModulo.directive('miDirectiva', function() {

return {

restrict: 'E',

replace: true,

template: '<p> Al velocirraptor le gustan los helados </p> '

}

});

Y luego declararla en la vista...

<section ng-app="miAplicacion">

<!-- Esto será sustituido por Al velocirraptor le gustan los helados -->

<mi-directiva></mi-directiva>

</section>

Para prevenir colisiones con angular y, en general, el html de una página, conviene que todas nuestras directivas vayan precedidas por un par de letras significativas. Por ejemplo, si estamos haciendo una aplicación sobre dioses griegos, dg. En el módulo, definimos el nombre siguiendo una sintaxis CamelCase, es decir, poniendo en mayúsculas el principio de cada palabra (dgDios, dgTemplo). Y en la vista, todo en minúscula y separado por guiones (dg-dios, dg-templo).

Podemos definir directivas que luego se usarán en la vista al ser declaradas en un elemento, en el atributo de un elemento o en la clase de un elemento (también hay otras para los comentarios, pero están desaconsejadas).

<mi-directiva></mi-directiva>

<p mi-directiva></p>

<p class="mi-directiva></p>

En función de dónde la vayamos a usar, declaramos una de estas letras en restrict:

  • A: si es un atributo
  • E: si el nombre de un tag
  • C: si el nombre de una clase

Lo más conveniente es usar E.

La segunda propiedad que he definido en la directiva es replace. Con valor true, reemplaza el tag en el que se invoca; de lo contrario, el template se habría insertado entre el par de etiquetas. Es decir, si no lo hubiera definido o lo hubiera puesto como false, el resultado en la vista habría sido:

<p class="mi-directiva>

<p> Al velocirraptor le gustan los helados </p>

</p>

Ojo, cuando se define replace como true, solo puede haber un nodo en el template. Si hay más, hay que concatenarlos como si fuera una cadena con el signo +.

Por último, en template he indicado el fragmento de html que debe insertarse cuando se renderice la directiva, no tiene mucho más misterio... de momento :P, vamos a complicarlo un poco.

El scope ataca de nuevo

Dentro del template de una directiva podemos poner expresiones entre llaves que se definan en el $scope controlador. Algo así:

miModulo.directive('directivaScope', function() {

return {

restrict: 'E',

replace: true,

template: '<p> Al {{dinosaurio}} le gusta comer {{alimento}} </p> '

}

});

y en el controlador:

miModulo.controller("miControlador", function($scope) {

$scope.dinosaurio = "estegosaurio";

$scope.alimento = "avellanas";

});

// Al estegosaurio le gusta comer avellanas

Pero, hacerlo así, en teoría, no es recomendable, ya que lo suyo es que una directiva funcione de forma autónoma, como si fuera un plugin para aclararnos. Por lo tanto, vamos a «aislar» el scope (isolate scope) de la directiva añadiendo la propiedad scope:

miModulo.directive('directivaScope', function() {

return {

restrict: 'E',

replace: true,

scope: {

dinosaurio: /* DATO */

alimento: /* DATO */

},

template: '<p> Al {{dinosaurio}} le gusta comer {{alimento}} </p> '
}

});

El scope aislado y el berenjenal de la @, el = y el &.

Tenemos tres maneras distintas de pasarle los datos a uno scope aislado, cada una de las cuales está indicada por un signo: Text binding / one-way binding ('@'),  Direct model binding / two-way binding ('=') y Behaviour binding / Method binding ('&').

Mediante la arroba (@) indicamos que el scope aislado es receptivo al $scope del que es hijo. Así, por ejemplo, si tuviéramos este scope aislado:

...

scope: {

dinosaurio: '@',

alimento: '@'

},

...

y luego en la directiva definiéramos esos atributos así:

<directiva-scope dinosaurio="estogosaurio" alimento="avellanas"></directiva-scope>

En la vista se renderizaría: Al estegosaurio le gusta comer avellanas.

(Si el nombre del atributo en el scope fuera distinto del nombre del atributo en la vista, tendríamos que referenciar este último tras la arroba: '@nombreAtributo').

También podemos definir esos atributos a partir del $scope del controlador:

miModulo.controller("miControlador", function($scope) {

$scope.dinosaurio = "tiranosaurio";

$scope.alimento = "picos";

});

...

<directiva-scope1 dinosaurio="{{dinosaurio}}" alimento="{{alimento}}"></directiva-scope1>

// Al tiranosaurio le gusta comer picos.

En este caso, lo que le pasamos al scope de la directiva, al scope aislado, es una cadena y la comunicación solo es posible desde el scope padre, el del controlador para aclararnos, hacia el scope de la directiva. Es decir, con @, no podemos cambiar el valor que tiene en el controlador. Por eso, si volvemos a incluir la expresión dinosaurio y alimento después de nuestra directiva, sigue teniendo su valor original (tiranosaurio, picos). (Ver ejemplo en plunker).

Con =, por el contrario, los cambios que hagamos en la directiva sí que afectan al controlador y, en este caso, lo que pasamos es un objeto. Así, por ejemplo, en el controlador podemos tener algo así:

$scope.otroDinosaurio = {

nombre: "Iguanodon",

alimento: 'ciruelas'

};

Esto en la directiva:

miModulo.directive('directivaScope2', function() {

return {

restrict: 'E',

replace: true,

scope: {

dinosaurio: '=',

alimento: '='

},

template: '<p> Al {{dinosaurio}} le gusta comer {{alimento}} </p> '

}

});

Y en la directiva de la vista pasamos las propiedades del objeto declarado en el controlador.

<directiva-scope2 dinosaurio="otroDinosaurio.nombre" alimento="otroDinosaurio.alimento"></directiva-scope2>

Ahora, si cambiamos el valor de este objeto en la directiva, por ejemplo, al definir el atributo, este cambio se reflejará en el controlador.

<directiva-scope2 dinosaurio="otroDinosaurio.nombre='cambiado'" alimento="otroDinosaurio.alimento"></directiva-scope2>

Es un poco lío, pero espero que con el ejemplo que puse en plunker quede un poco más claro.

Me quedaría por hablar del tercer modo de pasarle datos al scope aislado, mediante el signo &, que sirve para recabarlos de una función del scope padre, es decir, que se lanza desde un método del controlador ligado al scope padre de la vista.

Bueno, de momento lo dejo aquí, en la próxima entrada seguimos con este tema.

|| Tags:

valoración de los lectores sobre angular con dinosaurios 4: introducción a las directivas

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 5 sobre 5 (4 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.