¡Esta es una revisión vieja del documento!
Tabla de Contenidos
9.2 scope
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;
}]);
- Línea 6: El texto a mostrar ahora se obtiene de la propiedad
textoque hay en elscopede 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:
- Tener exactamente el mismo
scopeque hay en el controlador - Tener un nuevo
scopepero que hereda delscopedel controlador, tal y como pasaba con la directiva 4.6 ng-if - Tener un nuevo
scopeque 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.
Mismo scope del controlador
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;
}]);
- Línea 7: Indicamos que el
scopede la directiva es el mismo objeto que elscopedel controlador
Ahora 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.
Nuevo scope pero que hereda del 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;
}]);
- Línea 7: Indicamos que el
scopede la directiva es un nuevoscopepero que hereda delscopedel controlador
Como 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.
Nuevo scope
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;
}]);
- Línea 7: Indicamos que el
scopede la directiva es un nuevoscopeque no tiene nada que ver con elscopedel 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.
Enlace unidireccional
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;
}]);
- Línea 8 : Al poner
texto:“@”lo que le hemos dicho es que el valor del atributotextose copie en la propiedadtextodelscopede 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>
Enlace bidireccional
El enlace unidireccional que acabamos de ver al usar “@” tiene 2 problemas:
- Es unidireccional como su nombre indica, si cambiamos el valor de la propiedad
textodelscopede la directiva no se cambia la propiedadmensajedel$scopedel controlador - Tener que usar “{ }” puede resultar un poco raro si lo que queremos es siempre enlazar a una propiedad del
$scopedel 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;
}]);
- Línea 8 : Al poner
texto:“=”lo que le hemos dicho es que la propiedadtextodelscopede la directiva sea exactamente la propiedad del$scopedel controlador especificado en el atributotextode 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;
}]);
- Ahora la directiva incluye un botón que al pincharlo hace lo siguiente:
texto='Texto cambiado desde dentro de la directiva' pero como se ejecuta dentro de la directiva , lo que cambiar es la propiedadtextodelscopede 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:
- Si hay un cambio en el
$scopedel controlador se modifica elscopede la directiva - Si hay un cambio en el
scopede la directiva se modifica el$scopedel 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 .
Enlace unidireccional vs bidireccional
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) | Si se modifica la propiedad del scope de la directiva |
Si se modifica la propiedad del scope de la propiedad | 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?
Ejemplo
