Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

How does directives work in Angular?

At the core, a directive is a function that executes whenever the Angular compiler finds it in the DOM. Angular directives are used to extend the power of the HTML by giving it new syntax. Each directive has a name — either one from the Angular predefined like ng-repeat , or a custom one which can be called anything.

What are the different types of directives in Angular?

The three types of directives in Angular are attribute directives, structural directives, and components.


If you declare your callback as mentioned by @lex82 like

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

You can call the callback method in the directive scope with object map and it would do the binding correctly. Like

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

without requiring for $parse. See my fiddle(console log) http://jsfiddle.net/k7czc/2/

Update: There is a small example of this in the documentation:

& or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given and widget definition of scope: { localFn:'&myAttr' }, then isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it's desirable to pass data from the isolated scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).


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.


In directive (myDirective):

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

In directive template:

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

In source:

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

...where myFunction is defined in the controller.

Note that param in the directive template binds neatly to param in the source, and is set to item.


To call from within the link property of a directive ("inside" of it), use a very similar approach:

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

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.