En el ejemplo de directiva, acTitulo
, que acabamos de ver en el tema anterior el texto del título siempre era “Hola Mundo”, vamos a modificar la directiva para que el texto pueda ser definido por el usuario de la directiva.
La forma de modificar la directiva el simplemente la siguiente:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>" } return directiveDefinitionObject; }]);
texto
que hay en el scope
de la directiva.
Ahora ya podemos tener distintos textos y para ello solo hay que cambiar la propiedad texto
del scope
de la directiva. Pero ahora aparece el problema ya que AngularJS nos permite muchas formas de crear ese nuevo scope
, las distintas formas son:
scope
que hay en el controladorscope
pero que hereda del scope
del controlador, tal y como pasaba con la directiva 4.6 ng-ifscope
que NO hereda del controlador.
Para establecer cual de las 3 usará nuestra directiva , se usa la propiedad scope
del objeto con la definición de la directiva.
Si la propiedad scope
del objeto con la definición de la directiva vale el valor de false
, la directiva usará como scope
exactamente el mismo scope
que tiene el controlador. Este es el funcionamiento por defecto en caso de que no pongamos nada en la propiedad scope
del objeto con la definición de la directiva.
Ahora la directiva quedaría asi:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:false } return directiveDefinitionObject; }]);
scope
de la directiva es el mismo objeto que el scope
del controladorAhora ya podemos modificar el texto de la directiva únicamente poniendo :
app.controller("PruebaController", function($scope) { $scope.texto="Adios mundo"; });
scope
del controlador y el que use la directiva podría no ser ni consciente de dicha relación.
Hacer ésto sería algo así a usar variables globales dentro de una función. Siendo la variable global el scope
del controlador.
Si la propiedad scope
del objeto con la definición de la directiva vale el valor de true
, la directiva usará como scope
un nuevo scope
pero que ha heredado del scope
del controlador. Este es el caso como el que explicamos en Herencia de $scope.
Ahora la directiva quedaría asi:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:true } return directiveDefinitionObject; }]);
scope
de la directiva es un nuevo scope
pero que hereda del scope
del controladorComo en el caso anterior ya podemos modificar el texto de la directiva únicamente poniendo :
app.controller("PruebaController", function($scope) { $scope.texto="Adios mundo"; });
scope
del controlador y el que use la directiva tampoco podría no ser ni consciente de dicha relación.
Sigue siendo como usar variables globales dentro de una función. Siendo la variable global el scope
del controlador.
Como ya he dicho, las dos forma anteriores no las recomiendo ya que usamos la directiva acTitulo
de la siguiente forma:
<ac-titulo></ac-titulo>
Y entonces en la página HTML aparece el texto “Adios Mundo”, ¿De donde ha aparecido el texto ese? Parece un poco raro. La forma correcta de usar la directiva debería haber sido la siguiente:
<ac-titulo texto="Adios Mundo"></ac-titulo>
Es decir , las directivas no tienen que usar variables globales como pueda ser el scope
del controlador sino que todo lo que necesiten se debe pasar como argumentos en forma de atributos en la propia directiva. Para ello debemos crear un nuevo objeto JavaScript con { }
en la propiedad scope
del objeto con la definición de la directiva.
Ahora la directiva quedaría asi:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:{ } } return directiveDefinitionObject; }]);
scope
de la directiva es un nuevo scope
que no tiene nada que ver con el scope
del controlador.
Pero ahora nos falta decirle al controlador que queremos que el contenido del atributo texto
de la directiva sea una propiedad del scope
de la directiva. Para hacerlo hay 2 formas distintas que pasamos a ver.
Simplemente tenemos que añadir un atributo al scope
cuya valor sea una arroba ”@”. De esa forma decimos que que queremos copiar el valor del atributo en el scope
.
Ahora la directiva quedaría asi:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:{ texto:"@" } } return directiveDefinitionObject; }]);
texto:”@”
lo que le hemos dicho es que el valor del atributo texto
se copie en la propiedad texto
del scope
de la directiva.Ahora ya podemos usar la directiva así:
<ac-titulo texto="Texto definido en el atributo"></ac-titulo>
Y se generará el siguiente HTML:
<h1>Texto definido en el atributo</h1>
Pero, ¿que pasa si el texto a mostrar está en una propiedad del $scope
del controlador? Por ejemplo en la propiedad mensaje
del $scope
del controlador.
app.controller("PruebaController", function($scope) { $scope.mensaje="Texto definido en el $scope del controlador"; });
Pues no hay más que usar la directiva usando las ya conocidas llaves ”{ }” de AngularJS:
<ac-titulo texto="{{mensaje}}"></ac-titulo>
Y se generará el siguiente HTML:
<h1>Texto definido en el $scope del controlador</h1>
El enlace unidireccional que acabamos de ver al usar ”@” tiene 2 problemas:
texto
del scope
de la directiva no se cambia la propiedad mensaje
del $scope
del controlador$scope
del controlador.
Pues bien, AngularJS nos permite usar el caracter ”=” para enlazar directamente una propiedad del scope
de la directiva con una propiedad del $scope
del controlador.
Lo que queremos poder hacer es lo siguiente:
<ac-titulo texto="mensaje"></ac-titulo>Queremos que lo que ponemos en el atributo
texto
sea tratado como el nombre de una propiedad del $scope
del controlador y no como el texto realmente. Es como añadir una indirección.
Ahora la directiva quedaría asi:
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:{ texto:"=" } } return directiveDefinitionObject; }]);
texto:”=”
lo que le hemos dicho es que la propiedad texto
del scope
de la directiva sea exactamente la propiedad del $scope
del controlador especificado en el atributo texto
de la directiva. (Parece un trabalenguas).Al hace este cambio ya podemos usar la directiva de la siguiente forma:
<ac-titulo texto="mensaje"></ac-titulo>
Y si en el controlador establecemos el valor de la propiedad mensaje
:
app.controller("PruebaController", function($scope) { $scope.mensaje="Texto definido en el $scope del controlador"; });
Se generará el siguiente HTML:
<h1>Texto definido en el $scope del controlador</h1>
Pero ahora lo interesante es que si cambiáramos el valor de la propiedad texto
en el scope
de la directiva también se cambiaría en la propiedad mensaje
del $scope
del controlador.
Vamos ahora a cambiar la directiva para añadir un botón que cambie el valor del scope
de la directiva.
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<div><h1>{{texto}}</h1><button ng-click=\"texto='Texto cambiado desde dentro de la directiva'\">Cambiar valor de scope.texto de la directiva</button></div>", scope:{ texto:"=" } } return directiveDefinitionObject; }]);
texto='Texto cambiado desde dentro de la directiva
' pero como se ejecuta dentro de la directiva , lo que cambiar es la propiedad texto
del scope
de la directiva.
Si pinchamos en el botón se cambiaría también la propiedad mensaje
del $scope
del controlador. Es decir que ya tenemos el enlace bidireccional:
$scope
del controlador se modifica el scope
de la directivascope
de la directiva se modifica el $scope
del controlador.
texto:”=”
no queremos tener el texto en el $scope
del controlador sino ponerlo directamente en la directiva. AngularJS tiene la siguiente sintáxsis:
<ac-titulo texto="'Texto directamente en el atributo'"></ac-titulo>Hay que fijarse en los 2 apostrofes que hemos puesto antes y después del texto .
Ahora que hemos visto los tipos de enlacen unidireccionales y bidireccionales vamos ha hacer una comparativa entre ellos
Enlace unidireccional | Enlace bidireccional | |
---|---|---|
Carácter usado | @ | = |
Expresión para enlazar a valores literales | valor | 'valor ' 1) |
Expresión para enlazar a propiedades del $scope del controlador | {{nombrePropiedad}} | nombrePropiedad |
Si se modifica la propiedad del $scope del controlador | Si se modifica la propiedad del scope de la directiva 2) 3) | Si se modifica la propiedad del scope de la directiva |
Si se modifica la propiedad del scope de la directiva | NO se modifica la propiedad del $scope del controlador | Si se modifica la propiedad del $scope del controlador |
Ahora la pregunta que nos queda es: ¿cuando usar uno u otro?
La respuesta es sencilla, solo depende de si vamos a necesitar cambiar una propiedad del $scope
del controlador desde la directiva.
$scope
del controlador desde la directiva deberemos usar el enlace bidireccional$scope
del controlador desde la directiva deberemos usar el enlace unidireccional y asi evitamos la posibilidad de un error y que pudiéramos cambiar el $scope
del controlador de la directiva sin quererlo.
Una última característica que nos permite AngularJS al enlazar con las propiedades del scope
es hacer que tengan distinto nombre.
Imaginemos que hemos decidido que el atributo texto
de la direcctiva ahora se llame txt
.
La directiva ahora se utilizaría de la siguiente manera:
<ac-titulo txt="{{mensaje}}"></ac-titulo>
Pero hemos decidido que no queremos cambiar la propiedad del scope
de la directiva que queremos que siga siendo texto
. Pues solo tenemos que para añadir a la ”@” 4) el nombre del atributo en caso de que no esa igual que el de la propiedad del scope
.
app.directive("acTitulo",[function() { var directiveDefinitionObject ={ restrict:"E", replace : true, template:"<h1>{{texto}}</h1>", scope:{ texto:"@txt" } } return directiveDefinitionObject; }]);
scope
, le ponemos junto a la ”@” el nombre del atributo de la directiva.Y ahora ya podemos usar la directiva con el atributo “txt” en vez de “texto”.
El ejemplo consiste en hacer 2 directivas como la última que hemos usado , la del botón, pero que una de ellas tanga enlace unidireccional y la otra enlace bidireccional.
Tendremos 2 propiedades en el $scope
del controlador para en lazar en cada una de las directivas. Las propiedades son:
mensajeUnidireccional
mensajeBidireccional
Al pulsar sobre los botones “Cambiar valor de scope.texto de la directiva” se cambiarán los valores de las propiedades del scope
de las directivas y se podrá comprobar si también se cambian las propiedades del $scope
del controlador. De esa forma comprobaremos el enlace desde la directiva hacia el controlador.
Al pulsar sobre los botones “Cambiar valor del $scope.mensaje del controlador” se cambiarán los valores de las propiedades del $scope
de los controladores y se podrá comprobar si también se cambian las propiedades del scope
de las directivas. De esa forma comprobaremos el enlace desde el controlador hacia la directiva.