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


1067

我已经仔细阅读了有关该主题的AngularJS文档,然后摆弄了一条指令。这是小提琴

以下是一些相关的摘要:

  • HTML

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

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

有几件事我没有得到:

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

我发现了另一个小提琴,它也显示了表达式解决方案:http : //jsfiddle.net/maxisam/QrCXh/


18
有道理。研究和寻找答案的能力很重要。
乔纳森(Jonathan)


1
简而言之,=在指令隔离范围中使用它来启用双向绑定,@并且不更新模型,仅更新指令范围值。

@iwein为什么您在jsfiddle.net/maxisam/QrCXh的小提琴代码 无法与googleapi 一起使用-ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js?仅当我使用您的CDN时,您的代码才有效-code.angularjs.org/1.0.1/angular-1.0.1.js
MukulSharma

我在下面看到很多不错的答案,但是没有人能提供指向回答这个问题的官方角度文档的指针吗?
约翰·亨克尔

Answers:


1151

为什么我必须在“ @ ”中使用“ {{title}}”,而在“ = ”中使用“ title” ?

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

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

使用@,您可以执行以下操作title="{{title}} and then some":插入{{title}},然后将字符串“ and some some”与其连接在一起。最终的串联字符串是local / directive范围属性所获得的。(您不能使用=,只能使用@。)

随着@,你将需要使用attr.$observe('title', function(value) { ... }),如果您需要在您的链接(荷兰国际集团)函数使用的值。例如,if(scope.title == "...")不会像您期望的那样工作。请注意,这意味着您只能异步访问此属性。如果仅在模板中使用值,则无需使用$ observe()。例如template: '<div>{{title}}</div>'

使用=,您不需要使用$ observe。

我是否还可以直接访问父范围,而不用属性装饰元素?

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

scope: { ... }

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

该文档说:“通常希望通过表达式将数据从隔离的范围传递到父范围”,但这似乎也可以与双向绑定一起使用。为什么表达途径会更好?

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

也可以看看


1
呵呵,这是一个非常奇怪的行为,尤其是当不使用插值而只是尝试传递字符串时。显然,拉取请求确实已合并到开发版本中,并且在1.1.5和1.2.0 RC版本中。擅长于解决这种非常不直观的行为!
Ibrahim

50
写“ @”或“ =”比写“ eval-dom”或“ parent-scope”或任何其他人类可读文本更加清晰。好的设计决策。
书斋

13
@('at')复制“ ATtribute”的值。=('等于')等于说键等于您的表情。至少,这就是我使他们陷入困境的方式。
Matt DeKrey 2014年

1
您确定=仅适用于父范围属性吗?任何表达式似乎都有效-不仅限于父范围属性。
乔纳森·阿基诺

4
@JonathanAquino,是的,但是@会更适合foo="{{1+1}}"-因为-在这里我们不需要双向数据绑定。我在上面的注释中试图指出的一点是,仅在指令需要双向数据绑定时才应使用=。使用@或&否则。
Mark Rajcok 2014年

542

这里有很多伟大的答案,但我想提供有关之间的差异我的角度来看@=&结合被证明对我来说非常有用。

这三种绑定都是通过元素的属性将数据从父范围传递到指令的隔离范围的方式:

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

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

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

我发现通过用简短的描述引用作用域绑定更容易记住这些差异:

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

这些符号还使范围变量在指令的实现内部表示什么变得更加清晰:

  • @
  • = 模型
  • & 方法

为了有用(无论如何对我来说):

  1. =
  2. @

13
实际上,"&"确实支持形式为的自变量(或更确切地说是本地变量),callback({foo: "some value"})然后可以使用<my-dir callback="doSomething(foo)">。否则,很好的答案
New Dev

11
应该接受答案。这是一篇简明扼要的文章,具有相同的信息,但带有附加的代码示例:umur.io/…–
Kevin Kevin

4
&不是“回调方法绑定”,而是Angular表达式绑定。表达式是一个特殊但并非唯一的示例callback(argument)。还是不一样callback
Dmitri Zaitsev 2015年

14
尽管我喜欢较高排名的答案的确定性,但我发现此答案产生了更大的影响,并且阅读此答案后,我对上一个答案有了更多的了解。
rbnzdave

1
我同意以上评论,这个答案对于这个问题更加清晰,明确和有用。它详细解释了您可以使用的信息。
user3125823 '16

64

=装置的双向绑定,因此给变量到父范围的参考。这意味着,当您更改指令中的变量时,它也会在父作用域中被更改。

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

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

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


1
是的,我明白这一点,请看问题中的小提琴。但是不清楚的部分呢?
iwein 2012年

4
问题是{{}}不能与=一起使用。=不会被评估,但字符串将直接用作属性名称。感谢您的回答!
iwein 2012年

1
我不认为=仅用于父作用域中的变量。它适用于任何表达式(例如1 + 1)。
乔纳森·阿基诺

1
@JonathanAquino您说对了表达式,是正确的。恕我直言,这实际上很奇怪,我不会那样使用。正是这种巧妙的技巧使指令作用域一开始让我很难理解。
iwein

1
我是唯一认为这个答案是错误的人!'='表示期望的角度是JavaScript表达式,如果传递了范围变量,则将进行双向映射。而@平均角度期望一个String等等。实际上,如果将@与{{}}结合使用,则将克隆变量的值。但这不是@的定义!
吕克·杜赞

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

2
问题和最佳答案中链接了几个示例。这增加了什么?
iwein 2014年

10
@iwein,它增加了清晰度。如果我能理解和吸收功能齐全的示例,则不需要此站点。
托尼·恩尼斯

