angularjs-directive directivas - ¿Cuál es la diferencia entre '@' y '=' en el ámbito de la directiva en AngularJS?




personalizadas ng-if (15)

He leído cuidadosamente la documentación de AngularJS sobre el tema, y ​​luego jugué con una directiva. Aquí está el fiddle .

Y aquí hay algunos fragmentos relevantes:

  • Desde el HTML:

    <pane bi-title="title" title="{{title}}">{{text}}</pane>
    
  • Desde la directiva del panel:

    scope: { biTitle: '=', title: '@', bar: '=' },
    

Hay varias cosas que no entiendo

  • ¿Por qué tengo que usar "{{title}}" con '@' y "title" con '=' ?
  • ¿También puedo acceder directamente al ámbito principal, sin decorar mi elemento con un atributo?
  • La documentación dice: "A menudo es deseable pasar datos del ámbito aislado a través de una expresión y al ámbito principal" , pero parece que también funciona bien con el enlace bidireccional. ¿Por qué sería mejor la ruta de expresión?

Encontré otro violín que también muestra la solución de expresión: http://jsfiddle.net/maxisam/QrCXh/


Answers

La propiedad @ local scope se usa para acceder a valores de cadena que están definidos fuera de la directiva.

= En los casos en que necesite crear un enlace bidireccional entre el ámbito externo y el ámbito de aislamiento de la directiva, puede usar el carácter =.

La propiedad & local scope permite al consumidor de una directiva pasar a una función que la directiva puede invocar.

Por favor, consulte el siguiente enlace que le proporciona una comprensión clara con ejemplos. Me pareció muy útil, así que pensé en compartirlo.

http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-2-isolate-scope


Incluso cuando el alcance es local, como en su ejemplo, puede acceder al alcance principal a través de la propiedad $parent . Supongamos que en el código siguiente, ese title se define en el ámbito principal. A continuación, puede acceder al título como $parent.title :

link : function(scope) { console.log(scope.$parent.title) },
template : "the parent has the title {{$parent.title}}"

Sin embargo, en la mayoría de los casos, el mismo efecto se obtiene mejor utilizando atributos.

Un ejemplo de dónde encontré la notación "&", que se usa "para pasar datos del alcance aislado a través de una expresión y al alcance principal", fue útil (y no se pudo usar un enlace de datos bidireccional) en una directiva para renderizar una estructura de datos especial dentro de una repetición ng.

<render data = "record" deleteFunction = "dataList.splice($index,1)" ng-repeat = "record in dataList" > </render>

Una parte de la representación fue un botón de eliminación y aquí fue útil adjuntar una función de eliminación desde el ámbito externo a través de &. Dentro de la directiva de render parece

scope : { data = "=", deleteFunction = "&"},
template : "... <button ng-click = "deleteFunction()"></button>"

El enlace de data = "=" bidireccional, es decir, los data = "=" no se pueden usar, ya que la función de eliminación se ejecutaría en cada ciclo de $digest , lo que no es bueno, ya que el registro se elimina inmediatamente y nunca se procesa.


Hay tres maneras en que se puede agregar el alcance en la directiva:

  1. Ámbito principal : esta es la herencia del ámbito predeterminado.

El alcance de la directiva y su principal (controlador / directiva en el que se encuentra) es el mismo. Por lo tanto, cualquier cambio realizado en las variables de alcance dentro de la directiva también se refleja en el controlador principal. No es necesario especificar esto, ya que es el valor predeterminado.

  1. Ámbito secundario: la directiva crea un ámbito secundario que se hereda del ámbito principal si especifica la variable de ámbito de la directiva como verdadera.

Aquí, si cambia las variables de alcance dentro de la directiva, no se reflejará en el alcance principal, pero si cambia la propiedad de una variable de alcance, eso se refleja en el alcance principal, ya que en realidad modificó la variable de alcance de la variable principal. .

Ejemplo,

app.directive("myDirective", function(){

    return {
        restrict: "EA",
        scope: true,
        link: function(element, scope, attrs){
            scope.somvar = "new value"; //doesnot reflect in the parent scope
            scope.someObj.someProp = "new value"; //reflects as someObj is of parent, we modified that but did not override.
        }
    };
});
  1. Ámbito aislado : se utiliza cuando desea crear el ámbito que no se hereda del ámbito del controlador.

Esto sucede cuando crea complementos, ya que esto hace que la directiva sea genérica, ya que se puede colocar en cualquier HTML y no se ve afectada por su alcance principal.

Ahora, si no desea ninguna interacción con el ámbito principal, puede especificar el ámbito como un objeto vacío. me gusta,

scope: {} //this does not interact with the parent scope in any way

En general, este no es el caso, ya que necesitamos cierta interacción con el ámbito principal, por lo que queremos que algunos de los valores / cambios se transfieran. Por esta razón, utilizamos:

