Angular ~ Directive - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Introduction
    • Component - directive with template
    • Attribute directive - change behaviour like [ngClass], [ngStyle] etc.
    • Structural directive - change DOM structure like *ngIf, *ngFor, ngSwitch etc.
  • Create a directive
    • Simple class with @Directive decorator
    • Configuration
      • selector
  • Register a directive into ngModule
  • Use a directive
  • ElementRef, Renderer by Dependency Injection
  • @HostBinding and @HostListener

Introduction

There are three kinds of directives in Angular: components, attribute directives and structural directives.

Components are directives, but directives with views and templates.

Structural directives add or remove elements from the DOM. *ngIf, *ngFor and *ngSwitch are examples of built-in structural directives. The directive name is prepended with * to skip having to define a and have the directive use the element it’s attached to as the template.

Attribute directives are used to change the styling or behaviour of elements.

The Angular team recommends using directives as attributes, prefixed with a namespace.


Defining the directive class

We create directives by annotating a class with the @Directive decorator.

The convention is to associate a directive to an element via an attribute selector, that is the name of the attribute wrapped in [].

When the directive gets created Angular can inject an instance of something called ElementRef into its constructor by Dependency Injection. The ElementRef gives the directive direct access to the DOM element upon which it’s attached. It is a wrapper for the actual DOM element which we can access via the property nativeElement.

Angular team has provided a platform independent way of setting properties on our elements via something called a Renderer. So via a renderer we can interact with and change certain properties of the element.


Difference between Renderer and ElementRef

The Renderer is a class that is a partial abstraction over the DOM. Using the Renderer for manipulating the DOM doesn't break server-side rendering or Web Workers (where direct access to the DOM would break).

ElementRef is a class that can hold a reference to a DOM element. This is again an abstraction to not break in environments where the browsers DOM isn't actually available. If ElementRef is injected to a component, the injected instance is a reference to the host element of the current component.

Renderer and ElementRef are not "either this or that", but instead they have to be used together to get full platform abstraction.

Renderer acts on the DOM and ElementRef is a reference to an element in the DOM the Renderer acts on.


@HostBinding and @HostListener

@HostBinding and @HostListener are two decorators in Angular that can be really useful in custom directives.

A directive can link an internal property to an input property on the host element using the @HostBinding decorator. The @HostBinding decorator takes one parameter, the name of the property on the host element which we want to bind to.

The @HostListener is a function decorator that accepts an event name as an argument. When that event gets fired on the host element it calls the associated function.


Inputs & Configuration

Configure directives with standard input property bindings.

To make the syntax look similar to the built-in directives we use an alias for the @Input decorator to match the directives selector.

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

@Directive({
	selector: '[appDropdown]'
})
export class DropdownDirective implements OnInit {
	/* -------------------- Properties -------------------- */
	@Input('appDropdown') appDropdown: boolean;
	@HostBinding('class.open') isOpen: boolean = false;

	/* -------------------- Constructor & Life-cycle -------------------- */
	constructor(){
	}
	ngOnInit(){
	}

	/* -------------------- Methods -------------------- */
	@HostListener('click') toggleOpen(event: Event){
		this.isOpen = !this.isOpen;
	}
}