AngularJS指令范围内的'@'和'='有啥区别?
Posted
技术标签:
【中文标题】AngularJS指令范围内的\'@\'和\'=\'有啥区别?【英文标题】:What is the difference between '@' and '=' in directive scope in AngularJS?AngularJS指令范围内的'@'和'='有什么区别? 【发布时间】:2012-12-12 13:53:22 【问题描述】:我已经仔细阅读了有关该主题的 AngularJS 文档,然后摆弄了一个指令。这是fiddle。
这里有一些相关的sn-ps:
来自HTML:
<pane bi-title="title" title="title">text</pane>
来自窗格指令:
scope: biTitle: '=', title: '@', bar: '=' ,
有几件事我没有得到:
为什么我必须将"title"
与'@'
和"title"
与'='
一起使用?
我是否也可以直接访问父作用域,而不用属性装饰我的元素?
文档说“通常希望通过表达式将数据从隔离范围传递到父范围”,但这似乎也适用于双向绑定。为什么表达路线会更好?
我还发现了另一个显示表达式解决方案的小提琴:http://jsfiddle.net/maxisam/QrCXh/
【问题讨论】:
公平点。研究和寻找答案的能力很重要。 ***.com/questions/14908133/… 简而言之,=
用于指令隔离范围以启用双向绑定,@
不更新模型,仅更新指令范围值。
@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 时,您的代码才有效
我在下面看到了很多很好的答案,但是任何人都可以指点官方 Angular 文档来回答这个问题吗?
【参考方案1】:
这个问题已经被打死了,但无论如何我都会分享这个问题,以防其他人正在为 AngularJS 范围的可怕混乱而苦苦挣扎。这将涵盖=
、<
、@
、&
和::
。完整的文章可以在here 找到。
=
建立双向绑定。更改父项中的属性将导致子项发生更改,反之亦然。
<
建立一个单向绑定,父到子。改变父属性会导致子属性发生变化,但改变子属性不会影响父属性。
@
将为子属性分配标签属性的字符串值。如果属性包含expression,则只要表达式计算为不同的字符串,子属性就会更新。例如:
<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。
使用::
的一次性绑定(初始化)
[Official docs]
更高版本的 AngularJS 引入了具有一次性绑定的选项,其中子范围属性仅更新一次。这通过消除查看父属性的需要来提高性能。语法与上面不同;要声明一次性绑定,请在 组件标记中的表达式前面添加::
:
<child-component
tagline = "::$ctrl.tagline">
</child-component>
这会将tagline
的值传播到子作用域,而无需建立单向或双向绑定。 注意:如果tagline
最初在父作用域中是undefined
,angular会观察它直到它发生变化,然后对子作用域中的相应属性进行一次性更新。
总结
下表显示了前缀如何根据属性是否为对象、数组、字符串等工作。
【讨论】:
【参考方案2】:我创建了一个包含 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>
【讨论】:
【参考方案3】:@ 将本地/指令范围属性绑定到 DOM 属性的评估值。 = 将本地/指令范围属性绑定到父范围属性。 & 绑定用于将方法传递到指令的作用域中,以便可以在指令中调用它。
@属性字符串绑定 = 双向模型绑定 & 回调方法绑定
【讨论】:
【参考方案4】:它们之间的主要区别只是
@ Attribute string binding
= Two-way model binding
& Callback method binding
【讨论】:
【参考方案5】:@
和 =
查看其他答案。
一个关于 &
TL;DR;&
的gotcha得到表达 strong> (不仅像其他答案中的示例中的函数一样)来自父级,并将其设置为指令中的函数,该函数调用表达式。并且此函数能够替换表达式的任何变量(甚至函数名),方法是传递带有变量的对象。
解释&
是一个表达式引用,这意味着如果你传递类似
<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>
有效。
因为它只是调用父表达式(例如<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
【讨论】:
【参考方案6】:@属性字符串绑定(一种方式) = 双向模型绑定 & 回调方法绑定
【讨论】:
【参考方案7】:在指令中可以通过三种方式添加作用域:
-
父范围:这是默认范围继承。
指令和它的父(控制器/它所在的指令)范围是相同的。 因此,对指令内部范围变量所做的任何更改也会反映在父控制器中。您不需要指定它,因为它是默认值。
-
子作用域:如果您将指令的作用域变量指定为 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.
;
);
-
独立作用域:当您要创建不从控制器作用域继承的作用域时使用。
当您创建插件时会发生这种情况,因为这会使指令通用,因为它可以放置在任何 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>
这是博客的link,它很好地描述了它。
【讨论】:
& 既不是“行为绑定”也不是“方法绑定”,它是 Angular 表达式绑定。【参考方案8】:为什么我必须将“title”与“@”一起使用,“title”与“=”一起使用?
当您使用 title 时,只有父范围值将传递给指令视图并进行评估。这仅限于一种方式,这意味着更改不会反映在父范围中。当您想将子指令中所做的更改也反映到父范围时,您可以使用“=”。这是两种方式。
我是否也可以直接访问父作用域,而不用修饰我的 具有属性的元素?
当指令中包含范围属性(范围:)时,您将不再能够直接访问父范围。但是仍然可以通过 scope.$parent 等方式访问它。如果从指令中删除范围,则可以直接访问它。
文档说“通常需要从 通过表达式和父作用域隔离作用域”,但是 似乎也适用于双向绑定。为什么会 表达路线更好?
这取决于上下文。如果你想用数据调用表达式或函数,你使用 & 如果你想共享数据,你可以使用双向方式使用 '='
您可以在下面的链接中找到将数据传递给指令的多种方式之间的区别:
AngularJS – Isolated Scopes – @ vs = vs &
http://www.codeforeach.com/angularjs/angularjs-isolated-scopes-vs-vs
【讨论】:
【参考方案9】:我们可以简单地使用:-
@ :- 用于单向数据绑定的字符串值。在一种数据绑定方式中,您只能将范围值传递给指令
= :- 用于双向数据绑定的对象值。以两种方式数据绑定,您可以更改指令中的范围值以及 html 中的值。
& :- 用于方法和函数。
编辑
在 Angular 1.5 版及更高版本的组件定义中 有四种不同类型的绑定:
=
双向数据绑定 :- 如果我们改变值,它会自动更新
<
单向绑定 :- 当我们只想从父作用域读取参数而不更新它时。
@
这是 字符串参数
&
这是用于回调,以防您的组件需要向其父范围输出某些内容
【讨论】:
【参考方案10】:这里有很多很好的答案,但我想就@
、=
和&
绑定之间的区别提供我的观点,这些绑定对我很有用。
所有三个绑定都是通过元素的属性将数据从父范围传递到指令的隔离范围的方式:
@ 绑定用于传递字符串。 这些字符串支持插入值的
表达式。 例如: .对插值表达式进行评估 指令的父范围。
= 绑定用于双向模型绑定。父范围内的模型 链接到指令隔离范围内的模型。更改为 一个模型影响另一个模型,反之亦然。
& 绑定用于将方法传递到指令的范围内,以便 它可以在您的指令中调用。该方法预先绑定到 指令的父范围,并支持参数。例如,如果方法在父范围内是 hello(name),那么在 为了从指令内部执行该方法,您必须 调用 $scope.hello(name:'world')
我发现通过更简短的描述来引用范围绑定更容易记住这些差异:
@
属性字符串绑定
=
双向模型绑定
&
回调方法绑定
这些符号还可以更清楚地说明作用域变量在指令的实现中代表什么:
@
字符串
=
模型
&
方法
按有用性排序(无论如何对我来说):
-
=
@
&
【讨论】:
实际上,"&"
确实支持callback(foo: "some value")
形式的参数(或者更确切地说,本地参数),然后可以使用<my-dir callback="doSomething(foo)">
。否则,很好的答案
应该被接受的答案。这是一篇包含相同信息但添加了代码示例的简明文章:umur.io/…
& 不是“回调方法绑定”,它是 Angular 表达式绑定。一个特殊但不是唯一的例子是表达式callback(argument)
。这仍然与callback
本身不同。
虽然我喜欢排名较高的答案是多么明确,但我发现这个答案产生了更有用的影响,在阅读了这个答案后,我对之前的答案有了更多的理解。
我同意上面的评论,这个答案对这个问题更加清晰、明确和有用。它解释了足够详细的信息,您可以去使用这些信息。【参考方案11】:
=
表示双向绑定,因此对父范围的变量的引用。这意味着,当您更改指令中的变量时,它也会在父范围内更改。
@
表示变量将被复制(克隆)到指令中。
据我所知,<pane bi-title="title" title="title">text</pane>
也应该可以。 bi-title
将接收父作用域变量值,可以在指令中更改。
如果您需要更改父作用域中的多个变量,您可以在指令中在父作用域上执行一个函数(或通过服务传递数据)。
【讨论】:
是的,我得到的那部分,请参阅问题中的小提琴。但是不清楚的部分呢? 问题是 不适用于 =。 = 未评估,但字符串按原样作为属性名称。感谢您的回答! 我不认为 = 仅适用于父范围内的变量。它适用于任何表达式(例如,1+1)。 @JonathanAquino 你是对的,它评估表达式。恕我直言,这实际上很奇怪,我不会那样使用它。正是这种巧妙的技巧让我一开始就很难理解指令作用域。 只有我一个人认为这个答案是错误的! '=' 表示角度期望一个 javascript 表达式,如果传递了范围变量,它将执行双向映射。而@mean angular 期望一个字符串等等。事实上,如果您将@ 与 结合使用,您将克隆变量的值。但这不是@的定义!【参考方案12】:为什么我必须将“title”与“@”一起使用,而“title”与“=”一起使用?
@ 将本地/指令范围属性绑定到 DOM 属性的评估值。如果使用title=title1
或title="title1"
,则DOM 属性“title”的值就是字符串title1
。如果您使用title="title"
,则DOM 属性“title”的值是title
的插值,因此该字符串将是当前设置的父范围属性“title”。由于属性值始终是字符串,因此在使用 @ 时,您将始终在指令范围内得到该属性的字符串值。
= 将本地/指令范围属性绑定到父范围属性。因此,使用 =,您可以使用父模型/范围属性名称作为 DOM 属性的值。您不能将 s 与 = 一起使用。
使用@,您可以执行title="title and then some"
之类的操作——title 被插值,然后字符串“and them some”与它连接。最终连接的字符串是本地/指令范围属性得到的。 (您不能使用 = 来执行此操作,只能使用 @。)
使用 @,如果您需要在 link(ing) 函数中使用该值,则需要使用 attr.$observe('title', function(value) ... )
。例如,if(scope.title == "...")
不会像您期望的那样工作。请注意,这意味着您只能访问此属性asynchronously。
如果您只使用模板中的值,则不需要使用 $observe()。例如,template: '<div>title</div>'
。
使用 =,您无需使用 $observe。
我也可以直接访问父作用域,而不用属性装饰我的元素吗?
是的,但前提是您不使用隔离范围。从你的指令中删除这一行
scope: ...
然后您的指令将不会创建新范围。它将使用父范围。然后,您可以直接访问所有父范围属性。
文档说“通常希望通过表达式将数据从隔离范围传递到父范围”,但这似乎也适用于双向绑定。为什么表达路线会更好?
是的,双向绑定允许本地/指令范围和父范围共享数据。 “表达式绑定”允许指令调用由 DOM 属性定义的表达式(或函数)——您还可以将数据作为参数传递给表达式或函数。所以,如果你不需要与父级共享数据——你只想调用一个定义在父级范围内的函数——你可以使用 & 语法。
另见
Lukas's isolated scope blog post(包括@、=、&) dnc253's explanation 的 @ 和 = my blog-like answer 关于作用域 -- directives 部分(位于底部,就在 Summary 部分之前)有一张隔离作用域及其父作用域的图片 -- 指令作用域使用 @ 作为一个属性和 = 另一个 What is the difference between & vs @ and = in angularJS【讨论】:
嗯,这是一个非常奇怪的行为,尤其是在不使用插值而只是尝试传递字符串时。显然,拉取请求确实已合并到开发版本中,并且在 1.1.5 和 1.2.0 RC 版本中。很高兴他们解决了这种非常不直观的行为! 写 '@' 或 '=' 比写 "eval-dom" 或 "parent-scope" 或任何其他人类可读的文本要清晰得多。良好的设计决策。@
('at') 复制 'ATtribute' 的值。 =
('equals') 相当于说 key 等于你的表达式。至少,这是我让他们保持冷静的方式。
您确定 = 仅适用于父范围属性吗?任何表达式似乎都有效 - 不仅是父范围属性。
@JonathanAquino,是的,这行得通,但@ 会更合适——使用foo="1+1"
——因为我们在这里不需要双向数据绑定。我在上面的评论中试图说明的一点是,我们应该只在指令需要双向数据绑定时才使用 =。使用 @ 或 & 否则。【参考方案13】:
= 方式是2-way binding,它允许您在指令中进行实时 更改。当有人从指令中更改该变量时,您将在指令中拥有更改后的数据,但 @ 方式不是 双向绑定。它的工作方式类似于 文本。绑定一次,就只有它的值了。
为了更清楚地了解它,您可以使用这篇很棒的文章:
AngularJS Directive Scope '@' and '='
【讨论】:
【参考方案14】:我在小提琴中实现了所有可能的选项。
它处理所有选项:
scope:
name:'&'
,
scope:
name:'='
,
scope:
name:'@'
,
scope:
,
scope:true,
https://jsfiddle.net/rishulmatta/v7xf2ujm
【讨论】:
【参考方案15】:@ 本地范围属性用于访问在指令之外定义的字符串值。
=如果您需要在外部作用域和指令的隔离作用域之间创建双向绑定,您可以使用 = 字符。
& 局部范围属性允许指令的使用者传入指令可以调用的函数。
请查看下面的链接,它可以通过示例让您清楚地理解。我发现它非常有用,所以想分享它。
http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-2-isolate-scope
【讨论】:
【参考方案16】:@
获取字符串
=
2路绑定
&
这有点不同,因为作用域获得了一个返回传入的对象的函数。我假设这是使它工作所必需的。 小提琴应该清楚地说明这一点。
This fiddle should demonstrate how they work。特别注意名称中带有get...
的作用域函数,希望能更好地理解我对&
的意思
【讨论】:
【参考方案17】:即使作用域是本地的,如您的示例,您也可以通过属性$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>"
不能使用双向数据绑定,即data = "="
,因为删除功能会在每个$digest
循环上运行,这不好,因为记录会立即被删除并且永远不会呈现。
【讨论】:
【参考方案18】:如果您想通过一个实时示例了解更多它是如何工作的。 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>"
;
);
【讨论】:
问题和最佳答案中链接了几个示例。这增加了什么? @iwein,它增加了清晰度。如果我能理解和吸收功能齐全的例子,我就不需要这个网站了。 juan,也许改正你的错别字? 'transclude' 拼写错误。更好的是,删除它(以及其他所有内容,如“替换”)不会直接导致问题,因此您的解决方案更加简单和清晰。为示例 +1。 感谢@AnikISlamAbhi 的编辑。我想贡献更多,我很高兴有些人发现我的样本很有帮助。这是主要目的。 不完整的例子。在您的演示中,您只更改了双向值。您甚至没有尝试更改具有孤立范围的价值。因此,它没有正确演示作用域在指令中的工作方式。以上是关于AngularJS指令范围内的'@'和'='有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
Java 中 给一个object 赋值属性, 既可以用构造函数的方式,也可以用setXXXX()的方式,而它们之间有啥区
在自定义指令的范围绑定中使用符号“@”、“&”、“=”和“>”:AngularJS
AngularJS:当 $rootScope 值更改时,指令内的 $watch 不起作用