Anatomy of a custom directive

Declaring and implementing a custom directive is pretty easy. We just need to import the Directive class to provide decorator functionalities to its accompanying controller class:

import { Directive } from '@angular/core';

Then, we define a controller class annotated by the @Directive decorator, where we will define the directive selector, input and output properties (if required), optional events applied to the host element, and injectable provider tokens, should our directive's constructor require specific types to be instantiated by the Angular injector when instancing itself (we will cover this in detail in Chapter 6, Building an Application with Angular Components):

Let's warm up by creating a very simple directive:

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

@Directive({
selector : '[highlight]'
})
export class HighLightDirective {
constructor( private elementRef: ElementRef, private renderer : Renderer2 ) {
var nativeElement = elementRef.nativeElement;
this.renderer.setProperty( nativeElement,'backgroundColor', 'yellow');
}
}

And to use it is as simple as typing:

<h1 highlight></h1>

We use two actors here, ElementRef and Renderer2, to manipulate the underlying element. We could use elementRef.nativeElement directly, but this is discouraged as this might break server side rendering or when interacting with service workers. Instead, we do all manipulations using an instance of Renderer2.

Notice how we don't type the square bracket, but only the selector name.

A quick recap of what we did here was to inject the ElementRef and access the nativeElement property, which is the actual element. We also put a @Directive decorator on a class just like we do with components and pipes. The main mindset to have when creating directives is to think reusable functionality that not necessarily relates to a certain feature. The topic chosen previously was highlighting, but we could build other functionalities such as tooltip and collapsible or infinite scrolling features with relative ease.

Properties and decorators' such as selector@Input(), or @Output() (same with inputs and outputs) will probably resonate to you from the time when we overviewed the component decorator spec. Although we haven't mentioned all the possibilities in detail yet, the selector may be declared as one of the following:

  • element-name: Select by element name
  • .class: Select by class name
  • [attribute]: Select by attribute name
  • [attribute=value]: Select by attribute name and value
  • not(sub_selector): Select only if the element does not match the
    sub_selector
  • selector1, selector2: Select if either selector1 or selector2 matches

In addition to this, we will find the host parameter, which specifies the events, actions, properties, and attributes pertaining to the host element (that is, the element where our directive takes action) that we want to access from within the directive. We can therefore take advantage of this parameter to bind interaction handlers against the container component or any other target element of our choice, such as window, document, or body. In this way, we can refer to two very convenient local variables when writing a directive event binding:

  • $event: This is the current event object that triggered the event.
  • $target: This is the source of the event. This will be either a DOM element or an Angular directive.

Besides events, we can update specific DOM properties that belong to the host component. We just need to link any specific property wrapped in braces with an expression handled by the directive as a key-value pair in our directive's host definition.

The optional host parameter can also specify static attributes that should be propagated to a host element, if not present already. This is a convenient way of injecting HTML properties with computed values.

The Angular team has also made available a couple of convenient decorators so that we can more expressively declare our host bindings and listeners straight on the code, like this:

@HostBinding('[class.valid]')
isValid: boolean; // The host element will feature class="valid"
// is the value of 'isValid' is true.
@HostListener('click', ['$event'])
onClick(e) {
// This function will be executed when the host
// component triggers a 'click' event.
}

In the next chapters, we will cover the configuration interface of directives and components in more detail, paying special attention to its life cycle management and how we can easily inject dependencies into our directives. For now, let's just build a simple, yet powerful, directive that will make a huge difference to how our UI is displayed and maintained.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset