Directivas en AngularJS
@gruizdevilla
@adesis
Meetup AngularJS Madrid
Puedes encontrar los ejemplos de la charla en:
https://github.com/gonzaloruizdevilla/directivas
https://github.com/gonzaloruizdevilla/directivas
¿Qué es una directiva?
- Es un marcador sobre un elemento de DOM,
como un atributo, un elemento o una clase CSS. - Le indica al compilador de HTML de AngularJS
($compile) que enganche un comportamiento
específico y/o transforme al elemento
o a sus descendientes.
jQuery y AngularJS
(sobre manipulación de DOM en general)
Solo hay UN SITIO* donde es legítimo manipular directamente el DOM (con jQuery, zepto, directamente, etc.):
LAS DIRECTIVAS
Si lo haces en otro sitio, lo estás haciendo MAL.
* Y tal vez un servicio, pero solo para los que REALMENTE saben lo que están haciendo. Por ej: https://github.com/angular-ui/bootstrap/blob/master/src/modal/modal.js#L109
Un ejemplo sencillo: chispas
Mi primera directiva
.directive('chispas', function () {
return {
restrict: 'E',
link: function postLink(scope, element) {
element.text('Mi primera directiva: chispas');
}
};
});
Estilos de declaración: EACM
- E: Elemento
- A: Atributo
- C: Clase CSS
- M: Comentario
Las recomendadas son EA.
El valor por defecto es A.
El valor por defecto es A.
La función "link"
Donde registramos eventos y manipulamos el DOM.
Se ejecuta DESPUÉS de haber clonado el DOM
(una vez por instancia de la directiva)
(una vez por instancia de la directiva)
La plantilla se compila con el scope que recibe la función link
.directive('reloj', function () {
return {
template: '<div>Hora: {{hora | date: \'h:mm:ss a\'}}</div>',
restrict: 'E',
link: function postLink(scope) {
scope.hora = new Date();
}
};
});
Hay que mantener AngularJS al corriente de los cambios de scope
Mediante $apply, $digest o en el contexto de una ejecución de AngularJS
.directive('reloj2', function () {
return {
template: '<div>Hora: {{hora | date: \'h:mm:ss a\'}} <button>Actualizar</button>
</div>',
restrict: 'E',
link: function postLink(scope, element) {
scope.hora = new Date();
element.find('button').click(function (){
scope.$apply(function() {
scope.hora = new Date();
});
});
}
};
});
Mediante la propiedad templateUrl separamos html y JavaScript
.directive('reloj3', function () {
return {
templateUrl: 'templates/reloj3.html',
restrict: 'E',
link: function postLink(scope, element) {
scope.hora = new Date();
element.find('button').click(function (){
scope.$apply(function() {
scope.hora = new Date();
});
});
}
};
});
Controladores: para no reinventar la rueda
Podemos extraer parte de la lógica a un controlador tradicional de AngularJS
.controller('RuedaCtrl', function ($scope) {
$scope.rotating = false;
$scope.toggle = function () {
$scope.rotating = !$scope.rotating;
};
})
.directive('rueda', function () {
return {
templateUrl: 'templates/rueda.html',
controller: 'RuedaCtrl',
restrict: 'E',
link: function postLink() {}
};
});
Comunicándose con el exterior
La directiva puede comunicar con el exterior:
-
A través del scope
-
Mediante atributos
La segunda opción suele ser mejor, aunque siempre hay excepciones.
Usando el Scope
Como ocurre en los controladores,
el scope se copia por defecto:
el scope se copia por defecto:
- los tipos básicos por valor
- los objetos por referencia
Puede ser legítimo leer del scope del padre, pero manipular los objetos que hay en el es otro tema, pues podríamos violar el Principio de la Mínima Sorpresa.
Usando atributos.
Es la mejor manera.
Conviene empezar aislándose del scope padre
.directive('miDirectiva', function () {
return {
//...
scope: {},
//...
}
})
Al definir un
scope:{}
, la directiva tendrá un scope limpio, que no heredará las propiedades del scope del contenedor.
Tres formas de comunicación:
.directive('miDirectiva', function () {
return {
//...
scope: {
"a": "@",
"b1": "@b2",
"c": "=",
"d1": "=d2",
"e": "&",
"f1": "&f2"
},
//...
},
})
La primera forma:
@ o @attr
Pista: arroba es "at", como de atributo.
Se recibe el valor del atributo, es decir, si es de la forma:
<midirectiva miatributo='algoPasaCon{{nombre}}'/>
y en el contenedor
$scope.nombre='Mary'
entonces en la directiva, el scope local tendrá
scope.miatributo='algoPasaConMary'
La segunda forma:
= o =attr
Pista: igual. El scope local con el padre.
Crea un bind bidireccional entre la propiedad del scope
local y la que se haya referenciado en el padre.
<midirectiva miatributo='propiedadP'/>
y en el contenedor
$scope.propiedadP = 'Yo soy tu padre'
entonces en la directiva, el scope local tendrá
scope.miatributo == 'Yo soy tu padre'
//el valor de propiedadP en el scope del padre, siempre actualizado
La tercera forma:
& o &attr
Pista: & es como un puntero en C. Devuelve una función.
Devuelve una función
que ejecutará la expresión
del atributo contra el scope del padre. El scope se puede
extender en el momento de la ejecución
con valores adicionales.
del atributo contra el scope del padre. El scope se puede
extender en el momento de la ejecución
con valores adicionales.
Un ejemplo habitual es
ng-click.
"Transclusion", el palabro más raro
(Lo que viene a ser <content> en webcomponents).
Hay que activar la transclusión:
.directive('midirectiva', function () {
return {
//...
transclude: true,
//..
};
});
Y luego usar:
ng-transclude
Esta directiva marca el punto de inserción del DOM de la directiva padre más cercana que tenga activada la transclusión.
Interacción de formularios:
ngModel.ngModelController
Las ventajas de ngModel:
- $setViewValue: para informar de un nuevo valor, reaccionando
a un evento DOM, por ejemplo - $formatters y $parsers: adaptan un valor que viaja
del scope a la vista y viceversa, validándolo si es necesario. - $setValidity: permite enganchar con el sistema
común de validaciones del formulario - $validators y $asyncValidators permiten enganchar validaciones
síncronas y asíncronas
Compile vs link
- Compile: trabaja sobre el DOM original, que se clonará
para usar en link, antes de procesarlo para enganchar
los comportamientos - (Post)Link: combina las directivas con un scope y produce una vista
viva. Los cambios en el scope se reflejan en la vista
y las interacciones en la vista se reflejan en el modelo.
El uso de compile es raramente necesario.
(Mira la presentación de Angular+Famous para ver un ejemplo de compile para poder realizar tareas en los momentos prelink y postlink)
(Mira la presentación de Angular+Famous para ver un ejemplo de compile para poder realizar tareas en los momentos prelink y postlink)
Directivas y Webcomponents (PolymerJS)
- Abramos el debate :)
- Angular 2 funciona mejor, que duda cabe
Es difícil predecir hacia donde evolucionarán,
pero será apasionante.
pero será apasionante.
¡Gracias!
¿Preguntas?
¿Empezamos a picar código? :)
Directivas en AngularJS
By Gonzalo Ruiz de Villa
Directivas en AngularJS
Introducción a las directivas de AngularJS
- 40,333