yashhy
yashhy

Reputation: 3086

Input accept numbers-only custom directive

I'm trying to create a custom directive in Angular2 so it accepts numbers-only /^\d+$/. So far I have created a directive which is kind of achieves what I need. But...

The Problem:

  1. When I try to type inside the <input> tag the character appears for a second and then disappears.

  2. The [(ngModel)] which is bound to the element, updates on typing readily (while typing a number), but when a character is typed 'a || b || c, etc' it get stored in ngModel and gets updated on typing a number.

  3. In this way I cannot go over the <input> characters by arrow keys, as it is bound to keyup.

Component:

export class AppComponent {
  myValue = 123;
}

HTML:

<input type="text" name="color" [(ngModel)]="myValue" numbers-only/> <br>
{{myValue}}

Directive:

@Directive({
  selector: '[numbers-only]', 
    host: {
        '(keyup)' : 'onInputChange()'
    }
})
export class UpperCaseDirective {
  private oldVal = '';
  @Input() ngModel: any;

  constructor(private el: ElementRef) { }

  ngOnInit() {
    this.oldVal = this.ngModel || '';
  }

  onInputChange() {
    console.log(this.ngModel, !isNaN(this.ngModel));
    var reg = /^\d+$/;
    if (reg.test(this.ngModel)) {      
      this.el.nativeElement.value = this.ngModel;
      this.oldVal = this.ngModel;
    } else {
      this.el.nativeElement.value = this.oldVal;
    }
  }

}

Coming form a Angular1 background, I find it very hard to write so much code for a simple number only <input>, ng-pattern would do the trick there.

Kindly suggest a way to achieve this OR any other better solution is also welcomed.

Upvotes: 0

Views: 3117

Answers (4)

Lahar Shah
Lahar Shah

Reputation: 7644

I found that key with one char(length=1) renders a char. If we only want to render numbers we can preventDefault for all other keys with length one and not a number(<48 or >57):

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

@Directive({
    selector: '[numbers-only]'
})
export class NumbersOnlyDirective {
    @HostListener('keydown', ['$event'])
    keyDownEvent(event: KeyboardEvent) {
        if (event.key.length === 1 && (event.which < 48 || event.which > 57)) {
            event.preventDefault();
        }
    }

}

Upvotes: 0

Yakov Fain
Yakov Fain

Reputation: 12378

You can create a custom validator and use Forms API of Angular 2. Instead of using two-way binding and events as you do, just create an instance of a FormControl and link it to your input field. While creating an instance of the FormControl, assign a custom validator to it, i.e. a function that will validate an input. For example this is how you can allow entering only positive numbers:

function positiveNumberValidator(control: FormControl): any {
  if (!control.value) return null;
  const price = parseInt(control.value);
  return price === null ||
  typeof price === 'number' &&
  price > 0 ? null : {positivenumber: true};
}

If this function returns null, this means that the value is valid. You can see this example implemented in a SearchComponent here: https://github.com/Farata/angular2typescript/tree/master/chapter8/auction/client/app/components/search

That example uses FormBuilder API, but you can just declare a variable, instantiate the object and assign the validator:

let price: FormControl = new FormControl('', positiveNumberValidator);

If you want to wrap your validator into a directive, this can be done like this:

@Directive({
     
    selector: '[numbers-only]',
      
    providers: [{provide: NG_VALIDATORS, 
                         useValue: positiveNumberValidator}]

    })
    
class NumberValidatorDirective {}

Upvotes: 0

Vlado Tesanovic
Vlado Tesanovic

Reputation: 6424

I am not sure that you are able to do that without mutating html, i can propose two solutions:

<input type="number" name="color" [(ngModel)]="myValue" numbers-only/
<br>
{{myValue}}

Or to do this dynamically in component

ngAfterViewInit() {
  this.el.nativeElement.type = "number";
}

Upvotes: 0

Manish
Manish

Reputation: 2180

use keypress event

@HostListener('keypress') onkeypress(e){
        let event = e || window.event;
        if(event){
          return this.isNumberKey(event);
        }
      }

  isNumberKey(event){
     let charCode = (event.which) ? event.which : event.keyCode;
     if (charCode > 31 && (charCode < 48 || charCode > 57)){
        return false;
     }
     return true;
  }


<input type="text" name="color" [(ngModel)]="myValue" appUpperCase/>

Upvotes: 1

Related Questions