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

iwe*_*ein 1053 angularjs angularjs-directive angularjs-scope isolated-scope

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

以下是一些相关的片段:

我有几件事没有得到:

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

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

Mar*_*cok 1146

为什么我必须将"{{title}}"与" @ "和"title"与" = "一起使用?

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

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

使用@,您可以执行诸如title="{{title}} and then some"- {{title}}插值之类的操作,然后将字符串"和它们一些"连接起来.最终的连接字符串是本地/指令范围属性获取的内容.(你不能用=,只做@.)

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

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

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

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

scope: { ... }

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

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

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

也可以看看

  • 写"@"或"="比写"eval-dom"或"parent-scope"或任何其他人类可读文本更清晰.好的设计决定. (47认同)
  • `@`('at')复制'ATtribute'的值.`=`('equals')相当于说键等于你的表达式.至少,这是我如何让他们保持紧张. (13认同)
  • @JonathanAquino,是的,但是@更合适 - 使用`foo ="{{1 + 1}}"` - 因为我们这里不需要双向数据绑定.我在上面的评论中试图提出的观点是,只有当指令需要双向数据绑定时才应该使用=.使用@或&否则. (4认同)

pix*_*its 538

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

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

  1. @ binding用于传递字符串.这些字符串支持{{}}插值的表达式.例如: .内插表达式根据指令的父作用域进行评估.

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

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

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

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

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

  • @
  • = 模型
  • & 方法

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

  1. =
  2. @
  3. &

  • 虽然我喜欢排名较高的答案是多么明确,但我发现这个答案产生了更有用的影响,在阅读完之后我更了解前一个答案. (14认同)
  • 实际上,`"&"`确实支持以下形式的参数(或者更确切地说是本地人):`callback({foo:"some value"})`,然后可以使用`<my-dir callback ="doSomething(富)">`.否则,好的答案 (13认同)
  • 应该接受答案.这是一篇包含相同信息的简明文章,但增加了代码示例:https://umur.io/angularjs-directives-using-isolated-scope-with-attributes/ (11认同)
  • &不是"回调方法绑定",它是Angular表达式绑定.一个特殊但不是唯一的例子是表达式`callback(argument)`.这与`callback`本身仍然不一样. (4认同)

asg*_*oth 64

=意味着双向绑定,因此引用一个变量到父范围.这意味着,当您更改指令中的变量时,它也将在父作用域中更改.

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

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

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

  • 问题是{{}}不适用于=.=未计算,但字符串将作为属性名称.谢谢你的回答! (4认同)

Jua*_*dez 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>"
    };
});
Run Code Online (Sandbox Code Playgroud)

  • @iwein,它增加了清晰度.如果我能理解并吸收全功能的例子,我就不需要这个网站了. (9认同)
  • 胡安,也许修复你的错别字?'transclude'拼写错误.更好的是,将其删除(以及其他所有内容,例如"替换")不会直接导致问题,因此您的解决方案更简单,更清晰.示例为+1. (3认同)
  • 问题和最佳答案中有几个相关的例子.这增加了什么? (2认同)

geg*_*geg 38

@ 得到字符串

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

= 2路绑定

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

&这种行为稍有不同,因为作用域获取了一个返回传入对象的函数.我认为这是必要的,以使其工作.小提琴应该清楚这一点.

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


这个小提琴应该证明它们是如何工作的.get...在名称中特别注意范围功能,以期更好地理解我的意思&


Kop*_*lyf 34

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

  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.
        }
    };
});
Run Code Online (Sandbox Code Playgroud)
  1. 隔离范围:当您要创建不从控制器范围继承的范围时,使用此范围.

当您创建插件时会发生这种情况,因为这会使指令变为通用,因为它可以放在任何HTML中,并且不受其父作用域的影响.

现在,如果您不希望与父作用域进行任何交互,则只需将作用域指定为空对象即可.喜欢,

scope: {} //this does not interact with the parent scope in any way
Run Code Online (Sandbox Code Playgroud)

大多数情况并非如此,因为我们需要与父作用域进行一些交互,因此我们希望一些值/更改能够通过.出于这个原因,我们使用:

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

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

@ 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
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

和html(注意@和=的不同):

<div my-directive
  class="directive"
  name="{{name}}"
  reverse="reverseName()"
  color="color" >
</div>
Run Code Online (Sandbox Code Playgroud)

这是一个博客的链接,很好地描述了它.


oju*_*rni 20

我们可以使用: -

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

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

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

编辑

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

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

  3. @这是针对字符串参数的

  4. &如果您的组件需要将某些内容输出到其父作用域,则这适用于回调


Rob*_*bin 12

我创建了一个包含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>
Run Code Online (Sandbox Code Playgroud)


Haz*_*yan 6

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

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

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


Mih*_*ira 6

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


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


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


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

<child-component description="The movie title is {{$ctrl.movie.title}}" />
Run Code Online (Sandbox Code Playgroud)
bindings: {
    description: '@', 
}
Run Code Online (Sandbox Code Playgroud)

此处,description子范围内的属性将是expression的当前值"The movie title is {{$ctrl.movie.title}}",其中movie父范围内的对象是。


&有点棘手,实际上似乎没有令人信服的理由去使用它。它允许您在父作用域中评估表达式,用子作用域中的变量替换参数。一个例子(plunk):

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

给定parentVar=10,表达式parentFoo({myVar:5, myOtherVar:'xyz'})将计算为5 + 10 + 'xyz',并且组件将呈现为:

<div>15xyz</div>
Run Code Online (Sandbox Code Playgroud)

您什么时候想使用这种复杂的功能?&人们经常使用它来将父范围内的回调函数传递给子范围。但是,实际上,通过使用'<'传递函数可以实现相同的效果,该效果更直接,并且避免了笨拙的花括号语法传递参数({myVar:5, myOtherVar:'xyz'})。考虑:

回调使用&

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

回调使用<

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

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


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

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

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

<child-component 
  tagline = "::$ctrl.tagline">
</child-component>
Run Code Online (Sandbox Code Playgroud)

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

摘要

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

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