Las directivas en AngularJS permite la manipulación y reutilización de código HTML, en este tutorial, vamos a ver una introducción a las mismas
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Tipología.
- 4. Directivas nativas.
- 5. Directivas propias.
- 6. Referencias.
- 7. Conclusiones.
1. Introducción
Siguiendo la propia documentación de AngularJS,
las directivas son marcas en los elementos del árbol DOM, en los nodos del HTML, que indican al compilador de Angular que
debe asignar cierto comportamiento a dichos elementos o transformarlos según corresponda.
Podríamos decir que las directivas nos permiten añadir comportamiento dinámico al árbol DOM haciendo uso de las
nativas del propio AngularJS o extender la funcionalidad hasta donde necesitemos creando las nuestras propias.
De hecho, la recomendación, es que sea en las directivas en el único sitio donde manipulemos el árbol DOM, para que entre
dentro del ciclo de vida de compilación, binding y renderización del HTML.
Las directivas son la técnica que nos va a permitir crear nuestros propios componentes visuales
encapsulando las posibles complejidades en la implementación, normalizando y parametrizándolos según nuestras necesidades.
En este tutorial vamos a ver qué tipos de directivas existen y unos ejemplos básicos de directivas tanto nativas como propias a modo de introducción
para que podamos hacernos una idea de para qué sirven y hasta donde podemos llegar con ellas.
2. Entorno.
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2.3 GHz Intel Core i7, 16GB DDR3).
- Sistema Operativo: Mac OS Mavericks 10.9.4
- AngularJS 1.4.0.
- Plunker
3. Tipos.
La primera tipología de directivas ya la hemos comentado: hay directivas nativas del propio Angular y cualquiera
puede crear sus propias directivas o hacer uso de directivas de terceros.
En el siguiente código podemos encontrar las siguientes directivas:
<div class="container" ng-app="myApp" ng-controller="MainCtrl"> <div class="btn-group"> <label class="btn btn-primary" ng-model="radioModel" btn-radio="'Auto'">Auto</label> <label class="btn btn-primary" ng-model="radioModel" btn-radio="'Manual'">Manual</label> </div> </div>
- ng-app (ngApp): es la directiva nativa de Angular que arranca nuestra aplicación estableciendo el elemento raíz de la misma.
- ng-controller (ngController): es la directiva nativa que asocia a una vista el controlador que mantiene la vinculación del modelo y gestiona los eventos de control de la misma.
- ng-model (ngModel): es la directiva nativa que vincula una propiedad en concreto del modelo declarado en el controlador a un componente de la vista; normalmente a un componente de formulario para obtener información.
- btn-radio (btnRadio):es una directiva de angular-bootstrap que transforma una etiqueta en un componente de tipo radio button de bootstrap
Lo normal es que las directivas que comienzan por ng* sean de Angular y cada modulo tenga su propio prefijo, pero no tiene porque, de hecho las directivas de bootstrap no lo tienen.
La segunda tipología que podemos encontrar en las directivas es su ámbito de aplicación a los nodos del árbol DOM, de modo tal que se pueden asociar:
- mediante la declaración de un atributo ‘A’ en cualquier elemento del DOM
<div customize-it></div>
- mediante la declaración de un clase css ‘C’ en cualquier atributo de tipo class de un elemento del DOM
<div class="customize-it"></div>
- mediante la asignación a un comentario ‘M’ en cualquier bloque de código comentado
<!-- directive: customize-it -->
- mediante la declaración de un elemento ‘E’ del propio árbol DOM
<customize-it></customize-it>
- también se puede combinar la declaración de un elemento con la asignación de un atributo ‘EA’ o añadirle también la declaración en un comentario ‘EAC’
Lo normal es disponer y declarar directivas a nivel de elemento y atributo.
4. Directivas nativas.
Como ya vimos en los primeros pasos con AngularSJS
disponemos de una serie de directivas nativas que nos permiten vincular el modelo de AngularJS a la vista, exponiéndolo mediante un controlador.
Se puede acceder a la documentación de las directivas de Angular para comprobar que existen las suficientes directivas
como para soportar sobradamente las necesidades de cualquier aplicación SPA y llama la atención que incluso los componentes estándar de formulario de HTML5 están recubiertos con una directiva, que permite
básicamente asociar una propiedad del modelo al componente de formulario.
Me remito en este punto a la documentación y al ejemplo planteado en el punto anterior, para centrarnos en el siguiente.
5. Directivas propias.
A la hora de crear directivas, la recomendación es que se nombren con camelCase y para hacer referencia a las mismas se separan con guiones;
aunque se podrían utilizar otras técnicas al usar guiones evitamos errores si usamos herramientas que validen nuestro HTML.
El siguiente código podemos ver cómo declarar una directiva en un módulo ya existente:
(function() { angular.module('tnt.ui.components') .directive('userInfo', [function() { return { restrict: 'E', template:'Nombre: {{user.name}}, email: <a href="mailto:{{user.email}}">{{user.email}}</a>' }; }]); }());
Para hacer uso de la misma bastaría con el siguiente código:
<div ng-app="tnt.ui.components" ng-controller="DemoDirectivesCtrl"> <user-info></user-info> </div>
La directiva da por hecho que el ámbito del controlador existe una propiedad «user» con la información a renderizar del usuario:
(function() { angular.module('tnt.ui.components').controller('DemoDirectivesCtrl', function($scope){ $scope.user = { name: 'Jose', email: 'jmsanchez@autentia.com' }; }());
A continuación veremos todos los parámetros disponibles para la declaración de una directiva y comprobaremos que
lo visto no es la manera más recomendable si queremos que nuestra directiva sea realmente reutilizable.
Aquí os dejo un plunker con el código de esta primera directiva: http://plnkr.co/WxsORtycwMlrGyFhgFju.
4.1 API.
A la hora de declarar una directiva podemos asignar las siguientes propiedades:
- restrict: para delimitar el ámbito de declaración de la directiva a un atributo, elemento, estilo o comentario. Es la tipología de directivas que vimos anteriormente y se especifica con un literal ‘A’, ‘E’, M, ‘C’ o ‘EA’.
- template: especifica el código HTML que se compilará en el ámbito de la directiva aplicando las propiedades definidas también en el ámbito de la misma
- templateUrl: la recomendación es que cuando el código de la plantilla se hace demasiado grande se externalice en un fichero html aparte al que podría apuntar la propiedad templateUrl que es como un ngInclude,
- scope: todas las directivas tienen asociado un ámbito que por defecto es el de su padre, lo normal es que sea un controlador.
Mediante este atributo que puede adoptar los siguientes valores se puede modificar:- false: es el comportamiento por defecto, hereda el ámbito y cualquier modificación tanto dentro
como fuera de la directiva de una propiedad del modelo se verá reflejada en ambos sentidos, - true: se crea un nuevo ámbito haciendo una copia del ámbito del padre de modo tal que,
al ser un nuevo ámbito las modificaciones en el modelo que se realicen dentro de la directiva no se verán
reflejadas hacia el exterior, sin embargi las modificaciones en el modelo del ámbito del padre si tendrán
reflejo en el ámbito de la directiva, - {}: se creará un ámbito nuevo independiente del padre; es el ámbito recomendado para que
realmente una directiva pueda ser reutiliazada y no dependa ni herede todo el ámbito de su padre. Si bien,
dentro de la declaración de ámbito se pueden pasar valores especificando una serie de prefijos que van a
permitir no solo pasar propiedades del modelo del ámbito del padre sino vincularlas en uno o ambos sentidos.
Estas propiedades se declaran como atributos del elemento HTML de la directiva. Así:- @: vinculación en un solo sentido, al crearse el ámbito de la directiva el valor de
esta propiedad se asigna al nuevo ámbito de tal forma que las modificaciones dentro de la directiva
no afectan al ámbito del padre pero sí a la contra. El valor de la propiedad debe ser evaluada {{}}
en la declaración del atributo. - =: vinculación en ambos sentidos, espera que el valor de la propiedad sea una
referencia al modelo, no una expresión evaluable como en el caso anterior. La vinculación se hace en
ambos sentidos de tal forma que las modificaciones tanto fuera como dentro de la directiva del modelo
afectan a ambos. - &: vinculación de métodos que permite invocar desde la directiva a métodos
declarados en el ámbito del padre
Por último, todos estos prefijos pueden ir acompañados de una redefinición del nombre de la propiedad en el
ámbito.Sobre el ejemplo inicial haremos las siguientes modificaciones en la declaración de la directiva:
(function() { angular.module('tnt.ui.components') .directive('userInfo', [function() { return { restrict: 'E', scope:{ name: '@', email: '@', rate: '=', click: '&' }, template:'
' }; }]); angular.module('tnt.ui.components').controller('DemoDirectivesCtrl', function($scope){ $scope.users = [{ name: 'Jose', email: 'jmsanchez@autentia.com', rate: '1' }, { name: 'Gustavo', email: 'gmartin@autentia.com', rate: '2' } ]; $scope.vote = function(user){ console.log(user); }; }); }());Adaptamos el código HTML a los parámetros que ahora recibe la directiva y comprobamos que ahora sí,
la directiva es totalmente reutilizable:<div ng-app="tnt.ui.components" ng-controller="DemoDirectivesCtrl"> <user-info ng-repeat="user in users" name="{{user.name}}" email="{{user.email}}" rate="user.rate" click="vote(user)"></user-info> </div>
- @: vinculación en un solo sentido, al crearse el ámbito de la directiva el valor de
- false: es el comportamiento por defecto, hereda el ámbito y cualquier modificación tanto dentro
- link: define una función con el siguiente contrato function link(scope, element, attrs) { … } recibiendo el ámbito de la directiva, el elemento del árbol DOM al que está vinculado la directiva, envuelto por un objeto de la implementación de jQuery de Angular ‘jqLite’ y un objeto con los pares de nombre y valor de los atributos de la directiva declarados en el elemento HTML.
A través del parámetro element y pudiendo acceder tanto al ámbito y, como consecuencia, el valor de las propieaddes como a los atributos que recibe, estén o no declarados en el ámbito se puede manipular el nodo HTML y todos sus hijos antes de renderizarse. - transclude: adoptando el valor a true permite crear directivas que encapsulan otras directivas o envuelven porciones de código con una envoltura permitiendo insertar el código envuelto en la plantila con la directiva <ng-transclude>
- require: permite declarar como abligatorios parámetros en la directiva que hacen referencia a otras directivas.
- controller: permite declarar un controlador a nivel de la directiva. La diferencia entre la función de link es que el código del controlador se ejecuta antes
de la compilación y el del link después. No se debería incluir manipulación de árbol DOM en un controlador que debería
reservarse para la inicialización o manipulación de las propiedades del ámbito de la directiva.
Aquí os dejo otro plunker con el ejemplo antes expuesto http://plnkr.co/edit/rR3BiWGOqh3IquWVrnDK
5. Referencias.
6. Conclusiones.
Las directivas son una parte muy interesante de Angular que nos permiten la reutilización de código.
Si los quisiéramos comparar con otras tecnologías de presentación serían como los componentes por composición de JSF2
o las plantillas de JSTL o velocity.
Idependientemente de la tecnología debemos intentar aplicar una serie de patrones que nos faciliten la
reutilización de código para no caer en el DRY, pero siempre teniendo en cuenta los principios KISS y YAGNI;
por eso la recomendación es crear directivas o componentes reutilizables a medida que vamos desarrollando y
estandarizar el desarrollo de forma gradual, elevando a nivel de arquitectura aquello que se vaya necesitando.
Un saludo.
Jose
Hola José Manuel, excelente material. Recien estoy empezando con angular.
Cuando dices que la directiva es totalmente reutilitzable no entiendo ese concepte.
Es muy sencillo we las directivas son como declarar un grupo de instrucciones que reusaras en cualquier otro lugar de tu aplicación o pagina web en el caso de Angular.
Así que no te compliques we
el scope de la directiva falla al declarar variables con mayusculas, guiones y raya al piso, hay alguna forma de diferenciar la variable y no ponerla todo en minuscula, por ejemplo:
mivariablenueva
ponerla como mi_variable_nueva o miVariableNueva . . .