Format Input text with dotted format in thousands and decimal places

1

I am trying to create a directive or other element that allows me to show formatted an input text as 12.345,67 and to enter the decimal point (,).

Examples:

  

Thousand, formatted should be 1,000

     

Thousand with 53 decimals, formatted should be 1,000,53

In my attempts evidently to put the comma as a decimal gives error.

link

    
asked by Theasker 18.05.2018 в 13:58
source

2 answers

1

Here I propose this pipe, quite simple to understand and to use as well. Try it and tell me how it went.

import { Pipe, PipeTransform } from "@angular/core";

const PADDING = "000000";

@Pipe({ name: "thousandsPipe" })
export class ThousandsPipe implements PipeTransform {

  private DECIMAL_SEPARATOR: string;
  private THOUSANDS_SEPARATOR: string;

  constructor() {
    // TODO Puedes configurar los separadores que prefieras
    this.DECIMAL_SEPARATOR = ",";
    this.THOUSANDS_SEPARATOR = ".";
  }

  transform(value: number | string, fractionSize: number = 2): string {
    let [ integer, fraction = "" ] = (value || "").toString()
      .split(this.DECIMAL_SEPARATOR); // Divide entre parte entera y decimal, por la "," en este caso

    fraction = fractionSize > 0
      ? this.DECIMAL_SEPARATOR + (fraction + PADDING).substring(0, fractionSize)
      : "";

    integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, this.THOUSANDS_SEPARATOR);

    return integer + fraction;
  }

  parse(value: string, fractionSize: number = 2): string {
    let [ integer, fraction = "" ] = (value || "").split(this.DECIMAL_SEPARATOR);

    integer = integer.replace(new RegExp(this.THOUSANDS_SEPARATOR, "g"), "");

    fraction = parseInt(fraction, 10) > 0 && fractionSize > 0
      ? this.DECIMAL_SEPARATOR + (fraction + PADDING).substring(0, fractionSize)
      : "";

    return integer + fraction;
  }

}

You use it in the following way:

<p>{{amount | thousandsPipe}} </p>
    
answered by 18.05.2018 в 19:24
-2

In the end I found the solution:

link

import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

@Component({
  selector: 'app-control-value-accessor',
  template: '
  <input 
    type="text" 
    [(ngModel)]="valueInputText" 
    (focus)="onFocus($event);" 
    (change)="onChange($event)" 
    (blur)="onBlur($event)"
    (mouseup)="$event.preventDefault()"
    (ngModelChange)="onChangeModel($event)"
    (keydown)="onKeydown($event)"
    (paste)="onPaste($event)"
    [disabled]="isDisabled" > 

    <p>Modelo del componente input: {{value}}</p>
  ',
  providers: [
    {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => ControlValueAccessorComponent),
        multi: true
    },
]
})
export class ControlValueAccessorComponent implements OnInit, OnDestroy, ControlValueAccessor {
  //@Input('value') value: string = '1.234,56';
  public valueInputText: string;

  @Input('value') value: number;
  @Output() valueChange: EventEmitter<number> = new EventEmitter<number>();
      
  private propagateChange = (_: any) => { };
  private propagateTouch = (_: any) => { };
  public isDisabled: boolean = false;

  // Permite números decimales. El \. es para que ocurra sólo una vez
  //private regex: RegExp = new RegExp(/^[0-9]+(\,[0-9]*){0,1}$/g);
  private regexNumber: RegExp = new RegExp(/\d$/g);
  //private regexAny: RegExp = new RegExp(/\D$/g);
  //private copyPasteKeys: string = '';
  private copyPasteKeys: Array<string> = ['Control','Shift','c','v','Insert'];
  private copyPastePreviousKey: string;
  private InitialcopyPastePreviousKey: any;

  // Teclas que permito introducir
  private specialKeys: Array<string> = ['Enter','Backspace','Tab','End','Home','ArrowRight','ArrowLeft','Delete',','];
  //private commaCounter: number; // Contador de comas
  
