Angular Directives - Numbers only Directive

 Angular Directives

In Angular, Directives are custom HTML attributes which tell Angular compiler to change the style or behavior of the elements. These are basically classes, that add additional behavior to elements in your Angular applications.

We have 3 different types of Directives in Angular

  1. Component Directives: These are directives with a template. General components 
  2. Attribute Directives: These will change the appearance and behavior of the elements. Built in Attribute directives are NgClass, NgStyle, NgModel etc.
  3. Structural Directives: These will change the DOM layout by adding or removing DOM elements. Built in Attribute directives are NgIf, NgFor, NgSwitch etc.

You can read more about directives here.

Creating Numbers only Directive

In this post, I am going to explain how to create an attribute Directive which will allow only numbers from the user for an input box.

1) Create a directives by using below command  
ng generate directive numbersOnly
which generates 
1) A directive file src/app/numbers-only.directive.ts
2) A test file for the above directive src/app/numbers-only.directive.spec.ts
3) Declare the class in app.module.ts
with the below content

src/app/numbers-only.directive.ts
import { Directive } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {

  constructor() { }

}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NumbersOnlyDirective } from './numbers-only.directive';

@NgModule({
  declarations: [
    AppComponent,
    NumbersOnlyDirective
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
2) Now add ElementRef in the directive's constructor() to inject a reference to the host DOM element, the element to which you apply appNumbersOnly
import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {

  inputElement: HTMLElement;

  constructor(public el: ElementRef) {
    this.inputElement = el.nativeElement;
  }
}
3) Now for implementing the numbers only we need to handle in 3 ways
a) When user enters a key
b) When user paste some value
c) When user drop some value
  @HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }

  @HostListener('paste', ['$event'])
  onPaste(event:any) {
    event.preventDefault();
    const pastedInput: string = event.clipboardData
      .getData('text/plain')
      .replace(/[^0-9-]+/ig, ''); // get a digit-only string
    document.execCommand('insertText', false, pastedInput);
  }

  @HostListener('drop', ['$event'])
  onDrop(event: DragEvent) {
    event.preventDefault();
    const textData =  event.dataTransfer?.getData('text').replace(/[^0-9-]+/ig, '');
    this.inputElement.focus();
    document.execCommand('insertText', false, textData);
  }
Here the in the keydown event I am checking whether e.Key is number or not. 
Note: e.KeyCode,e.which are deprecated and not supported from Angular 8 version. Read more here.

4) Now our directive will accept only numbers. Here our keydown event accept only if the given key is number. But we need to allow some other keys as well like Backspace, Delete, Tab etc. So add exceptions for those as well.
 private navigationKeys = [
    'Backspace',
    'Delete',
    'Tab',
    'Escape',
    'Enter',
    'Home',
    'End',
    'ArrowLeft',
    'ArrowRight',
    'Clear',
    'Copy',
    'Paste'
  ];
@HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    if (
      this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
    ) {
      // let it happen, don't do anything
      return;
    }
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }
  
  
Here is the final version of our Directive.
 import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {
  private navigationKeys = [
    'Backspace',
    'Delete',
    'Tab',
    'Escape',
    'Enter',
    'Home',
    'End',
    'ArrowLeft',
    'ArrowRight',
    'Clear',
    'Copy',
    'Paste'
  ];

  inputElement: HTMLElement;

  constructor(public el: ElementRef) {
    this.inputElement = el.nativeElement;
  }

  @HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    if (
      this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
    ) {
      // let it happen, don't do anything
      return;
    }
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }

  @HostListener('paste', ['$event'])
  onPaste(event:any) {
    event.preventDefault();
    const pastedInput: string = event.clipboardData
      .getData('text/plain')
      .replace(/[^0-9-]+/ig, ''); // get a digit-only string
    document.execCommand('insertText', false, pastedInput);
  }

  @HostListener('drop', ['$event'])
  onDrop(event: DragEvent) {
    event.preventDefault();
    const textData =  event.dataTransfer?.getData('text').replace(/[^0-9-]+/ig, '');
    this.inputElement.focus();
    document.execCommand('insertText', false, textData);
  }
  
}
 

Applying our custom attribute directive

To use the NumbersOnlyDirective, add an <input> element to the HTML template with the directive as an attribute.
 Enter number:

Gopikrishna

    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment