我知道我们通常通过隔离范围将函数传递给指令:
.directive('myComponent', function () {
return {
scope:{
foo: '&'
}
};
})
然后在模板中我们可以像这样调用这个函数:
<button class="btn" ng-click="foo({ myVal: value })">Submit</button>
其中myVal
是父作用域中函数foo
的参数名称。
现在,如果我打算使用link
函数而不是模板来使用它,我将不得不用scope.foo()(value)
调用它,因为scope.foo
用作原始函数的包装器。这对我来说似乎有点乏味。
如果我使用myComponent
将函数传递给=
指令:
.directive('myComponent', function () {
return {
scope:{
foo: '='
}
};
})
然后我可以从我的链接功能中使用scope.foo(value)
。那么这是一个在函数上使用双向绑定的有效用例,还是我做了一些我不应该做的黑客攻击?
答案 0 :(得分:19)
这就是我贬低答案的原因。
首先,你不应该使用'='来传递对指令的函数引用。
'='创建两个监视并使用它们来确保指令范围和父范围引用是相同的(双向绑定)。允许指令更改父作用域中函数的定义是一个非常糟糕的主意,这是使用此类绑定时发生的情况。此外,手表应尽量减少 - 虽然它会起作用,但两个额外的手表是不必要的。所以这不好 - 下来投票的一部分是暗示它是。
第二 - 答案歪曲了什么'&amp;'确实。 &安培;不是“单向约束”。它只是因为,与'='不同,它不会创建任何$ watch,并且在指令范围内更改属性的值不会传播到父级。
根据文件:
&安培;或&amp; attr - 提供在上下文中执行表达式的方法 父范围
当你使用&amp;在指令中,它生成一个函数,该函数返回针对父作用域计算的表达式的值。表达式不必是函数调用。它可以是任何有效的角度表达式。此外,这个生成的函数接受一个object参数,该参数可以覆盖表达式中找到的任何局部变量的值。
要扩展OP的示例,假设父级以下列方式使用此指令:
<my-component foo="go()">
在指令(模板或链接功能)中,如果你打电话
foo({myVal: 42});
你正在做的是评估表达式“go()”,它恰好在父作用域上调用函数“go”,不传递任何参数。
可替换地,
<my-component foo="go(value)">
您正在评估父作用域上的表达式“go(value)”,它基本上会调用$ parent.go($ parent.value)“
<my-component foo="go(myVal)">
您正在评估表达式“go(myVal)”,但在计算表达式之前,myVal将替换为42,因此评估的表达式将为“go(42)”。
<my-component foo="myVal + value + go()">
在这种情况下,$ scope.foo({myVal:42})将返回以下结果:
42 + $parent.value + $parent.go()
基本上,这种模式允许指令“注入”指令的使用者可以选择在foo表达式中使用的变量。
你可以这样做:
<my-component foo="go">
并在指令中:
$scope.foo()(42)
$ scope.foo()将计算表达式“go”,它将返回对$ parent.go函数的引用。然后它将其称为$ parent.go(42)。此模式的缺点是,如果表达式未计算函数,则会出现错误。
投票结果的最后一个原因是ng-event指令使用&amp;的断言。事实并非如此。所有内置指令都不会创建隔离范围:
scope:{
}
'&amp; foo'的实现(为简洁起见而简化),归结为:
$scope.foo = function(locals) {
return $parse(attr.foo)($scope.$parent, locals);
}
ng-click的实现类似,但(也简化):
link: function(scope, elem, attr) {
elem.on('click', function(evt) {
$parse(attr.ngClick)(scope, {
$event: evt
}
});
}
所以要记住的关键是当你使用'&amp;'时,你没有传递一个函数 - 你正在传递一个表达式。该指令可以通过调用生成的函数随时获取该表达式的结果。
答案 1 :(得分:3)
传递函数的双向绑定很好,只要你的函数总是以相同的顺序采用相同的参数。但也为此无用。
单向绑定更有效,允许使用任何参数以任何顺序调用函数,并在HTML中提供更多可见性。例如,我们无法想象runner
是双向绑定:有时你想要ngClick
之类的更复杂的东西,比如
<div ng-click="doStuff(var1)">
请参阅:您可以直接从HTML中操作参数。
现在我觉得你误解了如何使用单向绑定。如果您确实在指令中定义了<div ng-click="doStuff('hardcoded', var1+4); var2 && doAlso(var2)">
,那么您应该从链接函数中执行以下操作:
onFoo: '&'
因此,在您的HTML中,您可以使用
// assume bar is already in your scope
scope.bar = "yo";
// Now you can call foo like this
scope.onFoo( {extra: 42} );
请注意,您不仅可以访问指令隔离范围(<div on-foo="doSomething(bar, extra)">
)的所有属性,还可以访问调用时添加的额外“本地人”(bar
)。 / p>
像extra
这样的符号看起来像是对我的攻击,这不是使用单向绑定的方法。
注意:单向绑定通常与某些“事件”功能一起使用,例如scope.foo()(value)
,when-drop
,on-leave
,ng-click
等。