  constructor() {  }

  ngOnInit(): void {
    this.copyPastePreviousKey = '';
    this.InitialcopyPastePreviousKey = setInterval(() => {
      this.copyPastePreviousKey = ''; 
    }, 1000);
  }

  ngOnDestroy(): void {
    if (this.InitialcopyPastePreviousKey) {
      clearInterval(this.InitialcopyPastePreviousKey);
    }
  }

  onFocus(event: any): void {
    console.log('onFocus event.target.value: ', event.target.value);
    console.log('onFocus this.valueInputText: ', this.valueInputText);
    
    if (this.valueInputText == null){
      this.valueInputText = '0,00';
      console.log('inicializado');
    }
    // Elimino todos los puntos ('.') del string
    let regex = /\./g;
    this.valueInputText = this.valueInputText.replace(regex, '');
  }

  onKeydown(event: KeyboardEvent): void {
    console.log('onKeydown', event.key);
    /*
    //console.log('onKeydown event.key: ', event.key);
    if (this.specialKeys.indexOf(event.key) != -1 || String(event.key).match(this.regexNumber) || event.key == ',') {
      if (event.key == ','){
        if (this.commaCounter == undefined) {
          this.commaCounter = 0;
          //console.log('this.commaCounter: ', this.commaCounter);
        }else if (this.commaCounter > 0){
          event.preventDefault();          
          //console.log('onKeydown preventDefault');
        }else {
          this.commaCounter++;
          return;
        }
      }
      //console.log('onKeydown return: ');
      return;
    }else {
      //console.log('onKeydown preventDefault');
      event.preventDefault();
    }
    */

  //////////////////////////////////////////////////
 
  console.log('onKeydown event.key: ', event.key);
  
  if (this.specialKeys.indexOf(event.key) != -1 || String(event.key).match(this.regexNumber) || event.key == ',') {
    if (event.key == ',') {
      if(this.valueInputText.search(',') >= 0) {
        event.preventDefault();        
      }
    }
    return;
  } else if (this.copyPasteKeys.indexOf(event.key) != -1) { // Control de Copy & Paste
    if (event.key == 'Control' || event.key == 'Shift') {
      this.copyPastePreviousKey = event.key;
    }
    console.log('this.copyPasteKeys.indexOf(event.key) != -1: ', this.copyPasteKeys.indexOf(event.key) != -1);
    console.log('this.copyPastePreviousKey == "Control" || this.copyPastePreviousKey == "Shift": ', this.copyPastePreviousKey == 'Control' || this.copyPastePreviousKey == 'Shift');
    if (this.copyPasteKeys.indexOf(event.key) != -1 && (this.copyPastePreviousKey == 'Control' || this.copyPastePreviousKey == 'Shift')) {
      return;
    }else {
      event.preventDefault();
    }
  } else {
    //console.log('onKeydown preventDefault');      
    event.preventDefault();    
  }
}

  onBlur(event: ClipboardEvent): void {
    console.log('onBlur this.valueInputText (antes) ', this.valueInputText);
    // if (this.valueInputText) {
    //   this.commaCounter = this.counterString(this.valueInputText,',');
    // }else {
    //   this.commaCounter = 0;
    // }
    // Elimino todos los puntos ('.') del string
    let regex = /\./g;
    this.valueInputText = this.valueInputText.replace(regex, '');

    this.valueInputText = this.thousandsSeparator(this.valueInputText);
    // console.log('onBlur this.valueInputText (despues): ', this.valueInputText);
    // console.log('onBlur this.parseStringNumber(this.valueInputText): ', this.parseStringNumber(this.valueInputText));
    this.value = this.parseStringNumber(this.valueInputText);
    // console.log('onBlur this.value: ', this.value, typeof(this.value));
    
    this.valueChange.emit(this.value); 
  }
  
  
  private onChange(event : any): void {
    this.propagateChange(this.valueInputText);
    //console.log('onChange this.valueInputText (antes): ', this.valueInputText);
    this.valueInputText = this.thousandsSeparator(this.valueInputText);
    // console.log('onChange this.valueInputText (después): ', this.valueInputText);
    // console.log('onChange this.parseStringNumber(this.valueInputText): ', this.parseStringNumber(this.valueInputText));
    this.value = this.parseStringNumber(this.valueInputText);
    // console.log('onChange this.value: ', this.value, typeof(this.value));
    
    this.valueChange.emit(this.value); 
  }

