Currency Pipe for Input with reactive Forms in Angular

184 views Asked by At

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.

2

There are 2 answers

3
Eliseo On BEST ANSWER

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

5
NgDaddy On

Try to utilize this:

  1. https://angular.io/api/common/CurrencyPipe
  2. https://angular.io/api/common/DecimalPipe

If you need a specific culture: https://angular.io/api/core/LOCALE_ID

Code sample (stackblitz):

main.ts

import {
  CurrencyPipe,
  DecimalPipe,
  formatCurrency,
  formatNumber,
} from '@angular/common';
import { Component, inject, LOCALE_ID } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './main.html',
  imports: [DecimalPipe, CurrencyPipe],
})
export class App {
  localeId = inject(LOCALE_ID);

  name = 'Angular';

  num1: number = 12.638467846;
  num2: number = 0.5;
  num3: string = formatNumber(this.num1, this.localeId);

  cur1: number = 0.25;
  cur2: number = 10.263782;
  cur3: string = formatCurrency(this.cur2, this.localeId, 'USD');
}

bootstrapApplication(App);

main.html

<h1>Hello from {{ name }}!</h1>
<h3>Decimal Pipe</h3>
<div>
  <p>{{num1 | number}}</p>
  <p>{{num1 | number:'3.2-5'}}</p>
  <p>{{num2 | number:'3.2-5'}}</p>
  <p>{{num1 * num2 | number:'1.3-6'}}</p>
  ==========
  <p>{{num3}}</p>
</div>
<h3>Currency Pipe</h3>
<div>
  <p>{{cur1 | currency:'INR':false}}</p>
  <p>{{cur2 | currency:'INR':false:'1.2-4'}}</p>
  <p>{{cur1 | currency}}</p>
  <p>{{cur2 | currency:'USD':true:'2.2-4'}}</p>
  ==========
  <p>{{cur3}}</p>
</div>