I've declared a format for splitting large numbers in groups of three digits and use it frequently like this.
<div>Huge number: {{ i_am_huge | make_threesome }}</div>
Now, there's a request for corresponding functionality but implemented in an input control like this one.
<input id="numeroUno"
type="text">
The approach I can think of is to listen to typing and for each key perform a reformatting of the contents of the box like so.
<input id="numeroUno"
type="text"
(keyup)="formatify">
However, while this approach would work, I can't stop wondering if it's too much of Q&D, so before I build a whole control fauna around this paradigm, I'd like to get more info.
The usual googling didn't give me much. However, give a rather unusual nature of the requirement, it might be hard to find.
The assumption at this point is that input control isn't supposed to be used that way, which explains the clunkyness of my approach.
Use a Directive. In stackblitz you can see how work.
The directive store in the variable "value" the string without spaces. Each a change happens (I use @HotListener(input)) get the position of the cursor, get the value of the element, remove spaces, formate the number and put the cursor in the position
@Directive({ selector: "[testPipe]" })
export class TestPipe implements OnInit {
private el: HTMLInputElement;
private value: any;
constructor(@Optional() private ngControl:NgControl,
private elementRef: ElementRef) {
this.el = this.elementRef.nativeElement;
}
@HostListener("input", ["$event.target.value"])
onInput() {
let pos = this.el.selectionStart; //get the position of the cursor
this.value = this.el.value.replace(/ /gi, ""); //store the value without spaces
if (this.value.length%3==1) //If we must add an extra space
pos++;
//Yes, it's a "bizarro" way to separate in group of three
this.el.value=this.value.match(/(.+?)(?=(.{3})+(?!.)|$)/g).join(' ');
//this.el.value=this.value.match(/(\d+?)(?=(\d{3})+(?!\d)|$)/g).join(' ');
//Finally give the position of cursor
this.el.selectionStart = this.el.selectionEnd = pos;
if (this.ngControl)
this.ngControl.control.setValue(this.el.value,{emit:false})
}
ngOnInit()
{
this.value = this.el.value.replace(/ /gi, "");
this.el.value=this.value.match(/(.+?)(?=(.{3})+(?!.)|$)/g).join(' ');
// this.el.value=this.value.match(/(\d+?)(?=(\d{3})+(?!\d)|$)/g).join(' ');
if (this.ngControl)
this.ngControl.control.setValue(this.el.value,{emit:false})
}
}
Update I add a @Optional() ngControl:NgControl in constructor, so, if the directive is applied to a ngControl (the input belong to a formGroup or has a [(ngModel)], change the value too
I would think that you should rather use an (attribute) directive here. You can use directives for validation purposes, so why not use it to do formatting in your input fields (actually trying to do something like this in my own project at the moment).
Basically it would somewhat look like that (example from the angular docs):
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('appHighlight') highlightColor: string;
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
This way you can manipulate the values inside of the input field and listen on events like click
.
Another thing you have to do before it works: Add the directive (very similar to [ngStyle]
or [ngClass]
) to your input filed via [your-directives-name]
. Look at the following code snippet and how it's done:
<h1>My First Attribute Directive</h1>
<h4>Pick a highlight color</h4>
<div>
<input type="radio" name="colors" (click)="color='lightgreen'">Green
<input type="radio" name="colors" (click)="color='yellow'">Yellow
<input type="radio" name="colors" (click)="color='cyan'">Cyan
</div>
<p [appHighlight]="color">Highlight me!</p>