  onPaste(event: any): void {
        
  }

  /**
   * Allows Angular to update the model (rating).
   * Update the model and changes needed for the view here.
   * @param  {any} value
   * @returns void
   */
  writeValue(value: any): void {
    // console.log('writeValue value: ', value);
    if (value) {
      this.valueInputText = value;
    }
  }
  
  /**
   * Allows Angular to register a function to call when the model (rating) changes.
   * Save the function as a property to call later here.
   * @param  {(_:any)=>void} fn
   * @returns void
   */
  registerOnChange(fn: (_: any) => void): void {
      this.propagateChange = fn;      
  }

  /**
   * Allows Angular to register a function to call when the input has been touched.
   * Save the function as a property to call later here.
   * @param  {()=>void} fn
   * @returns void
   */
  registerOnTouched(fn: () => void): void {
      this.propagateTouch = fn;
  }

  private onChangeModel(value: any) {
    //console.log('onChangeModel value: ', value);
  }

  private onTouch(event : any){
    console.log('onTouch event: ', event);
    this.propagateTouch(event);
    console.log('onTouch this.propagateTouch(event): ', this.propagateTouch(event));
  }
  
  /**
   * Allows Angular to disable the input.
   * @param  {boolean} isDisabled
   * @returns void
   */
  setDisabledState(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
  }

  /**
   * Cuenta las ocurrencias de un string
   * @param  {string} stringToSearch
   * @param  {string} search
   * @returns number
   */
  counterString (stringToSearch: string, search: string): number{
    console.log('counterString');
    let i: number = 0;
    let counter: number = 0;
    while (i != -1) {
      i = stringToSearch.indexOf(search,i);
      if (i != -1) {
        i++;
        counter++;
      }
    }
    return counter;
  }

  /**
   * Pone los separadores de miles con punto y coma decimal
   * @param  {string} stringNumber
   * @returns string
   */
  public thousandsSeparator (stringNumber: string): string {
    console.log('thousandsSeparator');
    let resultado: string;
    stringNumber = stringNumber.replace(',','.');
    console.log('thousandsSeparator stringNumber: ', stringNumber);
    let flotante: number = parseFloat(stringNumber);
    let flotanteString = flotante.toFixed(2);
    resultado = flotanteString.replace('.', ',');
    console.log('thousandsSeparator flotanteString: ', flotanteString);
    console.log('thousandsSeparator resultado: ', resultado);
  
    let pos = resultado.indexOf(",");
    console.log('thousandsSeparator pos: ', pos);
    // string.substr(<desde>, <longitud>);
    while (pos > 3) {
      resultado = resultado.substr(0, pos-3)+'.'+resultado.substr(pos-3, 3)+resultado.substr(pos);
      pos=pos-3;
    }
    if (resultado == 'NaN') {
      return '0,00';
    }else {
      return resultado;    
    }
  }

  /**
   * Convierte un string con formato 10.000.000,00 a un número 10000000.00
   * @param {string} stringNumber
   * @returns string
   */
  public parseStringNumber (stringNumber: string): number {
    let resultado: string;
    resultado = stringNumber;
    resultado = resultado.replace(/\./g, '');
    resultado = resultado.replace(',', '.');
    return parseFloat(resultado);
  }

  
  
}

link

    
answered by 04.06.2018 в 09:38