将函数传递给指令以在链接

时间:2015-04-24 21:42:13

标签: angularjs angularjs-directive angularjs-scope

我知道我们通常通过隔离范围将函数传递给指令:

.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)。那么这是一个在函数上使用双向绑定的有效用例,还是我做了一些我不应该做的黑客攻击?

2 个答案:

答案 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-dropon-leaveng-click等。