Directives - deependhamecha/angular GitHub Wiki

Assigning value to directive

<div appDude="red"></div>
@Input('appDude') appDude: string;

Get Form Control Value inside Directive

<input type="text" [formControl]="someFormControl" myCustomDirective />
constructor(
  private el: ElementRef,
  private control: NgControl
) {}

ngOnInit() {
  const abstractControl = this.control.control;
  abstractControl && abstractControl.setValidators([Validators.required]);
}

Structural Directive (unless)

@Input() set unless(condition: boolean) {
    if(!condition) {
        this.vcRef.createEmbeddedView(this.templateRef);
    } else {
        this.vcRef.clear();
    }
}

constructor(private  templateRef: TemplateRef<any>, private vcRef: ViewContainerRef) { }

Directives in Depth

Directive has only OnInit and OnDestroy lifecycle because it does not have view.

@Directive({
  selector: 'appDude', // [appDude] OR .appDude
})
import { Directive, ElementRef, OnInit } from '@angular/core';

// Attribute Directive
@Directive({
  selector: '[appMyDirective]'
})
export class MydirectiveDirective implements OnInit {
  constructor(private elementRef: ElementRef) {}

  ngOnInit() {
    // This aint a good practice, check BetterHightlight directive
    this.elementRef.nativeElement.style.backgroundColor = 'green';
    this.elementRef.nativeElement.style.color = 'white';
    this.elementRef.nativeElement.style.padding = '10px';
  }
}

HostListener

If you need to link events on which directive is used, eg: MouseEnter/MouseLeave on a paragraph.

<p appDude></p>
@HostListener('mouseenter')
mouseOver(eventdata: Event) {}

@HostListener('mouseleave')
mouseLeft(eventdata: Event) {}

HostBinding

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

  @Directive({
    selector: '[insBtn]',
    host: {
      '[class.btn]': '_shouldBe',
      '[class.btn-primary]': '_shouldBe'
    },
    inputs: ['bankName', 'branch: building']
  })
  export class InsbtnDirective implements OnChanges {

    constructor() { }

    _shouldBe: boolean;

    get shouldBe() {
      return this._shouldBe;
    }

    @Input('shouldBe')
    set shouldBe(value: boolean) {
      console.log(value);
      this._shouldBe = value;
    }

    // @Input('shouldBe') _shouldBe: boolean;

    bankName: string;
    branch: string;

    ngOnChanges(simple) {
      console.log(simple);
    }
  }
<button type="button" insBtn [shouldBe]="shouldBe">Ins Btn</button>

shouldBe: boolean=true;

It work for direct @Input('shouldBe') _shouldBe: boolean because host only looks at instance variables of the component and not the input.

If else

<p *ngIf="booleanValue; else otherValue">Dude</p>

<ng-template #otherValue>
  <p>Display this instead.</p>
</ng-template>

Types of Directives

There are two types of directives:

  • Non structural - Does not add a template
  • Structural - Adds a template

It creates a <!-- template binding --> place holder in html when you check dev tools with value. ngIf unlike ng-hide in angularjs removes the element from the DOM.

<p *ngIf="booleanValue">Show this on true</p>

Structural Directive

When you write structural directive(*), it adds a <ng-template>.

<p *ngIf="condition">Our heroes are true!</p>
<ng-template [ngIf]="condition">
  <p>
    Our heroes are true!
  </p>
</ng-template>

Non-Structural(Attribute) Directive

<p [ngIf]="condition">
  Our heroes are true!
</p>

Directives Example

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

@Directive({
  selector: '[appHostlistenerDirective]'
})
export class HostlistenerDirectiveDirective implements OnInit {

  constructor(private el: ElementRef, private renderer: Renderer2) {
    console.log('jjj')
  }

  ngOnInit() {
    this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
  }
  
  @HostListener("mouseenter")
  mouseEnter(event: Event) {
    this.renderer.setStyle(this.el.nativeElement, 'text-shadow', '1px 1px 5px black');
  }

  @HostListener('mouseleave')
  mouseLeave(event: Event) {
    this.renderer.setStyle(this.el.nativeElement, 'text-shadow', 'none');
  }
}
  • If you dont use the directive, it wont be instantiated(constructor wont be called).
  • @HostListener listens to event.
  • ElementRef gives reference to the element on which directive is applied.
  • Renderer2 is used for dynamic styling.
  • <div appHostlistenerDirective></div>

If you want even better hold on styling then use HostBinding

  @HostBinding('style.textShadow') textShadow = 'none';
  @HostListener("mouseenter")
  mouseEnter(event: Event) {
    // this.renderer.setStyle(this.el.nativeElement, 'text-shadow', '1px 1px 5px black');
    this.textShadow = '1px 1px 5px black';
  }

  @HostListener('mouseleave')
  mouseLeave(event: Event) {
    // this.renderer.setStyle(this.el.nativeElement, 'text-shadow', 'none');
    this.textShadow = 'none';
  }

If you want input parameters, then use @Input to access inside directive.

<div appMyDirective [color]="red"></div>
@Input() color: string;
@HostBinding('style.color') mycolor = this.color;

If you have certain situation where you have different value for component input and directive input then you can do like this

<app-com [appMyDirective]="'red'"></app-com>

Inside appMyDirective

@Input('appMyDirective') color: string;
⚠️ **GitHub.com Fallback** ⚠️