Attribute directive with Angular 6 CLI

Attribute directive with Angular 6 CLI

Sometimes you want to apply the same style or behaviour to multiple DOM elements. For example you want to highlight a element while it's hovered with the mouse and unhighlight it when hover is removed. The bad way would be to write the logic in every component. Angular 6 has something called attribute directives which you can define and use in DOM elements. If you look into Angular docs, you will find this explanation:

"An Attribute directive changes the appearance or behavior of a DOM element."

This is exactly what we want!

Create a attribute directive with the Angular 6 CLI

We will use the Angular 6 CLI to create the directive, just enter following line

ng generate directive highlight

The CLI will take care of everything and you should see this output:

CREATE src/app/highlight.directive.spec.ts (236 bytes)
CREATE src/app/highlight.directive.ts (147 bytes)
UPDATE src/app/app.module.ts (490 bytes)

The CLI set the name appHightlight as the selector name. You could also name it highlight but to avoid conflicts with standard HTML attributes it is good practice to use a prefix. And with the prefix you see directly that this attribute directive is one you have created yourself.

We have to import ElementRef to manipulate DOM elements, HostListener to react on system events like mouse enter or leave and Input to set the color from outside as we have learned in the previous chapter. All that comes from the @angular/core library.

import { Directive, ElementRef, Input, HostListener } from '@angular/core';

To get a reference to the DOM element we have to inject ElementRef, and name it for example element, into the directive via constructor injection.

constructor(private element: ElementRef) { }

We need a function which set the style, especially the background color of the nativeElement.

private highlight(color: string) {
    this.element.nativeElement.style.backgroundColor = color;
  }

The color should be set from outside the component, so we need a @Input() decorator property of type string. Let's call it highlightColor

@Input() highlightColor: string;

We need to listen to two system events, one is mouseenter where we will set the color and the other one is mouseleave where we will set the color to default. To listen to system events we have to annotate a function with @HostListener('name-of-the-event') and call our highlight function in it.

@HostListener('mouseenter') onmouseenter() {
  this.highlight(this.highlightColor);
}

@HostListener('mouseleave') onmouseleave() {
  this.highlight(null);
}

With that set, we are able to call our create attribute directive. In post-list.component.html we can add inside the <div> element the selectors name appHighlight and bind the @Input() property highlightColor to the same name for example.

<div appHighlight [highlightColor]="highlightColor" *ngFor="let post of posts">

The property highglightColor will be bound with another @Input decorator inside post-list.component.ts. The reason is, as we have learned from the previous chapter, we will have a single source of truth, a "dumb" display component and the ability to set the color from outside. So, add this to post-list.component.ts:

@Input() highlightColor: string;

and inject the color from app.component.html. You could of course bind the color to your app.component.ts but for the sake of simplicity, i will hardcoded it here.

<app-post-list [posts]="posts" highlightColor="yellow" (clicked)="deleteLastPost()"></app-post-list>

That's it! We have extended our post-list.component, created a generic directive and injected the properties from outside!

The next chapter is coming soon, feel free to leave your feedback or suggestions here. You can find me at Twitter or simply send me an email to tomislav.eric@arconsis.com.