Search code examples
angulardecimalcurrency

Currency Pipe for Input with reactive Forms in Angular


i've only been learning Angular for a short time and I'm facing a problem. I have built a form using reactive forms. The user can enter an amount of money in an input field. When he leave the field, I would like the value to be automatically formatted into German notation. (e.g. 87.998,00) I don't need the euro symbol.

So far I haven't been able to find a solution to solve this with reactive forms via a pipe. Is it worth continuing to search or do I have to solve this using a classic Javascript function?

So far I've only managed to include the two decimal places.

validateCurrency(event: any){
let t = event.target.value;
event.target.value = 
  t.indexOf(',')>= 0
  ? t.substr(0, t.indexOf(',')) + t.substr(t.indexOf(','), 2)
  : t;

}

Edit:

For an Example, here is my Form:

<form [formGroup]="decimalForm">
<input type="text" #userNumber (blur)="changeNumber()" formControlName="userNumber">

And the .ts of the component

import { Component, ElementRef, ViewChild } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-forms',
  templateUrl: './forms.component.html',
  styleUrl: './forms.component.scss'
})
export class FormsComponent {

  public decimalForm: FormGroup = new FormGroup({
    userNumber: new FormControl('Test')
  })

  @ViewChild("userNumber") userNumber!: ElementRef;

  changeNumber() {


  }

}

In the function changeNumer() I need the code, to change the value after the user leave the input.


Solution

  • Typical if you want to "formatting a number" in (blur) you use a directive

    @Directive({
      selector:'[formatNumber]',
      standalone:true,
      
    })
    export class FormatNumberDirective implements AfterViewInit{
    
      mask:string='.2-2'
      @Input('formatNumber') set _(value:string)
      {
        this.mask=value||'.2-2'
      }
      //this is to align to right
      @HostBinding('style.text-align') style='right'
    
      //listen when (blur)
      @HostListener('blur') 
      onBlur() {
        this.parse()
      }
    
      //listen when focus
      @HostListener('focus',['$event.target'])
      onFocus(target:any) {
        target.value=this.control?.control?.value|| ""
      }
    
    
      //the "el.nativeElement" is the "input"
      //the control.control is the "control" (or NgModel or FormControl)
    
      //we inject the "LOCALE_ID". This can be "hardcode" in provider or 
      //get the value when use Angular localize
    
      constructor(@Inject(LOCALE_ID) private locale:string,
                  private el:ElementRef,
                  private control:NgControl){}
    
      //at very first stage we can also that the value is showed "formatted"
      ngAfterViewInit(){
        setTimeout(()=>{
          this.parse()
        })
      }
      parse()
      {
        this.el.nativeElement.value=this.control?.control?.value?
                   formatNumber(this.control?.control?.value,this.locale,this.mask):
                    ''
      }
    }
    

    And use like

    <input formatNumber [(ngModel)]="value"/>
    <input formatNumber='1.2-2' [formControl]="control"/>
    

    NOTE: Don't forget register your locale using registerLocaleData

    a stackbtliz

    Update

    We can change the parse function to allow the ","
      parse()
      {
        const value=''+(this.control?.control?.value||'')
        this.el.nativeElement.value=value?
                formatNumber(+(value.replace(',','.')),this.locale,this.mask):''
      }
    

    Generally, if we want to store in a dbs, the numbers should be in "standard format" (in en-US). But, we want to use the decimal separator in our locale.

    So, we can declare a variable

    decimalSymbol:string="."
    

    and in constructor of the directive get the value

    this.decimalSymbol=getLocaleNumberSymbol(this.locale, NumberSymbol.Decimal)
    

    Now we need change a bit the functions parse and onFocus

      @HostListener('focus',['$event.target'])
      onFocus(target:any) {
        target.value=this.decimalSymbol!='.'?
                     (this.control?.control?.value|| "").replace('.',this.decimalSymbol):
                     (this.control?.control?.value|| "")
      }
    
      parse()
      {
        const value=this.decimalSymbol!='.'?
                    +(''+(this.control?.control?.value||''))
                             .replace(this.decimalSymbol,'.'):
                    this.control?.control?.value
        this.control.control?.setValue(value, { emit: false });
        this.el.nativeElement.value=value?formatNumber(value,this.locale,this.mask):''
      }
    

    Just corrected in stackblitz