Creating an 'API' for a directive

46 views
Skip to first unread message

John Urberg

unread,
Mar 27, 2015, 12:18:03 PM3/27/15
to ang...@googlegroups.com
I have many cases where a directive I'm creating needs to provide methods a controller can call.  For example, I need to implement a reusable dialog and supply an open method a controller can call to open the dialog.  I may also need to register a listener to my directive to get a callback when an event occurs. 

What I'd like to do is treat my directive as a component and define an API to it.  My API should have read only input parameters, methods the directive makes available and a way to regiser callback methods. It doesn't seem that Angular 1.x provides an easy way to do this.  Here are some options I've tried:

1) Provide an 'options' object to the directive.  The options object is created in the controller and any callback methods are added, as well as any paramters.  The directive will add methods to the options object that the controller can call.  I've seen this used in a few examples on the web.  It makes for a simple interface to the directive but the API is not easy to determine when you see a directive used in a template.  Example: http://jsbin.com/muluko/2/edit?html,js,output

2) Use isolated scope values.  Input parameters are either string or two-way scope variables.  API methods are two-way scope parameters; the controller provides the name of the scope variable and the directive sets it.  Callbacks are either two-way or function scope variables which the controller can pass a method to call.  Example: http://jsbin.com/lijuso/4/edit?html,js,output

What are your thoughts on these options?  Are there other better ways to acomplish this?

Thanks,
John

Sonny Michaud

unread,
Mar 27, 2015, 12:34:12 PM3/27/15
to ang...@googlegroups.com
I did something similar to an amalgamation of your two options, with really good success.  I was using angular-ui-bootstrap tabs, which have sort of a messy API in that the directives expose many more scope variables than seem necessary, and it is actually pretty easy to set things up in a way that would generate conflicts.

What I ended up doing is only exposing a single boolean value to the directive, that it is free to manipulate:
https://github.com/sonnym/fics_web_interface/blob/master/app/templates/chat.ejs#L1

I put all the logic for how tab changes are handled down in the service layer:
https://github.com/sonnym/fics_web_interface/blob/master/app/assets/js/fics.js#L21-L28
https://github.com/sonnym/fics_web_interface/blob/master/app/assets/js/factories/chat.js#L62-L79

And I wrote an abstraction that observes changes propagated from the directive and just does the right thing:
https://github.com/sonnym/fics_web_interface/blob/master/app/assets/js/factories/tab_manager.js
https://github.com/sonnym/fics_web_interface/blob/master/app/assets/js/factories/activity_notifier.js

It leverages Object.defineProperty to define the "active" state, which allows for methods to be hooked into the changing of that boolean with ease.  I wrote about that methodology here:
https://sonnym.github.io/2015/01/21/using-defined-properties-for-better-angularjs-directives/

I hope that is somehow informative!

- Sonny
--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, 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.

Sander Elias

unread,
Mar 28, 2015, 1:38:15 AM3/28/15
to ang...@googlegroups.com

Hi John,

This is discussed in this group before. There are more solutions, but in my eyes, exposing the directives controller to the scope, via controllerAs has the cleanest interface. Just a few days ago I did adapt a small sample for another question.
If you want/need more information about this, just ask!

Regards
Sander

John Urberg

unread,
Mar 29, 2015, 2:48:16 PM3/29/15
to ang...@googlegroups.com
We've tried exposing the directive's controller as the API.  It's made me uncomfortable for a couple of reasons: 1) There are often a number of methods on the directive's controller that are only used by the directive itself and we don't want those available to the outside world and 2) when you see a directive in a template, you have to go look up the directive source to understand what the API is.  

Sander Elias

unread,
Mar 29, 2015, 11:52:53 PM3/29/15
to ang...@googlegroups.com
Hi John,
 
1) There are often a number of methods on the directive's controller that are only used by the directive itself and we don't want those available to the outside world and
This is indeed something you might encounter. There are way's around this. For example, put those methods inside the link function, and attach them to the isolate scope. They still will be available to your controller, just less visible. As often, building a system is handling the trade-offs you are comfortable with.
 
2) when you see a directive in a template, you have to go look up the directive source to understand what the API is.  
Hmm, I would say you are in need of some documentation. This is the same issue you had with your initial point 1. Building an API for something brings the need for some documentation with it.

If the above trade-offs are not acceptable for you, you can go indeed with your option 2. However, if you want to create directives that work together, this is going to be much harder. Have another look at the sample I linked in. There are 3 directives in the template that work as a single API. (as this was a code-sample from somebody else, it's not exactly how I would name/do things normally!) The clearSet directive handles as a group holder, the other 2 interact via that one. 
I believe a directive should have a single purpose. then you can combine those to achieve complex behaviors. 

Hope this helps you a bit,
Regards
Sander

Reply all
Reply to author
Forward
0 new messages