Angular 5,输入格式和解析,什么是标准解决方案

时间:2018-09-21 10:47:08

标签: angular angular5

我有一个输入字段,想要使用德语语言环境输入一个浮点数。我的解决方案如下,它可以工作,但是很复杂,应该有更好的方法吗?

<input matInput [ngModel]="amount | number: '0.2-2'" 
(blur)="transformAmount($event)" (keyup.enter)="transformAmount($event)"/>

transformAmount(event) {
  console.log(event.target.value);
  this.amount = parseFloat(event.target.value.replace('.', '').replace(',','.'));
  console.log('amount=' + this.amount);
}

4 个答案:

答案 0 :(得分:3)

我在格式化反应形式输入字段时遇到了类似的问题。为了解决这个问题,我使用了这个article。我创建了一个略微修改的版本以满足我的需求。

disposable.add(module.getInfo()
.flatMapSingleElement {
  profile ->
    profile.getDetails().map {
      //IS THIS NEW DISPOSABLE NEEDED
      newDisposable.add(
    //Retrofit api to return Single<ResponseBody>
    //IS THIS THE RIGHT WAY TO MAKE A SEQUENTIAL API CALL IN RX CONSUMING DATA FROM ANOTHER OPERATOR
    module.saveImageDetails(
        ImageDetails(imageId)
        subscribeOn(Schedulers.io().observeOn(Schedulers.io())
            .subscribe(
                Consumer { handleResponse(it) },
                createErrorHandler()
            )
    )
      profile
    }
}
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(//do something)

要使用此指令,您的html应该如下所示:

import { Directive, ElementRef, forwardRef, HostListener, Input, OnDestroy } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material';
import { Subscription } from 'rxjs';
import { formatNumber } from '@angular/common';

@Directive({
  selector: 'input[localizedNumericInput]',
  providers: [
    { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: LocalizedNumericInputDirective },
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => LocalizedNumericInputDirective),
      multi: true
    }
  ]
})
export class LocalizedNumericInputDirective implements ControlValueAccessor, OnDestroy {
  locale = 'en';
  decimalMarker: string;

  constructor(private element: ElementRef<HTMLInputElement>) {
  }

  private _value: string | null;

  get value(): string | null {
    return this._value;
  }

  @Input('value')
  set value(value: string | null) {
    this._value = value;
    this.formatValue(value);
  }

  @HostListener('input', ['$event.target.value'])
  input(value) {
    //Find all numerics, decimal marker(, or .) and -
    //It will delete thousandSeparator cos it's always opposite to decimal marker
    const regExp = new RegExp(`[^\\d${this.decimalMarker}-]`, 'g');
    //Separate value on before and after decimal marker
    const [integer, decimal] = value.replace(regExp, '').split(this.decimalMarker);

    //Send non localized value, with dot as decimalMarker to API
    this._value = decimal ? integer.concat('.', decimal) : integer;

    // If decimal separator is last character don't update
    // because it will delete . || ,
    if (this.isLastCharacterDecimalSeparator(value)) {
      this._value = value;
    }

    // here to notify Angular Validators
    this._onChange(this._value);
  }

  @HostListener('blur')
  _onBlur() {
    /**
     * Adding thousand separators
     */
    this.formatValue(this._value);
  }

  @HostListener('focus')
  onFocus() {
    this.unFormatValue();
  }

  _onChange(value: any): void {}

  /**
   * @param value
   * apply formatting on value assignment
   */
  writeValue(value: any) {
    this._value = value;
    this.formatValue(this._value);
  }

  registerOnChange(fn: (value: any) => void) {
    this._onChange = fn;
  }

  registerOnTouched() {}

  isLastCharacterDecimalSeparator(value: any) {
    return isNaN(value[value.length - 1]);
  }


  private formatValue(value: string | null) {
    if (value === null) {
      this.element.nativeElement.value = '';
      return;
    }

    if (this.isLastCharacterDecimalSeparator(value)) {
      this.element.nativeElement.value = value;
      return;
    }

    // Conclude the decimal and thousand separators from locale
    const [thousandSeparator, decimalMarker] = formatNumber(1000.99, this.locale).replace(/\d/g, '');
    this.decimalMarker = decimalMarker;

    //Here value should always come with . as decimal marker thus any other behavior is bug
    const [integer, decimal] = value.split('.');

    //Group every three elements, and add thousandSeparator after them
    this.element.nativeElement.value = integer.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);

    //Add decimals and decimalMarker if any
    if (decimal) {
      this.element.nativeElement.value = this.element.nativeElement.value.concat(decimalMarker, decimal);
    }
  }

  private unFormatValue() {
    const value = this.element.nativeElement.value;
    if (this.isLastCharacterDecimalSeparator(value)) {
      return;
    }
    const regExp = new RegExp(`[^\\d${this.decimalMarker}-]`, 'g');
    const [integer, decimal] = value.replace(regExp, '').split(this.decimalMarker);

    this._value = integer.concat('.', decimal);
    if (value) {
      this.element.nativeElement.value = this._value;
    } else {
      this.element.nativeElement.value = '';
    }
  }
}

答案 1 :(得分:1)

是的,这部分有些棘手。此外,有时您需要显示的不是实际值。

我发现它是为我的输入创建一个自定义组件(使用ngModel,请参阅有关ControlValueAccessor的方法)的实际最佳解决方案,它看起来像是基于您要执行的操作。

html:

<input [formControl]="inputFormControl" #myInput />

ts:

@ViewChild('myInput') inputElm: ElementRef;
formGroup: FormGroup;
value: number;

this.inputFormControl.valueChanges.subscribe((value: string) => {
  if (value) {
    const displayValue = ('' + value).replace(',', '.');
    this.value = parseFloat(displayValue); // manage the value
    this.inputFormControl.setValue(this.value, { emitEvent: false });
    this.inputElm.nativeElement.value = new DecimalPipe(this.localId).transform(displayValue, '0.2-2'); //control the display
  }
});

从某种意义上讲,您一定会发现它有些复杂,但是最后它还为您提供了更多的控制权;)

