В чем разница между '@' и '=' в области действия директивы в AngularJS?

Я внимательно прочитал документацию AngularJS по этой теме, а затем возился с директивой. Вот скрипка

И вот некоторые соответствующие фрагменты:

  • Из HTML:

    <pane bi-title="title" title="{{title}}">{{text}}</pane>
    
  • Из директивы панели:

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

Есть несколько вещей, которые я не понимаю:

  • Почему я должен использовать "{{title}}" с '@' а также "title" с '='?
  • Могу ли я также получить доступ к родительской области напрямую, не украшая свой элемент атрибутом?
  • Документация гласит: "Часто желательно передавать данные из изолированной области с помощью выражения и в родительскую область", но, похоже, это работает и с двунаправленной привязкой. Почему маршрут выражения будет лучше?

Я нашел другую скрипку, которая также показывает решение для выражения: http://jsfiddle.net/maxisam/QrCXh/

17 ответов

Решение

Почему я должен использовать "{{title}}" с "@" и "заголовок" с "="?

@ связывает локальную / директивную область видимости с оцененным значением атрибута DOM. Если вы используете title=title1 или же title="title1"значение атрибута DOM "title" - это просто строка title1, Если вы используете title="{{title}}"значение атрибута DOM "title" является интерполированным значением {{title}}следовательно, строка будет тем родительским свойством области видимости, которое в данный момент установлено. Поскольку значения атрибута всегда являются строками, вы всегда получите строковое значение для этого свойства в области действия директивы при использовании @.

= связывает локальную / директивную область видимости с родительской областью видимости. Таким образом, с = вы используете имя свойства родительской модели / области действия в качестве значения атрибута DOM. Вы не можете использовать {{}}с =.

С @ вы можете делать такие вещи, как title="{{title}} and then some" - {{title}} интерполируется, затем строка "и их некоторые" соединяется с ней. Последняя конкатенированная строка - это то, что получает свойство области видимости local/directive. (Вы не можете сделать это с =, только @.)

С @, вам нужно будет использовать attr.$observe('title', function(value) { ... }) если вам нужно использовать значение в вашей функции ссылки (ing). Например, if(scope.title == "...") не будет работать так, как вы ожидаете. Обратите внимание, что это означает, что вы можете получить доступ к этому атрибуту только асинхронно. Вам не нужно использовать $ наблюдаем (), если вы используете только значение в шаблоне. Например, template: '<div>{{title}}</div>',

С =, вам не нужно использовать $ наблюдение.

Могу ли я также получить доступ к родительской области напрямую, не украшая свой элемент атрибутом?

Да, но только если вы не используете изолирующую область. Удалить эту строку из вашей директивы

scope: { ... }

и тогда ваша директива не создаст новую область. Он будет использовать родительскую область. Затем вы можете получить доступ ко всем свойствам родительской области напрямую.

Документация гласит: "Часто желательно передавать данные из изолированной области через выражение и в родительскую область", но, похоже, это работает и с двунаправленной привязкой. Почему маршрут выражения будет лучше?

Да, двунаправленное связывание позволяет локальной / директивной области и родительской области совместно использовать данные. "Привязка выражений" позволяет директиве вызывать выражение (или функцию), определенную атрибутом DOM, и вы также можете передавать данные в качестве аргументов в выражение или функцию. Итак, если вам не нужно обмениваться данными с родителем - вы просто хотите вызвать функцию, определенную в родительской области, - вы можете использовать синтаксис &.

Смотрите также

Здесь есть много отличных ответов, но я хотел бы предложить свою точку зрения на различия между @, =, а также & переплет, который оказался полезным для меня.

Все три привязки - это способы передачи данных из родительской области видимости в изолированную область действия твоей директивы через атрибуты элемента:

  1. @ привязка для передачи строк. Эти строки поддерживают {{}} выражения для интерполированных значений. Например: . Интерполированное выражение оценивается по родительской области действия директивы.

  2. = связывание для двусторонней привязки модели. Модель в родительской области видимости связана с моделью в изолированной области действия директивы. Изменения в одной модели влияют на другую, и наоборот.

  3. & binding предназначен для передачи метода в область действия вашей директивы, чтобы его можно было вызывать в вашей директиве. Метод предварительно привязан к родительской области действия директивы и поддерживает аргументы. Например, если метод является hello (name) в родительской области, то для выполнения метода изнутри вашей директивы вы должны вызвать $scope.hello({name:'world'})

