Reputation: 3086
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:
When I try to type inside the
<input>
tag the character appears for a second and then disappears.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 inngModel
and gets updated on typing a number.In this way I cannot go over the
<input>
characters by arrow keys, as it is bound tokeyup
.
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
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
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
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
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