AngularJS中指令范围内的'@'和'='有什么区别?

时间:2012-12-27 06:10:29

标签: angularjs angularjs-directive angularjs-scope isolated-scope

我仔细阅读了关于该主题的AngularJS文档,然后使用指令进行了调整。这是fiddle

以下是一些相关的片段:

  • 来自HTML:

    <pane bi-title="title" title="{{title}}">{{text}}</pane>
    
  • 从窗格指令:

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

我有几件事没有得到:

  • 为什么我必须将"{{title}}"'@'"title"'='一起使用?
  • 我是否可以直接访问父作用域,而无需使用属性装饰我的元素?
  • 文档说“通常需要通过表达式将数据从隔离范围传递到父范围”,但这似乎也适用于双向绑定。为什么表达路线会更好?

我找到了另一个显示表达式解决方案的小提琴:http://jsfiddle.net/maxisam/QrCXh/

17 个答案:

答案 0 :(得分:1134)

  

为什么我必须将“{{title}}”与“ @ ”一起使用,将“title”与“ = ”一起使用?

@ 将本地/指令范围属性绑定到已评估的DOM属性值。如果您使用title=title1title="title1",则DOM属性“title”的值只是字符串title1。如果使用title="{{title}}",则DOM属性“title”的值是{{title}}的内插值,因此该字符串将是父范围属性“title”当前设置的任何值。由于属性值始终是字符串,因此在使用 @ 时,您将始终在指令范围内以此属性的字符串值结束。

= 将本地/指令范围属性绑定到父范围属性。因此,使用 = ,可以使用父模型/范围属性名称作为DOM属性的值。您不能将{{}} = 一起使用。

使用@,您可以执行内插title="{{title}} and then some" - {{title}}等内容,然后将字符串“和它们一些”连接起来。最终的连接字符串是本地/指令范围属性获取的内容。 (你不能用 = ,只有 @ 这样做。)

使用 @ ,如果您需要使用链接(ing)功能中的值,则需要使用attr.$observe('title', function(value) { ... })。例如,if(scope.title == "...")将无法像您期望的那样发挥作用。请注意,这意味着您只能访问此属性asynchronously。 如果仅使用模板中的值,则不需要使用$ observe()。例如,template: '<div>{{title}}</div>'

使用 = ,您无需使用$ observe。

  

我是否可以直接访问父作用域,而无需使用属性装饰我的元素?

是的,但仅限于您不使用隔离范围。从指令中删除此行

scope: { ... }

然后你的指令不会创建新范围。它将使用父范围。然后,您可以直接访问所有父作用域属性。

  

文档说“通常需要通过表达式将数据从隔离范围传递到父范围”,但这似乎也适用于双向绑定。为什么表达路线会更好?

是的,双向绑定允许本地/指令范围和父范围共享数据。 “表达式绑定”允许指令调用由DOM属性定义的表达式(或函数) - 您还可以将数据作为参数传递给表达式或函数。因此,如果您不需要与父级共享数据 - 您只想调用父作用域中定义的函数 - 您可以使用&amp; 语法。

另见

答案 1 :(得分:532)

这里有很多很棒的答案,但我想提一下我对@=&绑定之间的差异的看法,这对我来说非常有用。< / p>

所有三种绑定都是通过元素的属性将数据从父作用域传递到指令的独立作用域的方法:

  
      
  1. @ 绑定用于传递字符串。      这些字符串支持插值的{{}}表达式。      例如:      。针对插值表达式进行评估      指令的父范围。

  2.   
  3. = 绑定用于双向模型绑定。父范围中的模型      链接到指令的隔离范围中的模型。改变为      一个模型影响另一个模型,反之亦然。

  4.   
  5. &amp; 绑定用于将方法传递到指令的范围,以便      它可以在你的指令中调用。该方法是预先绑定的      指令的父作用域,并支持参数。例如,如果方法在父作用域中是hello(name),则在      为了从你的指令中执行方法,你必须这样做      致电$ scope.hello({name:'world'})

  6.   

我发现通过较短的描述引用范围绑定更容易记住这些差异:

  • @ 属性字符串绑定
  • = 双向模型绑定
  • & 回调方法绑定

这些符号还可以更清楚地说明范围变量在指令实现中的含义:

  • @ string
  • = 模型
  • & 方法

