1096

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

以下是一些相关的片段:

  • HTML

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

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

有几件事我没有得到:

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

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

4

18 回答 18

1167

为什么我必须使用带有' @ '的“{{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 them some”与它连接起来。最终连接的字符串是本地/指令范围属性得到的。(你不能用=做到这一点,只有@。)

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

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

我也可以直接访问父范围,而不用属性装饰我的元素吗?

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

scope: { ... }

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

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

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

也可以看看

于 2012-12-28T01:15:27.413 回答
550

这里有很多很好的答案,但我想就事实证明对我有用的 , 和 binding@之间=的差异提供我的观点。&

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

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

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

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

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

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

这些符号还可以更清楚地说明作用域变量在指令实现中所代表的内容:

  • @ 细绳
  • = 模型
  • & 方法

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

  1. =
  2. @
  3. &
于 2015-02-03T23:30:29.020 回答
67

=意味着双向绑定,因此对父范围的变量的引用。这意味着,当您更改指令中的变量时,它也会在父范围内更改。

@意味着变量将被复制(克隆)到指令中。

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

如果您需要更改父范围内的多个变量,您可以从指令内在父范围上执行一个函数(或通过服务传递数据)。

于 2012-12-27T06:20:30.460 回答
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>"
    };
});
于 2014-11-21T23:19:34.233 回答
39

@ 获取字符串

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

= 2路绑定

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

&这有点不同,因为作用域获得了一个函数,该函数返回传入的对象。我假设这是使它工作所必需的。小提琴应该清楚地说明这一点。

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


这个小提琴应该演示它们是如何工作的。特别注意get...名称中的作用域函数,希望能更好地理解我的意思&

于 2015-08-26T03:40:26.873 回答
38

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

  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>

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

于 2015-04-26T16:34:27.507 回答
20

我们可以简单地使用:-

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

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

  3. & :- 用于方法和函数。

编辑

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

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

  3. @这是字符串参数

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

于 2016-08-29T10:44:27.550 回答
14

我创建了一个包含 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>
于 2015-12-17T14:26:57.937 回答
9

这个问题已经被打死了,但无论如何我都会分享这个问题,以防其他人正在为 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: '<'
  }
});

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


要查看不同的前缀,请打开此plunk

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

[官方文档]
AngularJS 的更高版本引入了具有一次性绑定的选项,其中子范围属性仅更新一次。这通过消除查看父属性的需要来提高性能。语法与上面不同;要声明一次性绑定,请在组件标记::中的表达式前面添加:

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

这会将 的值传播tagline到子范围,而无需建立单向或双向绑定。注意:如果tagline最初undefined是在父范围内,Angular 会一直观察到它发生变化,然后对子范围内的相应属性进行一次更新。

概括

下表显示了前缀如何根据属性是对象、数组、字符串等来工作。

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

于 2019-02-13T14:08:17.957 回答
6

=方式是2-way binding,它允许您在指令中进行实时更改。当有人从指令中更改该变量时,您将在指令中拥有更改后的数据,但@way不是双向绑定。它像Text一样工作。你绑定一次,你将只有它的价值。

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

AngularJS 指令范围“@”和“=”

于 2016-05-10T23:03:06.397 回答
4

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

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

&局部范围属性允许指令的使用者传入指令可以调用的函数。

请检查下面的链接,它可以通过示例让您清楚地理解。我发现它非常有用,所以想分享它。

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

于 2016-02-05T13:12:00.427 回答
3

即使范围是本地的,例如您的示例,您也可以通过 property 访问父范围$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>

渲染的一部分是一个删除按钮,在这里通过 & 从外部范围附加一个删除函数很有用。在渲染指令内部看起来像

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

2-way databinding iedata = "="不能使用,因为删除函数会在每个$digest循环中运行,这是不好的,因为记录会立即被删除并且永远不会呈现。

于 2014-07-11T08:32:25.483 回答
3

它们之间的主要区别只是

@ Attribute string binding
= Two-way model binding
& Callback method binding
于 2018-05-31T09:51:56.857 回答
3

我在小提琴中实现了所有可能的选项。

它处理所有选项:

scope:{
    name:'&'
},

scope:{
    name:'='
},

scope:{
    name:'@'
},

scope:{

},

scope:true,

https://jsfiddle.net/rishulmatta/v7xf2ujm

于 2015-12-20T06:15:56.773 回答
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>有效。
因为它只是调用 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

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

于 2016-10-20T16:13:48.577 回答
0

为什么我必须用“{{title}}”和“@”和“title”和“=”?

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

我也可以直接访问父范围,而不用属性装饰我的元素吗?

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

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

这取决于上下文。如果你想用数据调用表达式或函数,你使用 & 如果你想共享数据,你可以使用双向方式使用 '='

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

AngularJS – 隔离作用域 – @ vs = vs &

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

于 2017-06-28T07:42:41.807 回答
0

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

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

于 2018-06-18T06:36:16.983 回答
0

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

于 2017-11-13T05:39:41.247 回答