1. "@"   (  Text binding / one-way binding )
2. "="   ( Direct model binding / two-way binding )
3. "&"   ( Behaviour binding / Method binding  )

@ significa que los cambios del alcance del controlador se reflejarán en el ámbito de la directiva, pero si modifica el valor en el alcance de la directiva, la variable del alcance del controlador no se verá afectada.

@ siempre espera que el atributo mapeado sea una expresión. Esto es muy importante; porque para hacer que el prefijo "@" funcione, debemos ajustar el valor del atributo dentro de {{}}.

= es bidireccional, por lo que si cambia la variable en el ámbito de la directiva, la variable de alcance del controlador también se verá afectada

& se utiliza para enlazar el método de alcance del controlador, de modo que si es necesario podemos llamarlo desde la directiva

La ventaja aquí es que el nombre de la variable no necesita ser el mismo en el ámbito del controlador y en el ámbito de la directiva.

Por ejemplo, el ámbito de la directiva tiene una variable "dirVar" que se sincroniza con la variable "contVar" del ámbito del controlador. Esto le da mucha potencia y generalización a la directiva ya que un controlador puede sincronizarse con la variable v1 mientras que otro controlador que usa la misma directiva puede pedirle a dirVar que sincronice con la variable v2.

A continuación se muestra el ejemplo de uso:

La directiva y el controlador son:

 var app = angular.module("app", []);
 app.controller("MainCtrl", function( $scope ){
    $scope.name = "Harry";
    $scope.color = "#333333";
    $scope.reverseName = function(){
     $scope.name = $scope.name.split("").reverse().join("");
    };
    $scope.randomColor = function(){
        $scope.color = '#'+Math.floor(Math.random()*16777215).toString(16);
    };
});
app.directive("myDirective", function(){
    return {
        restrict: "EA",
        scope: {
            name: "@",
            color: "=",
            reverse: "&"
        },
        link: function(element, scope, attrs){
           //do something like
           $scope.reverse(); 
          //calling the controllers function
        }
    };
});

Y el html (note la diferencia para @ y =):

<div my-directive
  class="directive"
  name="{{name}}"
  reverse="reverseName()"
  color="color" >
</div>

Aquí hay un link al blog que lo describe muy bien.


Creé un pequeño archivo HTML que contiene código angular que demuestra las diferencias entre ellos:

<!DOCTYPE html>
<html>
  <head>
    <title>Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
  </head>
  <body ng-app="myApp">
    <div ng-controller="myCtrl as VM">
      <a my-dir
        attr1="VM.sayHi('Juan')" <!-- scope: "=" -->
        attr2="VM.sayHi('Juan')" <!-- scope: "@" -->
        attr3="VM.sayHi('Juan')" <!-- scope: "&" -->
      ></a>
    </div>
    <script>
    angular.module("myApp", [])
    .controller("myCtrl", [function(){
      var vm = this;
      vm.sayHi = function(name){
        return ("Hey there, " + name);
      }
    }])
    .directive("myDir", [function(){
      return {
        scope: {
          attr1: "=",
          attr2: "@",
          attr3: "&"
        },
        link: function(scope){
          console.log(scope.attr1);   // =, logs "Hey there, Juan"
          console.log(scope.attr2);   // @, logs "VM.sayHi('Juan')"
          console.log(scope.attr3);   // &, logs "function (a){return h(c,a)}"
          console.log(scope.attr3()); // &, logs "Hey there, Juan"
        }
      }
    }]);
    </script>
  </body>
</html>

@ obtener como cadena

  • Esto no crea ningún tipo de enlaces. Simplemente estás recibiendo la palabra que pasaste como una cadena

= 2 vías

  • Los cambios realizados desde el controlador se reflejarán en la referencia contenida en la directiva, y viceversa.

& Esto se comporta de forma un poco diferente, porque el ámbito obtiene una función que devuelve el objeto que se pasó . Supongo que esto fue necesario para que funcione. El violín debe dejar esto claro.

  • Después de llamar a esta función getter, el objeto resultante se comporta de la siguiente manera:
    • si se pasó una función : entonces la función se ejecuta en el cierre principal (controlador) cuando se llama
    • si se pasó una no-función : simplemente obtenga una copia local del objeto que no tiene enlaces


Este violín debe demostrar cómo funcionan . Preste especial atención a las funciones de alcance con get... en el nombre para, con suerte, comprender mejor a qué me refiero con &


El = significa vinculación bidireccional, por lo que es una referencia a una variable para el ámbito principal. Esto significa que, cuando cambie la variable en la directiva, también se cambiará en el ámbito principal.

@ significa que la variable se copiará (clonará) en la directiva.

