Florin Vistig
Florin Vistig

Reputation: 1729

AngularJS call method from an ancestor scope inside directive

I have an Angular app where I'm using ui-grid. I want to have a custom action on a cell of the grid that calls a method from my app. So basically, this means calling a method that's somewhere up in the parent hierarchy, from a directive.

This would be achieved by calling something like: $scope.$parent.$parent.$parent.$parent.foo(). But that doesn't seem too nice.

One option would be to create a recursive function that goes up the ancestry of the $scope. That's nicer, but still seems a bit weird.

Also... Is it good practice to try to achieve something like this?

Upvotes: 2

Views: 492

Answers (2)

road2victory
road2victory

Reputation: 496

You can achieve the same by using "&" through one of the scope variable in directive.Like this, you can bind your event to the controller method and from the method, you could do your desired things or if the original business logic which you wants to achieve on onClick of the grid is used across many modules than you can bisect it in service and make it reusable and call the service from the event method. Let me know if you do have any doubts with the approach.

Key Code of example:

Html

   <my-component attribute-foo="{{foo}}" binding-foo="foo" isolated-expression- foo="updateFoo(newFoo)" >

Directive

    var myModule = angular.module('myModule', [])
.directive('myComponent', function () {
    return {
        restrict:'E',
        scope:{
            /* NOTE: Normally I would set my attributes and bindings
            to be the same name but I wanted to delineate between 
            parent and isolated scope. */                
            isolatedAttributeFoo:'@attributeFoo',
            isolatedBindingFoo:'=bindingFoo',
            isolatedExpressionFoo:'&'
        }        
    };
})

Upvotes: 1

sma
sma

Reputation: 9597

You're correct that $parent.$parent.$parent is definitely not a good practice.

If the method you're calling is another directive, you can require that directive in your child directive and then, the parentDirective's controller function will be injected as the fourth parameter to your link function:

In your DDO:

return {
    require : '^parentDirective',
    restrict : 'E',
    link : function (scope, elem, attrs, parentDirectiveController) {}
}

If what you're trying to call is on a factory/service, you can inject that factory/service into your directive, although this sometimes is a code smell, depending on what you're trying to inject.

Finally, another way to do it is to use event propagation. From your directive, you can use $scope.$emit to send information up to parent controllers:

From the directive:

$scope.$emit('directiveDidStuff', {
    data : 'blah'
});

In the parent controller:

$scope.$on('directiveDidStuff', function (evt, params) {
    this.data = params.data;  // equals blah
});

Upvotes: 4

Related Questions