Я считаю, что легче запомнить эти различия, ссылаясь на привязки области видимости в более коротком описании:

  • @ Привязка строки атрибута
  • = Двухстороннее связывание моделей
  • & Привязка метода обратного вызова

Символы также проясняют, что представляет переменная области видимости внутри реализации вашей директивы:

  • @ строка
  • = модель
  • & метод

В порядке полезности (для меня в любом случае):

  1. знак равно
  2. @
  3. &

= означает двунаправленное связывание, поэтому ссылка на переменную в родительской области видимости. Это означает, что когда вы изменяете переменную в директиве, она будет изменяться и в родительской области видимости.

@ означает, что переменная будет скопирована (клонирована) в директиву.

Насколько я знаю, <pane bi-title="{{title}}" title="{{title}}">{{text}}</pane> должно работать тоже. bi-title получит значение родительской переменной области видимости, которое можно изменить в директиве.

Если вам нужно изменить несколько переменных в родительской области, вы можете выполнить функцию в родительской области из директивы (или передать данные через службу).

Если вы хотите увидеть больше, как это работает, на живом примере. 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>"
    };
});

@ получить как строку

  • Это не создает никаких привязок вообще. Вы просто получаете слово, которое вы передали в виде строки

= 2 способа привязки

  • Изменения, сделанные в контроллере, будут отражены в справочной информации, содержащейся в директиве, и наоборот

& Это ведет себя немного по-другому, потому что область действия получает функцию, которая возвращает переданный объект. Я предполагаю, что это было необходимо, чтобы заставить это работать. Скрипка должна прояснить это.

  • После вызова этой функции получения результирующий объект ведет себя следующим образом:
    • если функция была передана: то функция выполняется в закрытии родителя (контроллера) при вызове
    • если была передана не-функция: просто получите локальную копию объекта, который не имеет привязок


Эта скрипка должна продемонстрировать, как они работают. Обратите особое внимание на функции области с get... во имя, надеюсь, лучше понять, что я имею в виду &

Существует три способа добавления области действия в директиву:

  1. Родительская область: это наследование области по умолчанию.

Директива и ее родительский (контроллер / директива, в которой она находится) контекст одинаковы. Таким образом, любые изменения, внесенные в переменные области видимости внутри директивы, также отражаются в родительском контроллере. Вам не нужно указывать это, так как это по умолчанию.

  1. Дочерняя область: директива создает дочернюю область, которая наследуется от родительской области, если вы задаете переменную области действия директивы как true.

Здесь, если вы измените переменные области действия внутри директивы, она не будет отражаться в родительской области, но если вы измените свойство переменной области, которое отражено в родительской области, так как вы фактически изменили переменную области родительского элемента,

Пример,

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. Изолированная область: используется, если вы хотите создать область, которая не наследуется от области контроллера.

Это происходит, когда вы создаете плагины, так как это делает директиву общей, так как она может быть помещена в любой HTML и не зависит от родительской области видимости.

Теперь, если вы не хотите никакого взаимодействия с родительской областью, то вы можете просто указать область как пустой объект. лайк,

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

В большинстве случаев это не так, поскольку нам нужно некоторое взаимодействие с родительской областью, поэтому мы хотим, чтобы некоторые значения / изменения были пройдены. По этой причине мы используем:

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

@ означает, что изменения в области действия контроллера будут отражены в области действия директивы, но если вы измените значение в области действия директивы, переменная области действия контроллера не будет затронута.

@ всегда ожидает, что сопоставленный атрибут будет выражением. Это очень важно; потому что для того, чтобы префикс "@" работал, нам нужно обернуть значение атрибута внутри {{}}.

= является двунаправленным, поэтому, если вы измените переменную в области действия директивы, это также повлияет на переменную области контроллера

& используется для привязки метода контекста контроллера, чтобы при необходимости мы могли вызывать его из директивы