3
胡安,也许解决你的错字?“ transclude”拼写错误。更好的是,删除不会直接导致问题的它(以及其他所有内容,例如“替换”),因此您的解决方案更加简单明了。范例为+1。
托尼·恩尼斯

谢谢@AnikISlamAbhi的编辑。我想贡献更多,很高兴有人发现我的样品有用。那是主要目的。
胡安·门德斯

示例不完整。在演示中,您仅更改双向值。您甚至没有试图更改具有隔离范围的值。因此,它没有正确演示作用域如何在指令中工作。
Sudarshan_SMD

38

@ 作为字符串获取

  • 这不会创建任何绑定。您只是获得了作为字符串传递的单词

= 2路装订

  • 控制器所做的更改将反映在指令保留的引用中,反之亦然

&这的行为有些不同,因为作用域获得了一个函数,该函数返回传入的对象。我假设这是使其工作必不可少的。小提琴应该弄清楚这一点。

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


这个小提琴应该证明它们是如何工作的。请特别注意名称中的作用域函数,get...以期更好地理解我的意思。&


36

可以在指令中添加范围的三种方式:

  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>

这是一个很好描述博客的链接


&既不是“行为绑定”也不是“方法绑定”,它是Angular表达式绑定。
Dmitri Zaitsev 2015年

20

我们可以简单地使用:

  1. @:-用于字符串值的一种方式数据绑定。以一种方式数据绑定只能将范围值传递给指令

  2. =:-用于双向数据绑定的对象值。通过两种方式的数据绑定,您可以在指令以及html中更改作用域值。

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

编辑

Angular 1.5及更高版本的Component定义中, 有四种不同类型的绑定:

  1. = 双向数据绑定:-如果更改值,它将自动更新
  2. < 一种方式绑定:-当我们只想从父范围读取参数而不更新它时。

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

  4. &这用于回调,以防您的组件需要向其父范围输出某些内容


13

我创建了一个小的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>

6

=方式是2路结合,它可以让你有你的指令内的变化。当某人从指令中更改该变量时,您将在指令中拥有该更改的数据,但是@way不是双向绑定。它像Text一样工作。您绑定一次,您将只有它的价值。

为了使它更清晰,您可以使用以下精彩文章:

AngularJS指令范围'@'和'='


6

这个问题已经被打死了,但是我仍然会分享这个问题,以防其他人为AngularJS范围的可怕困境而苦苦挣扎。这将包括=<@&::。完整的文章可以在这里找到。


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


<建立单向绑定,父母对孩子。更改父级属性将导致子级更改,但更改子级属性不会影响父级属性。


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

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

在此,description子作用域中的属性将是expression的当前值"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的更高版本引入了具有一次性绑定的选项,其中子范围属性仅更新一次。通过消除监视父属性的需求,可以提高性能。语法与上面不同。要声明一次性绑定,请::component标签的表达式前面添加

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

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

摘要

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

各种隔离范围绑定如何工作


4

@ local scope属性用于访问在指令外部定义的字符串值。

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

local scope属性允许指令的使用者传递该指令可以调用的函数。

请检查下面的链接,它使您对示例有了清楚的了解。我发现它非常有用,因此可以考虑共享它。

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


3

即使范围是本地的(如您的示例),您也可以通过属性访问父范围$parent。假定下面的代码title是在父作用域上定义的。然后,您可以按$parent.title以下方式访问标题:

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

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

我在指令中找到了一个示例,其中“&”符号用于“通过表达式将数据从隔离范围传递到父范围”(有用,并且无法使用双向数据绑定)用于在ng-repeat中呈现特殊的数据结构。

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

渲染的一部分是删除按钮,这里通过&从外部作用域附加delete函数很有用。在渲染指令中,它看起来像

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

2向数据绑定,即data = "="不能使用,因为删除功能会在每个$digest周期上运行,这是不好的,因为然后立即删除记录并且从不呈现记录。




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和父对象的parameter 来调用y
您可以覆盖两者。
现在,您知道了为什么),并用您指定的参数(在本例中为)替换了可能的值。<button ng-click="functionFromParent({x:5})"></button>
因为它只调用parent的表达式(例如<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>

它只是一个表达式,无论是一个函数还是多个函数,还是只是比较,都没有关系。您可以替换此表达式的任何变量。

示例:
指令模板与调用代码:
父级已定义$ 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

父级已定义$ 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


0

为什么我必须在“ @”中使用“ {{title}}”,而在“ =”中使用“ title”?

当您使用{{title}}时,只会将父作用域值传递到指令视图并进行评估。这仅限于一种方式,这意味着更改不会反映在父范围内。如果要将子指令中所做的更改也反映到父作用域中,则可以使用“ =”。这是两种方式。

我是否还可以直接访问父范围,而不用属性装饰元素?

当指令中具有scope属性(scope:{})时,您将不再能够直接访问父范围。但是仍然可以通过scope。$ parent等访问它。如果从指令中删除范围,则可以直接访问它。

该文档说:“通常希望通过表达式将数据从隔离的范围传递到父范围”,但这似乎也可以与双向绑定一起使用。为什么表达途径会更好?

它取决于上下文。如果要使用数据调用表达式或函数,请使用&;如果要共享数据,则可以使用biderectional方式,并使用'='

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

AngularJS –孤立的作用域– @ vs = vs&

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



0

@将本地/定向范围属性绑定到DOM属性的评估值。=将本地/定向范围属性绑定到父范围属性。&绑定用于将方法传递到指令的作用域中,以便可以在指令中调用它。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.