Angular Way to control a Directive from Controller?

37,559 views
Skip to first unread message

Olivier Clément

unread,
Oct 31, 2012, 4:54:17 PM10/31/12
to ang...@googlegroups.com
Hi all,  I have a newb question here;

Let's say I have a Directive in which I define a few functions that will affect its behavior/presentation,
but I want to be able to call these functions from outside the directive (the page's controller for instance);

What would be the best approach? 

Thanks

Andy Joslin

unread,
Oct 31, 2012, 11:00:01 PM10/31/12
to ang...@googlegroups.com
Here's a few ways: http://jsfiddle.net/Mve2e/

Olivier Clément

unread,
Nov 1, 2012, 9:36:46 AM11/1/12
to ang...@googlegroups.com
Thanks Andy, will look at, I'm sure it will help!

Olivier Clément

unread,
Nov 1, 2012, 10:44:51 AM11/1/12
to ang...@googlegroups.com
Hum ok so after fiddling a bit with all this I think I am either missing something, or something else (!)

What's closest to what I need in your examples would be the "Joe" Example:

...Controller:
        $scope.sayHiJoe function({
            $scope.heyJoe();
        };
...

...Directive:
        linkfunction(scopeelmattrs{
            scope.heyJoe function({
                console.log('k');
                elm.text((elm.text()||"hey joe")+"!");
            };
        }
...

However, I think I cannot use this method

Because of, I guess, isolated scope:
The Directive's "scope" and Controller's "$scope" vars are not the same in my case.
So doing $scope.heyJoe() wouldn't work for me;


Also, the function I want to call has to be defined in the directive, so the two other methods in your example are a no go...

Open the console

Any other ideas?

Thanks

Brian Chapman

unread,
Nov 1, 2012, 11:10:15 AM11/1/12
to ang...@googlegroups.com
Here is one way of doing what you are looking for (http://plnkr.co/edit/yiq9Cd).

Brian

Olivier Clément

unread,
Nov 1, 2012, 11:25:06 AM11/1/12
to ang...@googlegroups.com
Thanks Brian,

I'd say this is a clever solution, but isn't this a bit hackish? 
I thought there would be a cleaner way to do so... Or is this the kind of use-case Angular doesn't care about? 

Considering all the answers I get gravitate around the $watch (which I would have liked to avoid in that particular case), I'm wondering if I should rethink how I want to do that...

Thanks all for your inputs

Peter Bacon Darwin

unread,
Nov 1, 2012, 11:25:39 AM11/1/12
to ang...@googlegroups.com

Currently this is not really possible in AngularJS.

The best option you have, which is what Brian is suggesting is to expose a configuration object on your directive which the directive watches and reacts it changes. You can pretty much do all you need with this strategy.

Another alternative is for your directive to listen for a specific event on the scope and for your "controlling external object" to broadcast the event down the scope stack to it. This doesn't work so well if you have multiple child directives listening as you may not know which directive should react.

... sent from my tablet

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular?hl=en.
 
 

Olivier Clément

unread,
Nov 1, 2012, 1:49:09 PM11/1/12
to ang...@googlegroups.com
Thanks for the clarification
I will reevaluate my needs and see what's best to do in that case then

Thank you again to all

Witold Szczerba

unread,
Nov 1, 2012, 6:49:37 PM11/1/12
to ang...@googlegroups.com

I would suggest the scope.$on/$broadcast way or using a service with scope.$watch in directive, because it seems to be easily testable and clear for the reader of the code.

Imagine writing expectations in unit tests of your controller. It processes some business logic and eventually sends message to the "outside" world by broadcasting a message or invoking service's method (which you mock).

Regards,
Witold Szczerba
---
Sent from my mobile phone.

Josh Kurz

unread,
Nov 2, 2012, 2:31:47 AM11/2/12
to ang...@googlegroups.com
Is this a case where the new Object Observe would be useful? 

Sent from my iPhone

Greg Weber

unread,
Nov 3, 2012, 9:32:32 AM11/3/12
to ang...@googlegroups.com
Without knowing more specifics of the code you are writing, it is hard to judge the best solution.
The ideal would be to include the controller in the directive (with the controller attribute of the directive), but it seems we are all assuming this is not possible.
An alternative to communicating through events is to use a service. If you have multiple instances of a directive that need their own data, the service would need to expose a hash accessed by a key. The controller would need to tell the directive what the key name is. Broadcasting events downward avoids that, so might be better in this case as long as the directive does not have any recursion.

Olivier Clément

unread,
Nov 5, 2012, 1:44:54 PM11/5/12
to ang...@googlegroups.com
Sorry for the late response;

Thank you all for your inputs; I thought about using a Service, but considering I won't ever need more than one instance of that particular directive at a time, I think it might be a bit overkill to create a service for this;

I guess that the solution that would suits my needs the best would be Witold's suggestion of using $on/$broadcast

I'll get into this right now;

--

As for more details:

The directive is a D3.js Tree that display's an organisation.
Outside of that directive I have a few controls that will modify the tree accordingly. I need to be able, on the click of a button or similar (outside the directive, under the same parent controller), to fire a function living in the directive and pass some parameters to it. That function will take a list of nodeID as params, and the directive will be responsible of getting JSON from a WebService and inject this data in the tree

Corentin Kerisit

unread,
May 26, 2013, 5:45:54 PM5/26/13
to ang...@googlegroups.com
Hello Olivier,

I know its been a while since you had that issue but I would really love to hear how you managed to control the directive from the controller.
I am facing such issue with a directive embeding a Three.js canvas. I have a listing of effects off a webservice. I need to apply these effects to the canvas on ng-click.

I was thinking of the $on/$broadcast. Is that what you used in the end ?

Thanks

Olivier Clément

unread,
May 26, 2013, 7:14:23 PM5/26/13
to ang...@googlegroups.com
Hi
I don't remember quite well as it been a while indeed;

Also, I must say my comprehension and understanding of angular has evolved since too.

If you need to apply a different set of effects by clicking different buttons for instance, maybe you could just use $scope.$watch from within the directive, and binding the effects you get from the server to a scope property on that same directive.

Basically you could have <directive ng-model="whatever" effects="effectList">

effectList being a property in you controller's scope or something. In the directive you'd need to define this property to its scope: { effects: "="}

Hope this help!
--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/htKUVVNhi7A/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.

Oliver Wienand

unread,
Aug 28, 2013, 8:18:39 AM8/28/13
to ang...@googlegroups.com
I had the same problem and used the following solution using bi-directional binding ('=') of a variable from the controller scope. In this way you can control also several instances of the same directive on a page.

I created a very short plunker example for reference.

Best regards,

Oliver Wienand

Corentin Kerisit

unread,
Aug 28, 2013, 8:51:03 AM8/28/13
to ang...@googlegroups.com
Thanks !


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.

To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.

Christian Maniewski

unread,
Sep 23, 2013, 1:00:11 PM9/23/13
to ang...@googlegroups.com
Hey, thank you very much for your example. I do not quite understand it. Why would you need the

      scope:    {
        clicked:   '='
      },

lines? For me it works without them and without assigning the clicked attribute.

Sebastien Vincent

unread,
Sep 23, 2013, 1:29:02 PM9/23/13
to ang...@googlegroups.com
It works because your directive is using the controller scope directly, but is not ideal, cause the directive is too tightly coupled with the controller. The bidirectional binding option is better in most cases, cause the scopes stay segregated, and you can control the binding via the attribute (bit like ng-model). As Oliver this also allows for the 2 instance of the directive being used at the same time by the same controller.

This is all explained in the directive doc. 
Message has been deleted

Daniel Pedro dos Santos Fernandes

unread,
Sep 25, 2013, 10:29:47 AM9/25/13
to ang...@googlegroups.com
Hi all,

My way to do that is use events.

When you need (and you will need) handle the DOM (keep in mind: - 'YOU SHOULD HANDLE THE DOM JUST ON DIRECTIVES') based on a scope status you can use $scope.$emit('EVENT_NAME') on controller and scope.$on('EVENT_NAME', function() { ... }) into directive.

If you need some example, please take a look here http://jsfiddle.net/Fb49w/1/ I did this little code to show what I'm talking about.
Message has been deleted

Brian Ingles

unread,
Jun 2, 2014, 8:57:13 AM6/2/14
to ang...@googlegroups.com
You could always create a basic pub / sub type of class that broadcasts a message to any listeners.

var EventTrigger = function() {
    
    var _listeners = [];
    
    var _trigger = function() {
        
        if(_listeners) {
            _listeners.forEach(function(listener) {
            
                 listener();
            });
        }
    };
    
    var _listen = function(listener) {
        
        _listeners.push(listener);
    }
            
    return {
        trigger: _trigger,
        listen: _listen
    };
}

Then you can instantiate an instance on your controller $scope:

$scope.SomeEventTrigger = new EventTrigger();

and raise an event:

$scope.SomeEventTrigger.trigger();

Then in your directive you can bind to the instance using the '=' binding on your isolated scope:

scope: {
    EventTrigger: '=eventTrigger'
}

and add a listener to the instance:
$scope.EventTrigger.listen(function() {
   $scope.directiveFunctionIWantToCall();
});


Here's a jsfiddle showing a working example:

Iain Duncan

unread,
Jun 2, 2014, 8:53:30 PM6/2/14
to ang...@googlegroups.com
I could be off base here, but another approach is to use the "controller as" option in your directive and save what you want accessed in the directive as attributes of "this" in the controller instead of on scope. 

Would be interested in hearing from others more experienced in Angular if there is a reason not to do the above too.

Iain

Sander Elias

unread,
Jun 3, 2014, 2:06:33 AM6/3/14
to ang...@googlegroups.com
Hi Iain,

I did this a couple of times. Works like a charm. You can design the controller so, that it can serve as an API for your directive.

Regards
Sander

Daniel Pedro dos Santos Fernandes

unread,
Jun 3, 2014, 7:10:29 AM6/3/14
to ang...@googlegroups.com
Awesome, I'll do a try on that.
The design is great.

Thanks for share with us.


Daniel Pedro dos Santos Fernandes 
-----
Phone: +55 (35) 3422-7343
Mobile: +55 (35) 8701-0095
E-maildani...@gmail.com | daniel.pedr...@hotmail.com 

=======================================
Turn from evil and do good; seek peace and pursue it.
Psalm 34:14 
=======================================


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/htKUVVNhi7A/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

David Jensen

unread,
Jul 29, 2014, 5:37:18 AM7/29/14
to ang...@googlegroups.com
Do you have an example of this working that you can share?

Thanks.

Christian Maniewski

unread,
Jul 30, 2014, 3:01:32 PM7/30/14
to ang...@googlegroups.com
That is a really great idea! As stated by others already it'd be nice if you shared an example with us :)

Thank you very much.

Chris

Daniel Pedro dos Santos Fernandes

unread,
Jul 30, 2014, 3:15:46 PM7/30/14
to ang...@googlegroups.com
Hi @Chris and @David.


So, you can check a very simple way to deal with it here: http://jsfiddle.net/Fb49w/1/

Cheers,
Daniel.

Javier Lerones Gallego

unread,
Oct 13, 2014, 8:23:48 PM10/13/14
to ang...@googlegroups.com
A little late, but I believe this is the way they meant to do this

Just use the controllerAs syntax to bind the name of the directive's controller to a scope property in the parent controller, then you can access all the directive's methods from its parent. Very useful when you need to trigger those methods from controls outside of the directive.

For example let's say you have a calendar directive somewhere in the page, and you want to put month/year navigation buttons somewhere else in your views

I hope this is helpful
Javier

barroudjo

unread,
Feb 13, 2015, 10:34:26 AM2/13/15
to ang...@googlegroups.com
Just to let you guys know, I tried this plunker with angular 1.3.10 and this technique doesn't appear to work anymore.
However I found a similar way to obtain the same result: 

Kon M

unread,
Dec 5, 2015, 5:18:01 AM12/5/15
to AngularJS
Really Sorry about the extremely late response to this thread. 
Olivier, originally you had recommended using events ($on / $broadcast) and in the new update yo have mentioned using $watch based on evolved understanding of Angular. Please would it be possible to elaborate why the $watch would be preferable to events i.e. any performance or hidden issues with events?

Unfortunately Angular Directives don't have a "delegation" model where a parent controller can delegate/command a child directive to do something, with the guarantee that it will get done. All the currently available approaches are like a "best effort" where it is really up to the directive to do something, or not.

Assuming a parent has to notify a directive of a change in the data, and the directive needs to update its view. Using $watch on a scope property in the directive seems hacky as $watch or $watchCollection can sometimes become inefficient if the watched property is a deeply nested array. 
One could create a separate "eventCounter" and keep incrementing it each time a new update is to be triggered & watch it in the directive, but again it doesn't feel like the right way to do it. I was of the opinion that events are the "best" way to achieve this, hence would appreciate your feedback on the above question.

Olivier Clément

unread,
Dec 8, 2015, 3:43:24 PM12/8/15
to AngularJS
Hi,

I haven't followed this topic in quite a while and am not as active on those groups as I was before. 
It really is a stroke of luck that I saw your message today :P

I don't quite remember the initial problematic, the what and the why, etc. Could you maybe explain what you're trying to achieve?

Since the last few years, I found that the community as a whole "evolved" quite a bit about best practices, how to approach those kind of problems, etc

If I may suggest, you might want to update your understanding/knowledge of angular to follow the same path; The current direction being what we could call the "Component Approach" which is closer to where WebComponents/Polymer/React/Angular 2 are going. The big idea is to have dumb components (directives) that receives only the data they need from their parents and so forth. To that effect, the Flux pattern ("pioneered" by Facebook with React)  is getting more popular in that effect (but is definitely not an absolute).

It is "now" common place to consider that using explicit $watch in your controllers/directive might reflect a bad design, and it is recommended to avoid them whenever possible, granted you sometime won't have much choices (or should I say, the effort in developing the alternative solution might be too long/costly to go that route due to time constraints for example)
The same goes in using the $scope service, 

Here's a few articles that I found useful that might shed some light (there are many others, don't be shy to use google!):

And John Papa's style guide for Best Practices is quite good too:

Following those idea should simplify how you're going to build your application in the long run

I'm sorry for not providing you with a more concrete answer to your question, but I think this will at least give you some tools to find a better solution to it and many others you might encounter (Teach a hungry man to fish and all that jazz?). I have to admit that part of the reason for this answer is due to me being in a rush right now and not really taking all the time I wish I could to give you a more proper answer

In all cases, I hope this help at least a bit!

O.
Reply all
Reply to author
Forward
0 new messages