Por lo que sé, <pane bi-title="{{title}}" title="{{title}}">{{text}}</pane> debería funcionar. bi-title recibirá el valor de la variable del ámbito principal, que se puede cambiar en la directiva.

Si necesita cambiar varias variables en el ámbito principal, puede ejecutar una función en el ámbito principal desde dentro de la directiva (o pasar datos a través de un servicio).


Aquí hay muchas respuestas geniales, pero me gustaría ofrecer mi perspectiva sobre las diferencias entre @ , = y & binding que me resultaron útiles.

Los tres enlaces son formas de pasar datos de su ámbito principal al ámbito aislado de su directiva a través de los atributos del elemento:

  1. @ vinculante es para pasar cadenas. Estas cadenas admiten {{}} expresiones para valores interpolados. Por ejemplo: . La expresión interpolada se evalúa contra el alcance principal de la directiva.

  2. = enlace es para enlace de modelo de dos vías. El modelo en el ámbito principal está vinculado al modelo en el ámbito aislado de la directiva. Los cambios en un modelo afectan al otro, y viceversa.

  3. & binding es para pasar un método al ámbito de su directiva para que pueda ser llamado dentro de su directiva. El método está previamente vinculado al ámbito principal de la directiva y admite argumentos. Por ejemplo, si el método es hola (nombre) en el ámbito principal, para ejecutar el método desde su directiva, debe llamar a $ scope.hello ({name: 'world'})

Encuentro que es más fácil recordar estas diferencias al referirse a los enlaces de alcance mediante una descripción más corta:

  • @ Enlace de cadena de atributo
  • = Enlace de modelo de dos vías
  • & enlace de método de devolución de llamada

Los símbolos también aclaran qué representa la variable de alcance dentro de la implementación de su directiva:

  • @ cadena
  • = modelo
  • & método

En orden de utilidad (para mí de todos modos):

  1. =
  2. @
  3. Y

@ Enlace de cadena de atributo (una vía) = Enlace de modelo bidireccional y enlace de método de devolución de llamada


@ enlaza una propiedad de ámbito local / directiva al valor evaluado del atributo DOM. = une una propiedad de ámbito local / directiva a una propiedad de ámbito principal. & binding es para pasar un método al ámbito de su directiva para que pueda ser llamado dentro de su directiva.

@ Enlace de cadena de atributo = Enlace de modelo bidireccional y enlace de método de devolución de llamada


¿Por qué tengo que usar "{{title}}" con '@' y "title" con '='?

Cuando usa {{título}}, solo el valor del ámbito principal se pasará a la vista de directiva y se evaluará. Esto se limita a una forma, lo que significa que el cambio no se reflejará en el ámbito principal. Puede usar '=' cuando desee reflejar los cambios realizados en la directiva secundaria al ámbito principal también. Esto es de dos maneras.

¿También puedo acceder directamente al ámbito principal, sin decorar mi elemento con un atributo?

Cuando la directiva tiene un atributo de ámbito (alcance: {}), ya no podrá acceder directamente al ámbito principal. Pero aún es posible acceder a él a través de scope. $ Parent, etc. Si elimina el alcance de la directiva, puede acceder a él directamente.

La documentación dice: "A menudo es deseable pasar datos del ámbito aislado a través de una expresión y al ámbito principal", pero parece que también funciona bien con el enlace bidireccional. ¿Por qué sería mejor la ruta de expresión?

Depende en función del contexto. Si desea llamar a una expresión o función con datos, use & y si quiere compartir datos, puede usar la forma biderectional usando '='

Puede encontrar las diferencias entre varias formas de pasar datos a la directiva en el siguiente enlace:

AngularJS - Scopes aislados - @ vs = vs &

http://www.codeforeach.com/angularjs/angularjs-isolated-scopes-vs-vs


Implementé todas las opciones posibles en un violín.

Se ocupa de todas las opciones:

scope:{
    name:'&'
},

scope:{
    name:'='
},

scope:{
    name:'@'
},

scope:{

},

scope:true,

https://jsfiddle.net/rishulmatta/v7xf2ujm


@ y = ver otras respuestas.

Uno tiene ganas de &
TL; DR;
& obtiene la expresión (no solo la función como en los ejemplos en otras respuestas) de un padre, y la establece como una función en la directiva, que llama a la expresión. Y esta función tiene la capacidad de reemplazar cualquier variable (incluso el nombre de la función) de la expresión, al pasar un objeto con las variables.

