[Javascript] Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?



Answers

Nothing wrong with the other answers, but I use the following technique when passing functions in a directive attribute.

Leave off the parenthesis when including the directive in your html:

<my-directive callback="someFunction" />

Then "unwrap" the function in your directive's link or controller. here is an example:

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
                });
            });
        }
    }
}]);    

The "unwrapping" step allows the function to be called using a more natural syntax. It also ensures that the directive works properly even when nested within other directives that may pass the function. If you did not do the unwrapping, then if you have a scenario like this:

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

Then you would end up with something like this in your inner-directive:

callback()()()(data); 

Which would fail in other nesting scenarios.

I adapted this technique from an excellent article by Dan Wahlin at http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters

I added the unwrapping step to make calling the function more natural and to solve for the nesting issue which I had encountered in a project.

Question

I have a form directive that uses a specified callback attribute with an isolate scope:

scope: { callback: '&' }

It sits inside an ng-repeat so the expression I pass in includes the id of the object as an argument to the callback function:

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

When I've finished with the directive, it calls $scope.callback() from its controller function. For most cases this is fine, and it's all I want to do, but sometimes I'd like to add another argument from inside the directive itself.

Is there an angular expression that would allow this: $scope.callback(arg2), resulting in callback being called with arguments = [item.id, arg2]?

If not, what is the neatest way to do this?

I've found that this works:

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

With

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

and the directive calling

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

But I don't think it's particularly neat and it involves puting extra stuff in the isolate scope.

Is there a better way?

Plunker playground here (have the console open).




Yes, there is a better way: You can use the $parse service in your directive to evaluate an expression in the context of the parent scope while binding certain identifiers in the expression to values visible only inside your directive:

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

Add this line to the link function of the directive where you can access the directive's attributes.

Your callback attribute may then be set like callback = "callback(item.id, arg2)" because arg2 is bound to yourSecondArgument by the $parse service inside the directive. Directives like ng-click let you access the click event via the $event identifier inside the expression passed to the directive by using exactly this mechanism.

Note that you do not have to make callback a member of your isolated scope with this solution.




Links