Преимущество здесь в том, что имя переменной не обязательно должно совпадать в области видимости контроллера и области действия директивы.

Например, область действия директивы имеет переменную "dirVar", которая синхронизируется с переменной "contVar" области контроллера. Это дает большую мощность и обобщение директиве, поскольку один контроллер может синхронизироваться с переменной v1, в то время как другой контроллер, использующий ту же директиву, может запрашивать синхронизацию dirVar с переменной v2.

Ниже приведен пример использования:

Директива и контроллер:

 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
        }
    };
});

И HTML (обратите внимание на разницу для @ и =):

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

Вот ссылка на блог, который описывает это красиво.

Просто мы можем использовать: -

  1. @: - для строковых значений для односторонней привязки данных. с одной стороны, привязка данных вы можете только передать значение области в директиву

  2. =: - для значения объекта для двухсторонней привязки данных. При двухстороннем связывании данных вы можете изменить значение области действия как в директиве, так и в html.

  3. &: - для методов и функций.

РЕДАКТИРОВАТЬ

В нашем определении компонентов для угловой версии 1.5 и выше
Есть четыре разных типа привязок:

  1. = Двусторонняя привязка данных: - если мы изменим значение, оно автоматически обновится
  2. < односторонняя привязка: - когда мы просто хотим прочитать параметр из родительской области, а не обновлять его.

  3. @ это для строковых параметров

  4. & это для обратных вызовов в случае, если ваш компонент должен вывести что-то в родительскую область видимости

Я создал небольшой HTML-файл, содержащий код Angular, демонстрирующий различия между ними:

<!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>

Этот вопрос уже забит до смерти, но я все равно поделюсь этим на случай, если кто-то еще будет бороться с ужасным беспорядком, который охватывает AngularJS. Это покроет =, <, @, & а также ::, Полное описание можно найти здесь.


= устанавливает двухстороннее связывание. Изменение свойства в родительском элементе приведет к изменению дочернего элемента, и наоборот.


< устанавливает одностороннюю привязку, родитель к ребенку. Изменение свойства в родительском элементе приведет к изменению дочернего, но изменение дочернего свойства не повлияет на родительское свойство.


@ присваивает дочернему свойству строковое значение атрибута тега. Если атрибут содержит выражение, дочернее свойство обновляется всякий раз, когда выражение оценивается в другую строку. Например:

<child-component description="The movie title is {{$ctrl.movie.title}}" />
bindings: {
    description: '@', 
}

Здесь description свойство в дочерней области будет текущим значением выражения "The movie title is {{$ctrl.movie.title}}", где movie является объектом в родительской области видимости


& немного сложнее, и на самом деле, кажется, нет веской причины когда-либо использовать его. Это позволяет оценивать выражение в родительской области, заменяя параметры переменными из дочерней области. Пример ( plunk):

<child-component 
  foo = "myVar + $ctrl.parentVar + myOtherVar"
</child-component>
angular.module('heroApp').component('childComponent', {
  template: "<div>{{  $ctrl.parentFoo({myVar:5, myOtherVar:'xyz'})  }}</div>",
  bindings: {
    parentFoo: '&foo'
  }
});

Дано parentVar=10, выражение parentFoo({myVar:5, myOtherVar:'xyz'}) будет оценивать 5 + 10 + 'xyz' и компонент будет отображаться как:

<div>15xyz</div>

Когда бы вы хотели использовать этот замысловатый функционал? & часто используется людьми для передачи в дочернюю область функции обратного вызова в родительской области. В действительности, однако, тот же эффект может быть достигнут при использовании "<" для передачи функции, которая является более простой и позволяет избежать неудобного синтаксиса фигурных скобок для передачи параметров ({myVar:5, myOtherVar:'xyz'}). Рассматривать:

Обратный звонок с использованием &:

<child-component parent-foo="$ctrl.foo(bar)"/>
angular.module('heroApp').component('childComponent', {
  template: '<button ng-click="$ctrl.parentFoo({bar:'xyz'})">Call foo in parent</button>',
  bindings: {
    parentFoo: '&'
  }
});

Обратный звонок с использованием <:

<child-component parent-foo="$ctrl.foo"/>
angular.module('heroApp').component('childComponent', {
  template: '<button ng-click="$ctrl.parentFoo('xyz')">Call foo in parent</button>',
  bindings: {
    parentFoo: '<'
  }
});

Обратите внимание, что объекты (и массивы) передаются по ссылке на дочернюю область, а не копируются. Это означает, что даже если это односторонняя привязка, вы работаете с одним и тем же объектом как в родительской, так и в дочерней области.


Чтобы увидеть разные префиксы в действии, откройте этот план.

Одноразовая привязка (инициализация) с использованием ::

[Официальные документы]
В более поздних версиях AngularJS появилась возможность иметь одноразовую привязку, когда свойство дочерней области обновляется только один раз. Это повышает производительность, устраняя необходимость следить за родительским свойством. Синтаксис отличается от выше; чтобы объявить одноразовую привязку, вы добавляете :: перед выражением в теге компонента:

<child-component 
  tagline = "::$ctrl.tagline">
</child-component>

Это будет распространять значение tagline для дочерней области без установления односторонней или двусторонней привязки. Примечание: если tagline изначально undefined в родительской области видимость angular будет наблюдать до тех пор, пока она не изменится, а затем выполнить однократное обновление соответствующего свойства в дочерней области.

Резюме

В таблице ниже показано, как работают префиксы в зависимости от того, является ли свойство объектом, массивом, строкой и т. Д.

Как работают различные привязки изоляторов

= Way - это двухстороннее связывание, которое позволяет вам вносить изменения в директиву. Когда кто-то изменяет эту переменную вне директивы, у вас будут эти измененные данные внутри вашей директивы, но @ way не является двухсторонней привязкой. Это работает как текст. Вы связываете один раз, и у вас будет только его значение.

Чтобы получить более четкое представление, вы можете использовать эту замечательную статью:

Область Директив AngularJS '@' и '='

@ свойство local scope используется для доступа к строковым значениям, которые определены вне директивы.

= В случаях, когда вам нужно создать двустороннюю привязку между внешней областью действия и изолированной областью директивы, вы можете использовать символ =.

Свойство& local scope позволяет потребителю директивы передавать функцию, которую директива может вызывать.

Пожалуйста, проверьте ссылку ниже, которая дает вам четкое понимание с примерами. Я нашел это действительно очень полезным, поэтому подумал поделиться им.

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

Даже если область является локальной, как в вашем примере, вы можете получить доступ к родительской области через свойство $parent, Предположим в приведенном ниже коде, что title определяется в родительской области. Вы можете получить доступ к заголовку как $parent.title:

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

Однако в большинстве случаев тот же эффект лучше получить с помощью атрибутов.

Пример, где я нашел нотацию "&", которая используется "для передачи данных из изолированной области через выражение и в родительскую область", полезная (и двусторонняя привязка данных не может быть использована) была в директиве для рендеринга специальной структуры данных внутри ng-повторения.

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

Одной частью рендеринга была кнопка удаления, и здесь было полезно прикрепить функцию удаления из внешней области через &. Внутри директивы рендера это выглядит так

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

Двусторонняя привязка данных, т.е. data = "=" не может быть использован, так как функция удаления будет работать на каждом $digest цикл, который не является хорошим, поскольку запись затем немедленно удаляется и никогда не отображается.

Я реализовал все возможные варианты в скрипке.

Здесь рассматриваются все варианты:

scope:{
    name:'&'
},

scope:{
    name:'='
},

scope:{
    name:'@'
},

scope:{

},

scope:true,

https://jsfiddle.net/rishulmatta/v7xf2ujm

Главное отличие между ними просто

@ Attribute string binding
= Two-way model binding
& Callback method binding

@ а также = увидеть другие ответы.

Один вопрос о &
TL;DR;
& получает выражение (не только функцию, как в примерах в других ответах) от родителя, и устанавливает его как функцию в директиве, которая вызывает выражение. И эта функция имеет возможность заменить любую переменную (даже имя функции) выражения, передавая объект с переменными.

