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


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

以下是一些相关的片段:

  • 来自HTML:

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

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

我有几件事没有得到:

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

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


996
2017-12-27 06:10


起源


有道理。研究和寻找答案的能力很重要。 - Jonathan
stackoverflow.com/questions/14908133/... - zloctb
简单来说 = 用于指令隔离范围以启用双向绑定和 @ 不更新模型,仅更新指令范围值。 - STEEL
@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
我在下面看到了很多很好的答案,但是任何人都可以指出回答这个问题的官方角度文档吗? - John Henckel


答案:


为什么我必须使用“{{title}}”和'@'和'标题'与'=“?

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

= 将本地/指令范围属性绑定到 父范围属性。所以 =,您使用父模型/范围属性名称作为DOM属性的值。你不能用 {{}}s with =

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

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

=,你不需要使用$ observe。

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

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

scope: { ... } 

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

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

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

也可以看看


1109
2017-12-28 01:15



嗯,这是一个非常奇怪的行为,特别是当不使用插值而只是尝试传递字符串时。显然,pull请求确实已合并到开发版本中,并且在1.1.5和1.2.0 RC版本中。他们很擅长修复这种非常不直观的行为! - Ibrahim
写“@”或“=”比写“eval-dom”或“parent-scope”或任何其他人类可读文本更清晰。好的设计决定。 - Den
@ ('at')复制'ATtribute'的值。 = ('等于')相当于说键等于你的表达式。至少,这是我如何让他们保持紧张。 - Matt DeKrey
您确定=仅适用于父范围属性吗?任何表达似乎都有效 - 不仅仅是父范围属性。 - Jonathan Aquino
@JonathanAquino,是的,有效,但@更合适 - 有 foo="{{1+1}}"  - 因为我们这里不需要双向数据绑定。我在上面的评论中试图提出的观点是,只有当指令需要双向数据绑定时才应该使用=。使用@或&否则。 - Mark Rajcok


这里有很多很棒的答案,但我想提出我对两者之间差异的看法 @=,和 & 对我来说有用的绑定。

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

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

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

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

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

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

这些符号还使得范围变量在指令实现中的含义更加清晰:

  • @ 
  • =  模型
  • &  方法

为了有用(对我来说反正):

  1. =
  2. @

524
2018-02-03 23:30



其实, "&" 确实支持表单的参数(或者更确切地说,本地人): callback({foo: "some value"}),然后可以使用 <my-dir callback="doSomething(foo)">。否则,好的答案 - New Dev
应该接受答案。这是一篇包含相同信息的简明文章,但增加了代码示例: umur.io/... - Kevin
&不是“回调方法绑定”,它是Angular表达式绑定。表达式是一个特殊但不是唯一的例子 callback(argument)。这还是不一样的 callback 本身。 - Dmitri Zaitsev
虽然我喜欢排名较高的答案是多么明确,但我发现这个答案产生了更有用的影响,在阅读完之后我更了解前一个答案。 - rbnzdave
我同意上述评论,这个答案对这个问题更清楚,明确和有用。它详细解释了您可以使用这些信息。 - user3125823


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

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

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

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


60
2017-12-27 06:20



是的,我得到的那部分,请看问题中的小提琴。但那些不清楚的部分呢? - iwein
问题是{{}}不适用于=。 =未计算,但字符串将作为属性名称。感谢你的回答! - iwein
我不认为=仅适用于父范围中的变量。它适用于任何表达式(例如,1 + 1)。 - Jonathan Aquino
@JonathanAquino你是对的,它评估表达式。 imho这实际上很奇怪,我不会那样用它。正是这种巧妙的技巧使得指令范围首先让我难以理解。 - iwein
我是唯一一个认为这个答案是错误的人! '='表示angular期望一个javascript表达式,如果传递了一个范围变量,它将执行双向映射。而@ mean angular期望一个字符串和所有。事实上,如果你在@ {{}}中使用@,你将克隆变量的值。但这不是@的定义! - Luc DUZAN


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

36
2017-11-21 23:19



问题和最佳答案中有几个相关的例子。这增加了什么? - iwein
@iwein,它增加了清晰度。如果我能理解并吸收全功能的例子,我就不需要这个网站了。 - Tony Ennis
胡安,也许修复你的错别字? 'transclude'拼写错误。更好的是,删除它(以及其他一切,如'替换'),这不会直接导致问题,因此您的解决方案更简单,更清晰。示例为+1。 - Tony Ennis
谢谢你@AnikISlamAbhi的编辑。我想贡献更多,我很高兴有些人觉得我的样品很有帮助。这是主要目的。 - Juan Mendez
不完整的例子。在演示中,您只更改双向值。你甚至都没有尝试改变具有孤立范围的价值。因此,它没有正确地证明范围如何在指令中起作用。 - Sudarshan_SMD


@  得到字符串

  • 这不会产生任何绑定。你只是把你传入的单词作为一个字符串

=  2路绑定

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

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

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


这个小提琴应该证明它们是如何工作的。特别注意范围功能 get... 希望能更好地理解我的意思 &


36
2017-08-26 03:40





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

  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  )

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

@ always总是希望映射属性是一个表达式。这个非常重要;因为要使“@”前缀起作用,我们需要将属性值包装在{{}}中。

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

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

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

例如,指令范围有一个变量“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>

这里有一个 链接 到了很好地描述它的博客。


33
2018-04-26 16:34



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


我们可以使用: -

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

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

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

编辑

在我们的 零件 的定义 Angular版本1.5 以上
有四种不同类型的绑定:

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

  3. @ 这是为了 字符串参数

  4. & 这是为了 回调 以防您的组件需要将某些内容输出到其父作用域


21
2017-08-29 10:44





我创建了一个包含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>

11
2017-12-17 14:26





= 方式是 双向绑定,让你拥有 生活 你的指令内部的变化。当某人将该变量更改为指令时,您将在指令中更改数据,但是 @ 方式不是 双向约束。它很像 文本。你绑定一次,你将只有它的价值。

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

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


7
2018-05-10 23:03