角度指令能否将参数传递给指令属性中指定的表达式中的函数?


160

我有一个使用callback带有隔离范围的指定属性的form指令:

scope: { callback: '&' }

它位于内,ng-repeat因此我传递的表达式包括id对象的,作为回调函数的参数:

<directive ng-repeat = "item in stuff" callback = "callback(item.id)"/>

当我完成指令后,它会$scope.callback()从其控制器函数中调用。在大多数情况下,这很好,这就是我想要做的,但是有时我想从directive自身内部添加另一个参数。

是否有一个允许这样的角度表达式:$scope.callback(arg2),导致用callback调用arguments = [item.id, arg2]

如果没有,那么最干净的方法是什么?

我发现这可行:

<directive 
  ng-repeat = "item in stuff" 
  callback = "callback" 
  callback-arg="item.id"/>

scope { callback: '=', callbackArg: '=' }

和指令调用

$scope.callback.apply(null, [$scope.callbackArg].concat([arg2, arg3]) );

但我认为这不是特别整洁,它涉及在隔离范围内放入其他内容。

有没有更好的办法?

此处为Plunker游乐场(已打开控制台)。


命名为“ callback =“的属性会误导用户。这实际上是一个回调评估,而不是回调本身。
Dmitri Zaitsev 2015年

@DmitriZaitsev是一个回调角表达式,其结果将为JavaScript函数。我认为很明显,它本身不是JavaScript函数。这只是首选项,但我希望不必在所有属性后缀“ -expression”。这与ngAPI 一致,例如ng-click="someFunction()"是一个表达式,其计算结果为执行一个函数。
Ed Hinchliffe 2015年

我从未见过称为“回调”的Angular表达式。名称始终是您传递给您的函数。您甚至在示例中使用了一个名为“回调”的函数,使事情更加混乱。
Dmitri Zaitsev 2015年

我不确定您是否感到困惑或感到困惑。在我的示例$scope.callback中,由指令定义对象的callback="someFunction"属性和scope: { callback: '=' }属性设置。$scope.callback 以后要调用的函数。实际的属性显然是一个字符串-HTML总是这样。
Ed Hinchliffe

您将属性和功能命名为相同的“回调”。那就是造成混乱的秘诀。真的很容易避免。
德米特里·扎伊采夫

Answers:


215

如果您声明@ lex82提到的回调,例如

callback = "callback(item.id, arg2)"

您可以使用对象映射在指令范围内调用回调方法,它将正确执行绑定。喜欢

scope.callback({arg2:"some value"});

无需$ parse。看到我的小提琴(控制台日志)http://jsfiddle.net/k7czc/2/

更新文档中有一个小例子:

&或&attr-提供一种在父作用域的上下文中执行表达式的方法。如果未指定attr名称,则假定属性名称与本地名称相同。给定和小部件的作用域定义:{localFn:'&myAttr'},然后隔离作用域属性localFn将指向count = count + value表达式的函数包装。通常希望通过表达式将数据从隔离范围传递到父范围,这可以通过将局部变量名称和值的映射传递到表达式包装器fn中来完成。例如,如果表达式为增量(金额),则可以通过将localFn调用为localFn({amount:22})来指定金额值。


4
非常好!这在任何地方都有记录吗?
2014年

12
我不认为这是因为在指令定义一个很好的解决方案,有时候你不知道什么是在传递参数。
OMGPOP

这是一个很好的解决方案,谢谢您,但是我相信答案需要一些帮助。谁是lex82,他提到了什么?
Wtower

有趣的方法。虽然当您要允许传递带有ANY参数(或多个)的任何函数时会发生什么?您对函数及其参数一无所知,因此需要在指令内部的某个事件上执行它。如何去做?例如,在一条指令上,您可能具有onchangefunc ='myCtrlFunc(dynamicVariableHere)'
trainoasis,

58

其他答案没什么问题,但是当在指令属性中传递函数时,我使用以下技术。

在html中包含指令时,请省略括号:

<my-directive callback="someFunction" />

然后在指令的链接或控制器中“解包”该函数。这是一个例子:

app.directive("myDirective", function() {

    return {
        restrict: "E",
        scope: {
            callback: "&"                              
        },
        template: "<div ng-click='callback(data)'></div>", // call function this way...
        link: function(scope, element, attrs) {
            // unwrap the function
            scope.callback = scope.callback(); 

            scope.data = "data from somewhere";

            element.bind("click",function() {
                scope.$apply(function() {
                    callback(data);                        // ...or this way
                });
            });
        }
    }
}]);    

“展开”步骤允许使用更自然的语法调用该函数。它还可以确保即使嵌套在其他可能通过该函数的指令中,该指令也可以正常工作。如果您没有进行解包,那么您将遇到以下情况:

<outer-directive callback="someFunction" >
    <middle-directive callback="callback" >
        <inner-directive callback="callback" />
    </middle-directive>
</outer-directive>

然后,您将在内部指令中得到如下所示的结果:

callback()()()(data); 

在其他嵌套方案中会失败。

我改编自Dan Wahlin的出色文章,网址http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters

我添加了展开步骤,以使调用函数更自然,并解决我在项目中遇到的嵌套问题。


2
一种不错的方法,但是我无法this在回调方法中使用指针,因为它使用了指令的范围。我正在使用Typescript,回调函数如下所示:public validateFirstName(firstName: string, fieldName: string): ng.IPromise<boolean> { var deferred = this.mQService.defer<boolean>(); ... .then(() => deferred.resolve(true)) .catch((msg) => { deferred.reject(false); }); return deferred.promise; }
ndee

1
注意:如果您有嵌套指令,并且想向上传播回调,则需要解开每个指令,而不仅是触发回调。
Episodex

43

在指令(myDirective)中:

...
directive.scope = {  
    boundFunction: '&',
    model: '=',
};
...
return directive;

在指令模板中:

<div 
data-ng-repeat="item in model"  
data-ng-click='boundFunction({param: item})'>
{{item.myValue}}
</div>

在源中:

<my-directive 
model='myData' 
bound-function='myFunction(param)'>
</my-directive>

...在myFunction控制器中定义的位置。

请注意,param指令模板中的指令模板与param源代码中的指令巧妙地绑定在一起,并设置为item


要从link指令的属性内(在指令的“内部”)进行调用,请使用非常相似的方法:

...
directive.link = function(isolatedScope) {
    isolatedScope.boundFunction({param: "foo"});
};
...
return directive;

在源代码中时:bound-function ='myFunction(obj1.param,obj2.param)'>然后如何进行?
Ankit Pandey

15

是的,有更好的方法:您可以在指令中使用$ parse服务在父作用域的上下文中评估表达式,同时将表达式中的某些标识符绑定到仅在指令内部可见的值:

$parse(attributes.callback)(scope.$parent, { arg2: yourSecondArgument });

将此行添加到指令的链接函数中,您可以在其中访问指令的属性。

然后可以设置您的回调属性,callback = "callback(item.id, arg2)"因为arg2通过指令内的$ parse服务绑定到yourSecondArgument。诸如此类的指令可ng-click让您通过$event完全使用此机制通过传递给指令的表达式内部的标识符来。

请注意,您不必callback使用此解决方案成为隔离范围的成员。


3
使用scope.$parent会使指令“泄漏”-它“过多地”了解外部世界,而精心设计的封装组件则不应。
Dmitri Zaitsev 2015年

3
好吧,它知道它有一个父作用域,但是它不访问该作用域中的特定字段,因此我认为这是可以容忍的。
lex82

0

对我来说,以下工作:

在指令中这样声明:

.directive('myDirective', function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            myFunction: '=',
        },
        templateUrl: 'myDirective.html'
    };
})  

在指令模板中,可以通过以下方式使用它:

<select ng-change="myFunction(selectedAmount)">

然后,当您使用指令时,像这样传递函数:

<data-my-directive
    data-my-function="setSelectedAmount">
</data-my-directive>

您通过函数的声明传递函数,并从指令中调用该函数,并填充参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.