为了有用性(反正对我而言):

  1. =
  2. @
  3. &安培;

答案 2 :(得分:63)

=表示双向绑定,因此对父作用域的变量的引用。这意味着,当您更改指令中的变量时,它也将在父作用域中更改。

@表示该变量将被复制(克隆)到指令中。

据我所知,<pane bi-title="{{title}}" title="{{title}}">{{text}}</pane>也应该有用。 bi-title将收到父范围变量值,该值可在指令中更改。

如果需要更改父作用域中的多个变量,可以在指令中对父作用域执行一个函数(或通过服务传递数据)。

答案 3 :(得分:39)

如果您想通过实例了解更多这方面的工作原理。 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>"
    };
});

答案 4 :(得分:37)

@ 获取字符串

  • 这不会创建任何绑定。你只是把你传入的单词作为字符串

= 双向绑定

  • 从控制器进行的更改将反映在指令持有的引用中,反之亦然

&这种行为略有不同,因为范围得到一个返回传入的对象的函数。我认为这是必要的,以使其工作。 小提琴应该清楚明白。

  • 调用此getter函数后,生成的对象的行为如下:
    • 如果传递函数:则在调用时在父(控制器)闭包中执行该函数
    • 如果传入非功能:只需获取没有绑定的对象的本地副本


This fiddle should demonstrate how they work。请特别注意名称中get...的范围函数,以便更好地理解我对&

的含义

答案 5 :(得分:33)

指令中可以添加三种范围:

  1. 父作用域:这是默认作用域继承。
  2. 该指令及其父级(它所在的控制器/指令)范围是相同的。 因此,对指令中的范围变量所做的任何更改也会反映在父控制器中。您不需要指定它,因为它是默认值。

    1. 子范围:指令创建一个子范围,如果将指令的范围变量指定为true,则该范围将从父范围继承。
    2. 这里,如果你更改了指令中的范围变量,它就不会反映在父范围内,但是如果你改变范围变量的属性,那就反映在父范围内,因为你实际修改了父母的范围变量。

      实施例,

      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. 隔离范围:当您要创建不从控制器范围继承的范围时,可以使用此选项。
      2. 当您创建插件时会发生这种情况,因为这会使指令变为通用,因为它可以放在任何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  )
        

        @ 表示来自控制器范围的更改将反映在指令范围中,但如果修改指令范围中的值,则控制器范围变量不会受到影响。

        @始终要求mapped属性为表达式。这是非常重要的;因为要使“@”前缀起作用,我们需要将属性值包装在{{}}中。

        = 是双向的,因此如果更改指令范围内的变量,控制器范围变量也会受到影响

        &amp; 用于绑定控制器范围方法,以便在需要时可以从指令中调用它

        这里的优点是变量的名称在控制器范围和指令范围内不必相同。

        例如,指令范围有一个变量&#34; dirVar&#34;与变量&#34; contVar&#34;同步控制器范围。这为指令提供了很多功能和概括,因为一个控制器可以与变量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>
        

        以下是博客的link,它很好地描述了它。

答案 6 :(得分:20)

我们可以使用: -

  1. @ : - 用于单向数据绑定的字符串值。在某种程度上,数据绑定只能将范围值传递给指令

  2. = : - 用于双向数据绑定的对象值。在双向数据绑定中,您可以更改指令中的范围值以及html中的范围值。

  3. &amp; : - 用于方法和功能。

  4. 修改

    Angular版本1.5的组件定义以及上面的 有四种不同类型的绑定:

    1. = 双向数据绑定: - 如果我们更改了值,则会自动更新
    2. < 单向绑定: - 当我们只想从父作用域读取参数而不更新它时。

    3. @这适用于字符串参数

    4. &这是针对回调,以防您的组件需要将内容输出到其父作用域

答案 7 :(得分:11)

我创建了一个包含Angular代码的HTML文件,演示了它们之间的差异:

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

答案 8 :(得分:6)

= 方式为双向绑定,可让您在指令中进行实时更改。当有人将该变量更改为指令时,您将在指令中更改数据,但 @ 方式不是双向绑定。它的工作方式类似于文字。你绑定一次,你将只有它的价值。

为了更清楚地了解它,你可以使用这篇伟大的文章:

AngularJS Directive Scope '@' and '='

答案 9 :(得分:3)

@ 本地范围属性用于访问在指令外定义的字符串值。