объяснил
& является ссылкой на выражение, это означает, что если вы передаете что-то вроде <myDirective expr="x==y"></myDirective>
в директиве это expr будет функция, которая вызывает выражение, например:
function expr(){return x == y},
так в директиве HTML <button ng-click="expr()"></button> вызовет выражение. В JS директивы просто $scope.expr() вызовет выражение тоже.
Выражение будет вызываться с помощью $scope.x и $scope.y родителя.
У вас есть возможность переопределить параметры!
Если вы установите их по вызову, например, <button ng-click="expr({x:5})"></button>
тогда выражение будет вызываться с вашим параметром x и родительский параметр y,
Вы можете переопределить оба.
Теперь вы знаете, почему <button ng-click="functionFromParent({x:5})"></button> работает.
Потому что это просто вызывает выражение родителя (например, <myDirective functionFromParent="function1(x)"></myDirective>) и заменяет возможные значения указанными вами параметрами, в этом случае x,
возможно:
<myDirective functionFromParent="function1(x) + 5"></myDirective>
или же
<myDirective functionFromParent="function1(x) + z"></myDirective>
с детским звонком:
<button ng-click="functionFromParent({x:5, z: 4})"></button>,
или даже с заменой функции:
<button ng-click="functionFromParent({function1: myfn, x:5, z: 4})"></button>,

это просто выражение, не имеет значения, является ли это функцией, множеством функций или просто сравнением. И вы можете заменить любую переменную этого выражения.

Примеры:
шаблон директивы против вызываемого кода:
parent определил $scope.x, $scope.y:
родительский шаблон: <myDirective expr="x==y"></myDirective>
<button ng-click="expr()"></button> звонки $scope.x==$scope.y
<button ng-click="expr({x: 5})"></button> звонки 5 == $scope.y
<button ng-click="expr({x:5, y:6})"></button> звонки 5 == 6

parent определил $scope.function1, $scope.x, $scope.y:
родительский шаблон: <myDirective expr="function1(x) + y"></myDirective>

<button ng-click="expr()"></button> звонки $scope.function1($scope.x) + $scope.y
<button ng-click="expr({x: 5})"></button> звонки $scope.function1(5) + $scope.y
<button ng-click="expr({x:5, y:6})"></button> звонки $scope.function1(5) + 6
директива имеет $scope.myFn в качестве функции:
<button ng-click="expr({function1: myFn, x:5, y:6})"></button> звонки $scope.myFn(5) + 6

Почему я должен использовать "{{title}}" с "@" и "заголовок" с "="?

Когда вы используете {{title}}, только родительское значение области будет передано в представление директивы и оценено. Это ограничено одним способом, означающим, что изменение не будет отражено в родительской области. Вы можете использовать '=', когда хотите отразить изменения, сделанные в дочерней директиве, в родительской области видимости. Это два пути.

Могу ли я также получить доступ к родительской области напрямую, не украшая свой элемент атрибутом?

Когда в директиве есть атрибут scope ( scope: {}), вы больше не сможете напрямую обращаться к родительской области. Но все же возможно получить к нему доступ через scope.$ Parent и т. Д. Если вы удалите scope из директивы, к ней можно получить прямой доступ.

Документация гласит: "Часто желательно передавать данные из изолированной области через выражение и в родительскую область", но, похоже, это работает и с двунаправленной привязкой. Почему маршрут выражения будет лучше?

Это зависит от контекста. Если вы хотите вызвать выражение или функцию с данными, вы используете &, и если вы хотите поделиться данными, вы можете использовать двунаправленный способ, используя '='

Вы можете найти различия между несколькими способами передачи данных в директиву по ссылке ниже:

AngularJS - изолированные области действия - @ vs = vs &

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

@ Привязка строки атрибута (односторонняя) = двусторонняя привязка модели и привязка метода обратного вызова

@ связывает локальную / директивную область видимости с оцененным значением атрибута DOM. = связывает локальную / директивную область видимости с родительской областью видимости. & binding предназначен для передачи метода в область действия вашей директивы, чтобы его можно было вызывать в вашей директиве.

@ Привязка строки атрибута = двусторонняя привязка модели и привязка метода обратного вызова

Другие вопросы по тегам