require to Host

Pankaj Parkar
ngconf
Published in
5 min readMay 3, 2019

--

Gentle guide to migrate require directive option to Angular

Wait! You must be wondering What the heck the title is? Don’t worry, bare with me, Its part of migrating AngularJS code to Angular.

There are many questions asked on Stack Overflow about “Should I write my directive code in link or controller function”? Oh! isn’t that interesting question. Great! The answer is pretty simple,

  1. Use controller when you want to share your directive code as an API to child directives. We can say that its helps to communicate between two directives.
  2. Code can be written in link function when you want to write self contained directive.

Now the question is “How the child directives will access parent directives controller code?”. Its way simple, you can use require option applicable on directive/component API, thereafter inside link function you can inject 4th parameter as ctrl and get access to parent directive controller from there. There is provision to get access to method of more than one controller. In case of you need access to multiple controllers, you will receive array in ctrl of link function.

Lets assume scenario about “how tabs works?” Basically we use tabs to segregate same kind of data into different sections. And show them accordingly on specific information by tabs. So in angular you can think of creating two directives, one can be called as tabs and other can be named as tab . There would be two main functionalities.

  1. Add tab under tabs section.
  2. Control selection of the tab.

While architecting above, we can think of placing this functionalities tab component itself, and that will of course work. But eventually it will replicate same as the number of times tab component repeats. It clearly violate the DRY(Do Not Repeat Yourself) principle. To fix this, we can think of placing this functionality at common place. Rather it would be great if we can fit in this functionality to tabs component. That’s an great idea! Look at the below code written in angularjs

tabs and tab directive code

tabs directive looks pretty simple, as we decided we kept select and addPane method which hold main stream functionality. panes variable hold all tab ‘s. Most important thing to be note here is, require: ‘^^tabs’ which says angularjs to make available the controller of tab directive.

Grammar behind ^ in require option

  1. ^tabs = Immediate parent directive must be tabs
  2. ^^tabs = tabs directive must appear in ancestor of directive

Directives html look like below namely tabs.html and tab.html

tabs.html and tab.html

We have also used transclusion, where transcluded content of each tab have been projected inside our template using ng-transclude directive and transclude: true option in directive. Since we’re focusing on require option of directive API, we are not going to cover what transclusion is.

see tabs example running (angularjs)

Awesome! We see tabs are working great. What if we want to migrate the same code in Angular? Your first investigation 🧐 would run in search of require alternative in Angular.

Yes, there is an alternative. Namely it is known as Host decorator in Angular. It can be used as to decorate constructor parameters such as Component/Directive/Service, and it traverse component/injector tree from current component towards root component in search of Component / Directive / Provider ( search happens from bottom to top like searching property in prototypal chain).

Well, lets start the migration of tabs and tab directive to Angluar, so that we can use Host . Sadly we can’t directly use Host decorator in AngularJS app. How we can migrate this directive and replicate the same behaviour with Angular application. To do the same we have to follow some migration rules.

Migration Rules from AngularJS to Angular

  1. Use controllerAs pattern (remove of $scope )
  2. Introduce Typescript in AngularJS app (skipping this)
  3. Migrate directive to component , use component API.
  4. Migrate to Angular Application and use Host decorator.

There we have to do much more than this in real world application, but for our application above steps are suffice. I’d think of writing covering migration in different article.

1. Use controllerAs pattern (ng-controller=”myCtrl as vm”)

Migration seems pretty simple, added controllerAs alias and bindToController option on both the directives and created their own controllers. Only interesting thing that you can see is, link function still exist, its required because you can’t directly access require directive controllers inside controller function. There are workaround to do it, but those are not healthy solutions.

Directive template has minimal changes, where we used controller alias where $scope variable was being used. See it in action in this below plunker

2. Introduce Typescript in AngularJS app (skipping this).

3. Migrate directive to component

Now we’re going to use Component API release in AngularJS 1.5 version to migrate our Directive ‘s to Component ‘s, so that they can look similar as that of Angular Component.

Used component API

Migration looks pretty simpler, scope and bindToController options have been replaced by bindings variable. Now require becomes object, earlier it can be defined as string | [string] type. Interesting part is we have $onInit hook to call addPane method when tab component is initialized.

Surprisingly there are no changes in the template. Have a look at below plunker

Hola! We’re done with whatever we supposed to do from angularjs side. Lets move on to the Angular part. Let’s move on to the final step.

4. Migrate to Angular Application and use Host decorator.

It’s hard to say migration, we can call it as re-write, but it is worth doing.

TabsComponent don’t have much to explain, only ng-transclude is replaced with ng-content , rest is pretty much simple.

TabComponent is few interesting things

  1. ngOnInit used for $onInit
  2. Used Input binding for details , instead of scope: {..} (isolated scope)
  3. Host decorator inspite of require

TLDR;

In case there is no compulsion of having Host component, we can easily handle this situation by putting Optional decorator before Host decorator.

constructor(@Optional() @Host() private tabsCtrl: TabsComponent) { }

Takeaway

I hope you have get to know about how to migrate angularjsrequire directive option code to Angular using Host decorator. It seems far easier in Angular than former version.

If you like this article press 👏 clap button 50 times or as many times you want. Feel free to ask a question if you have any. Thanks a lot for reading!

EnterpriseNG is coming

EnterpriseNG is a two-day conference from the ng-conf folks coming on November 19th and 20th. Check it out at ng-conf.org

--

--