= 如果您需要在外部作用域和指令的隔离范围之间创建双向绑定,则可以使用=字符。

&amp; local scope属性允许指令的使用者传入指令可以调用的函数。

请查看以下链接,通过示例清楚地了解。我发现这非常有用,所以想分享它。

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

答案 10 :(得分:3)

即使作用域是本地的,如您的示例所示,您也可以通过属性$parent访问父作用域。假设在下面的代码中,title是在父作用域上定义的。然后,您可以访问标题$parent.title

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

然而,在大多数情况下,使用属性可以更好地获得相同的效果。

我找到“&amp;”的一个例子用于“通过表达式将数据传递到隔离范围并传递到父范围”的符号(用于(并且不能使用双向数据绑定))用于在ng-repeat内部呈现特殊数据结构的指令

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

渲染的一部分是删除按钮,这里通过&amp;附加来自外部范围的删除功能很有用。在render-directive中,它看起来像

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

双向数据绑定即data = "="不能用,因为删除函数会在每个$digest周期运行,这是不好的,因为记录会立即删除并且永远不会呈现。

答案 11 :(得分:2)

它们之间的主要区别只是

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

答案 12 :(得分:2)

这个问题已经被打死了,但是无论如何我还是会分享这个问题,以防其他人在AngularJS范围的可怕混乱中挣扎。这将覆盖=<@&::。完整的文章可以在here中找到。


=建立双向绑定。更改父级中的属性将导致子级中的更改,反之亦然。


<建立了单向绑定,即父子绑定。更改父级属性会导致子级更改,但更改子级属性不会影响父级属性。


@会将tag属性的字符串值分配给child属性。如果属性包含expression,则子表达式在表达式计算为其他字符串时更新。例如:

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

请注意,对象(和数组)是通过引用传递给子作用域的,而不是被复制的。这意味着即使是单向绑定,您也要使用同一对象在父级和子级范围内。


要查看实际使用的不同前缀,请打开此plunk

使用::进行一次绑定(初始化)

[Official docs]
更高版本的AngularJS引入了具有一次性绑定的选项,其中子范围属性仅更新一次。通过消除监视父属性的需求,可以提高性能。语法与上面不同。要声明一次绑定,请在 component标签添加一个::

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

这会将tagline的值传播到子作用域,而不建立单向或双向绑定。 注意:如果tagline在父作用域中最初是undefined,则angular会一直监视它直到更改为止,然后对子作用域中的相应属性进行一次性更新。

摘要

下表显示了前缀如何工作,具体取决于属性是对象,数组,字符串等。

How the various isolate scope bindings work

答案 13 :(得分:1)

@=查看其他答案。

关于 & 的一个问题 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

答案 14 :(得分:0)

  

为什么我必须使用&#34; {{title}}&#34;与&#39; @&#39;和&#34;标题&#34;用&#39; =&#39;?

使用{{title}}时,只有父范围值将传递给指令视图并进行评估。这仅限于一种方式,这意味着更改不会反映在父范围中。您可以使用&#39; =&#39;当您想要将子指令中完成的更改反映到父作用域时。这是双向的。

  

我是否也可以直接访问父作用域,而无需装饰我的   具有属性的元素?

当指令中包含范围属性(范围:{})时,您将无法再直接访问父范围。但仍然可以通过范围访问它。$ parent等。如果从指令中删除范围,可以直接访问它。

  

文档说&#34;通常需要从中传递数据   隔离范围通过表达式和父范围&#34;,但那   似乎也可以使用双向绑定。为什么会这样   表达路线会更好吗?

这取决于具体情况。如果要使用数据调用表达式或函数,可以使用&amp;如果您想要共享数据,可以使用&#39; =&#39;

使用双向检索方式

您可以在以下链接中找到将数据传递给指令的多种方式之间的差异:

AngularJS – Isolated Scopes – @ vs = vs &

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

答案 15 :(得分:0)

@属性字符串绑定(单向) =双向模型绑定 &安培;回调方法绑定

答案 16 :(得分:0)

@将local / directive范围属性绑定到DOM属性的计算值。 =将本地/指令范围属性绑定到父范围属性。 &安培;绑定用于将方法传递到指令的作用域中,以便可以在指令中调用它。

@属性字符串绑定 =双向模型绑定 &安培;回调方法绑定