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

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

以下是一些相关的摘要:

  • 从 HTML:

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

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

我没有几件事:

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

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

答案

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

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

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

使用 @,您可以执行诸如title="{{title}} and then some"内插title="{{title}} and then some" -{{title}},然后将字符串 “and them some some” 与之串联。最终的串联字符串是 local / directive 范围属性所获得的。 (您不能使用= ,只能使用@ 。)

使用@时 ,如果需要在链接函数中使用值,则需要使用attr.$observe('title', function(value) { ... }) 。例如, if(scope.title == "...")将无法正常工作。请注意,这意味着您只能异步访问此属性。如果仅在模板中使用值,则无需使用 $ observe()。例如, template: '<div>{{title}}</div>'

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

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

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

scope: { ... }

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

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

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

也可以看看

这里有很多不错的答案,但是我想对@=&绑定之间的差异提供我的观点,这对我来说很有用。

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

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

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

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

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

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

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

  • @ 字符串
  • = 模型
  • & 方法

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

  1. =
  2. @

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

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

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

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

如果您想通过实际示例了解更多信息。 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 路绑定

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

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

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


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

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

  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>

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

我们可以简单地使用:

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

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

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

编辑

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

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

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

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

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

=方法是2 向绑定 ,它使您可以在指令中进行实时更改。当有人从指令中更改该变量时,您将在指令中包含该更改的数据,但是@way不是双向绑定 。它像Text一样工作。您绑定一次,您将只有它的价值。

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

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

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


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


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


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

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

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


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

::

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

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

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

摘要

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

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