RTFC


I really could not think of a better title for this post because it is not just about using an @Input property setter instead of the life-cycle hook ngAfterViewInit. Hence the title is pretty much inspired from RTFM where “Manual” is replaced by “Code”.

It’s about how important it is to read the code.

Just read the code..!

Last month I had published the Angular blog post on NgConf Medium in which I had proposed various ways to use jQuery plugins in Angular. If you have not read it yet, do read it here and comment if any. Unfortunately, I did not get lucky enough to be ngChampions (Kudos to those who become) and hence I have decided to publish the sequel here on my personal blog.

So after publishing the first post, I went on reading the source code for Material Badge component, just casually.

And to my surprise, I noticed 3 profound things:

Structural Directive over Component

It depends on the functionality you want to build into the component. If all you want to do is alter a single DOM then always go for a custom structural directive instead of writing a custom component. Because the custom component mostly introduces its own APIs unnecessarily.

For example, take a look at the app-toolbar-legends component from the last article. Remember, I’m not contradicting myself in this article, however, for this particular jQuery plugin in Angular, we could safely create an Angular Directive rather than having the Angular Component with its own API in terms of class and icon attributes below.

<app-toolbar-legends class="btn-toolbar-success" icon="fa-bitcoin" [toolbarConfig]="{position: 'right'}">
  <div class="toolbar-icons hidden">
    <a href="#"><i class="fa fa-bitcoin"></i></a>
    <a href="#"><i class="fa fa-eur"></i></a>
    <a href="#"><i class="fa fa-cny"></i></a>
  </div>
</app-toolbar-legends>
<app-toolbar-legends class="btn-toolbar-dark" icon="fa-apple" [toolbarConfig]="{position: 'right', style: 'primary', animation: 'flip'}">
  <div class="toolbar-icons hidden">
    <a href="#"><i class="fa fa-android"></i></a>
    <a href="#"><i class="fa fa-apple"></i></a>
    <a href="#"><i class="fa fa-twitter"></i></a>
  </div>
</app-toolbar-legends>

That means we can simplify the usage of the jQuery plugin in Angular by slapping the Angular Directive on the existing markup as follows. There is no need for an extraneous understanding of where class or icon values go in the component template, it’s pretty clear and concise in here. Easy, just slap a directive appToolbarLegends along with the jQuery plugin configurations.

<div class="btn-toolbar btn-toolbar-success" [appToolbarLegends]="{position: 'right'}" >
  <i class="fa fa-bitcoin"></i>
</div>
<div class="toolbar-icons hidden">
  <a href="#"><i class="fa fa-bitcoin"></i></a>
  <a href="#"><i class="fa fa-eur"></i></a>
  <a href="#"><i class="fa fa-cny"></i></a>
</div>


<div class="btn-toolbar btn-toolbar-dark" [appToolbarLegends]="{position: 'right', style: 'primary', animation: 'flip'}">
  <i class="fa fa-apple"></i>
</div>
<div class="toolbar-icons hidden">
  <a href="#"><i class="fa fa-android"></i></a>
  <a href="#"><i class="fa fa-apple"></i></a>
  <a href="#"><i class="fa fa-twitter"></i></a>
</div>

Generate Unique Id for the DOM

I wanted a unique id attribute for each instance of the toolbar in order to map them to their respective toolbar buttons. I’m still laughing at myself for going above and beyond just to generate a unique ID with 0 dependencies. Finally, StackOverflow came to the rescue 😅

Math.random().toString(36).substr(2, 9)

But while reading the source code for Material Badge component, I found an elegant approach that I wish to frame on the wall someday 😂. This will generate a unique _contentId for each instance of the directive without much fuss.

import { Directive } from '@angular/core';
let nextId = 0;
@Directive({
  selector: '[appToolbarLegends]'
})
export class LegendsDirective {
  private _contentId: string = `toolbar_${nextId++}`;
}

@Input property setter vs ngAfterViewInit

Before we get into the getter/setter, let’s understand when and why to use ngAfterViewInit. It’s fairly easy to understand — it is a life cycle hook that triggers when the View of the component or the directive attached to is initialized after all of its bindings are evaluated. That means if you are not concerned with querying the DOM or DOM attributes which have interpolation bindings on them, you can simply use Class Setter method as a substitute.

import { Directive, Input } from '@angular/core';
let nextId = 0;
@Directive({
  selector: '[appToolbarLegends]'
})
export class LegendsDirective {
  private _contentId: string = `toolbar_${nextId++}`;
  @Input('appToolbarLegends')
  set config(toolbarConfig: object) {
    console.log(toolbarConfig); // logs {position: "right"} object
  }
}

The Class Setters are called way before ngAfterViewInit or ngOnInit and hence they speed up the directive instantiation, slightly. Also, unlike ngAfterViewInit or ngOnInit , the Class Setters are called every time the new value is about to be set, giving us the benefit of destroying/recreating the plugin with new configurations.

Demo Day

Thanks for coming this far. So the moral of the story is to do read code written by others, does not matter which open source project it is.

Just read the code..!

https://stackblitz.com/edit/angular-zgi4er?embed=1&file=src/app/app.component.ts
If you found this article useful in anyway, feel free to donate me and receive my dilettante painting as a token of appreciation for your donation.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.