Learning Angular: Expose an API on your directives
Ever wanted to not only create a nice, visually appealing directive or web component, but to also provide a more rich programmatic API with it? Read on to learn about two potential implementation approaches.
This article is part of my “Learning NG” series, presenting some of my adventures while learning Angular. Check out the series intro and other articles. Note, I’m an Angular newbie, so I’m more than happy for any kind of feedback and improvement suggestions from more experienced people than me.
Problem
Suppose you create a directive intended to be used as a “web component” by multiple projects. That means that your directive has to adapt its behavior/appearence based on its surrounding context. Normally this is done by “configuring” it through proper attributes it exposes
<my-web-component data="vm.someData" callback-fn="vm.someFunction()"> </my-web-component>
However, for more sophisticated scenarios this might not be enough. Often it would be more convenient to have some kind of programmatic API.
Solution 1: use shared services
One possibility is to use Angular services. Generally speaking, it is highly recommended to extract your “logic part” into a dedicated service. This increases the reusability and maintainability of your code.
So you create
someModule.factory('personService', function(){ // implement your service api });
Inside your directive you delegate all the work to the personService
. As you might see, the service now provides you with some “kind of programmatic API” which lets you manipulate how your directive works. That’s a commonly used approach.
Solution 2: export an API object
Another solution is to export an API object from your directive.
app.directive('myWebComponent', function(){ return { ... bindToController: { // the API object you can bind to from the outside api: '=' }, template: ..., controller: function(){ var vm = this; ... // the API of my web component vm.api = { setPerson: function(newPerson){ vm.person = newPerson; } }; }, ... }; });
Note, I’m using the “new” bindToController
option that has been introduced in v1.3 and optimized in v1.4. If you do not know about it yet, you should read this article: Exploring Angular 1.3: Binding to Directive Controllers
As you can see, the directive defines a api
property which is attached to a JavaScript object with functions on it. From the outside of the directive you can now bind to that API object and invoke operate using its functions.
<div ng-controller="MainController as main"> <my-web-component api="main.webComponentApi"></my-web-component> ... <button ng-click="main.changePerson()">Change person</button> </div>
Inside your MainController
you use webComponentApi
:
app.controller('MainController', function(){ var vm = this; // this will be bound to the API object vm.webComponentApi = null; vm.changePerson = function(){ // Invoke the api which at this point should be bound vm.webComponentApi.setPerson({ name: 'Juri' }); }; });
Here’s a Plunkr that demonstrates an implementation of such an API object:
Conclusion
To be honest, I tend to towards the shared services model as it seems to be a much cleaner approach. But there might be situations where you need the “api object” as well.
Do you have other alternatives?? Please let me know in the comments.
Reference: | Learning Angular: Expose an API on your directives from our WCG partner Juri Strumpflohner at the Juri Strumpflohner’s TechBlog blog. |