答案 2 :(得分:0)

您的方法很好。

没有比将input的数据从变量转换为Pipe更好的方法,没有比绑定到DOM事件的函数更好的将数据从input转换为变量的方法。

为了简化代码,您可以执行以下操作:

<input matInput [ngModel]="amount | number: '0.2-2'" (ngModelChange)="transformAmount($event)"/>

transformAmount(event) {
  console.log(event);
  this.amount = parseFloat(event.replace('.', '').replace(',','.'));
  console.log('amount=' + this.amount);
}

答案 3 :(得分:0)

我使用这篇Angular 2 Directives to Format Text as you Type文章中的信息创建了一个指令。

它正在工作,但是我不明白为什么必须要有'Change'后缀,以及为什么我不能从行中将输入与事件分开:(appNumberInput)= .... [appNumberInput] = ...

<input [(appNumberInput)]="amount" format="0.2-2" />




@Directive({
    selector: '[appNumberInput]',

})
export class NumberInputDirective implements OnInit {

    @Input()
    appNumberInput: any;

    @Input()
    format: string;

    @HostBinding('value')
    stringValue: string;


    @Output() appNumberInputChange: EventEmitter = new EventEmitter();

    constructor(private decimalPipe: DecimalPipe) {
    }

    ngOnInit() {
      this.stringValue = this.decimalPipe.transform(this.appNumberInput, this.format);
    }

    @HostListener('blue', ['$event.target.value'])
    @HostListener('keyup.enter', ['$event.target.value'])
    formatANumber(value) {
        const numberValue = parseFloat(value.replace('.', '').replace(',', '.'));
        this.stringValue = this.decimalPipe.transform(numberValue, this.format);
        this.appNumberInputChange.next(numberValue);

  }
}