explicado
& es una referencia de expresión, eso significa que si pasas algo como <myDirective expr="x==y"></myDirective>
en la directiva, esta expr será una función, que llama a la expresión, como:
function expr(){return x == y} .
por lo tanto, en la directiva html <button ng-click="expr()"></button> llamará la expresión. En js de la directiva, solo $scope.expr() llamará la expresión también.
La expresión se llamará con $ scope.x y $ scope.y del padre.
Tienes la capacidad de anular los parámetros!
Si los configura por llamada, por ejemplo, <button ng-click="expr({x:5})"></button>
entonces se llamará a la expresión con su parámetro x y el parámetro del padre y .
Puede anular ambos.
Ahora sabes, por qué <button ng-click="functionFromParent({x:5})"></button> funciona.
Porque solo llama a la expresión padre (por ejemplo, <myDirective functionFromParent="function1(x)"></myDirective> ) y reemplaza los valores posibles con sus parámetros especificados, en este caso x .
podría ser:
<myDirective functionFromParent="function1(x) + 5"></myDirective>
o
<myDirective functionFromParent="function1(x) + z"></myDirective>
con llamada infantil:
<button ng-click="functionFromParent({x:5, z: 4})"></button> .
o incluso con la función de reemplazo:
<button ng-click="functionFromParent({function1: myfn, x:5, z: 4})"></button> .

es solo una expresión, no importa si es una función, o muchas funciones, o solo comparación. Y puedes reemplazar cualquier variable de esta expresión.

Ejemplos:
plantilla directiva vs código llamado:
padre ha definido $ scope.x, $ scope.y:
plantilla principal: <myDirective expr="x==y"></myDirective>
<button ng-click="expr()"></button> llama a $scope.x==$scope.y
<button ng-click="expr({x: 5})"></button> llama 5 == $scope.y
<button ng-click="expr({x:5, y:6})"></button> llamadas 5 == 6

padre ha definido $ scope.function1, $ scope.x, $ scope.y:
plantilla principal: <myDirective expr="function1(x) + y"></myDirective>

<button ng-click="expr()"></button> llama a $scope.function1($scope.x) + $scope.y
<button ng-click="expr({x: 5})"></button> llama a $scope.function1(5) + $scope.y
<button ng-click="expr({x:5, y:6})"></button> llama a $scope.function1(5) + 6
La directiva tiene $ scope.myFn como función:
<button ng-click="expr({function1: myFn, x:5, y:6})"></button> llama a $scope.myFn(5) + 6


La forma = es enlace de 2 vías , que le permite tener cambios en vivo dentro de su directiva. Cuando alguien cambia esa variable fuera de la directiva, tendrá los datos modificados dentro de su directiva, pero @ way no es vinculante de dos maneras . Funciona como texto . Se unen una vez, y solo tendrá su valor.

Para obtener más claramente, puede utilizar este gran artículo:

Ámbito de aplicación de la directiva AngularJS '@' y '='


Si desea ver más sobre cómo funciona esto con un ejemplo en vivo. http://jsfiddle.net/juanmendez/k6chmnch/

var app = angular.module('app', []);
app.controller("myController", function ($scope) {
    $scope.title = "binding";
});
app.directive("jmFind", function () {
    return {
        replace: true,
        restrict: 'C',
        transclude: true,
        scope: {
            title1: "=",
            title2: "@"
        },
        template: "<div><p>{{title1}} {{title2}}</p></div>"
    };
});

AngularJS maneja el mecanismo de enlace de datos con la ayuda de tres funciones poderosas: $watch() , $digest() y $apply() . La mayoría de las veces, AngularJS llamará a $ scope. $ Watch () y $ scope. $ Digest (), pero en algunos casos es posible que tenga que llamar estas funciones manualmente para actualizar con nuevos valores.

$ reloj () : -

Esta función se usa para observar cambios en una variable en el $ scope. Acepta tres parámetros: expresión, objeto de escucha e igualdad, donde el objeto de escucha y la igualdad son parámetros opcionales.

$ digest () -

Esta función itera a través de todos los relojes en el objeto $ scope y sus objetos secundarios $ scope
(si tiene alguno). Cuando $ digest () se repite en los relojes, comprueba si el valor de la expresión ha cambiado. Si el valor ha cambiado, AngularJS llama al oyente con un nuevo valor y un valor antiguo. La función $ digest () se llama siempre que AngularJS lo considere necesario. Por ejemplo, después de hacer clic en un botón, o después de una llamada AJAX. Puede tener algunos casos en los que AngularJS no llama la función $ digest () por usted. En ese caso tienes que llamarlo tú mismo.

$ apply () -

Angular do auto-mágicamente actualiza solo aquellos cambios de modelo que están dentro del contexto de AngularJS. Cuando cambia en cualquier modelo fuera del contexto Angular (como los eventos del navegador DOM, setTimeout, XHR o bibliotecas de terceros), entonces debe informar a Angular de los cambios llamando a $ apply () manualmente. Cuando la llamada a la función $ apply () finaliza, AngularJS llama a $ digest () internamente, por lo que todos los enlaces de datos se actualizan.





angularjs angularjs-directive angularjs-scope isolated-scope