简体   繁体   English

如何在Angular ControlValueAccessor指令中订阅FormControl值更改

[英]How Can I Subscribe to FormControl Value Changes In an Angular ControlValueAccessor Directive

I have a Directive in Angular that implements ControlValueAccessor. 我在Angular中有一个实现ControlValueAccessor的指令。 The directive seems to be working aside from getting values set in FormControl.setValue(). 该指令似乎与获取FormControl.setValue()中设置的值无关。 How do I get that value update in the directive? 如何在指令中获得该值更新?

Here's my Directive 这是我的指令

import {
  Directive,
  ElementRef,
  EventEmitter,
  forwardRef,
  HostListener,
  Input,
  Output,
  Renderer2
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {CurrencyPipe} from '@angular/common';

@Directive({
  selector: '[currency-input]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CurrencyInputDirective),
      multi: true
    }
  ]
})
export class CurrencyInputDirective implements ControlValueAccessor {

  // TODO: Allow null value
  _currencyString: string = '';

  @Input() ciDecimals = 2;
  @Input() ciCurrencyCode = 'USD';
  @Input() ciMaximumAmount = 10000;

  // Pass true if using formControl or formControlName
  @Input() ciModelDrivenForm = false;
  @Output() ciExceededMax: EventEmitter<any> = new EventEmitter();

  onChangeCallback = (_: any) => {};
  onTouchedCallback = () => {};

  @HostListener('onNgModelChange') onNgModelChange() {
    // Never runs
    console.log("in ng model change");
  }

  @HostListener('input', ['$event']) onInput(value: any) {
    // Never runs
    console.log("in input");
  }

  @HostListener('blur', []) onBlur() {
    this.onTouchedCallback();
  }

  @HostListener('focus', []) onFocus() {
    this.onTouchedCallback();
  }

  @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) {
    switch (e.key) {
      case 'Backspace':
        this.handleBackspaceKeyPress(e);
        break;
      default:
        if (isNaN(+e.key)) {
          e.preventDefault();
        } else {
          this.handleNumericKeyPress(e);
        }
        break;
    }
  }

  constructor (
    private _renderer: Renderer2,
    private _elementRef: ElementRef,
    private _currencyPipe: CurrencyPipe
  ) {}

  writeValue(value: any) {
    this._renderer.setProperty(this._elementRef.nativeElement, 'value', this.buildElementValue());
    this.onChangeCallback(this.buildControlValue());
  }

  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  setDisabledState(isDisabled: boolean) {
    this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
  }

  private handleBackspaceKeyPress(e: KeyboardEvent) {
    e.preventDefault();
    // Remove one digit
    if (this._currencyString.length > 0) {
      this._currencyString = this._currencyString.slice(0, this._currencyString.length - 1);
    }

    this.writeValue(this._currencyString);
  }

  private handleNumericKeyPress(e: KeyboardEvent) {
    e.preventDefault();

    const newCurrencyString = this._currencyString + e.key;
    const currencyValue: number = this.convertCurrencyStringToCurrencyValue(newCurrencyString);

    if (currencyValue > this.ciMaximumAmount) {
      setTimeout(() => {
        this.ciExceededMax.emit({ amount: currencyValue, maxAmount: this.ciMaximumAmount });
      }, 1);
      return;
    }

    this._currencyString = newCurrencyString;
    this.writeValue(this._currencyString);
  }

  private buildElementValue() {
    const currencyDecimals = '1.' + this.ciDecimals + '-' + this.ciDecimals;

    const retVal: string = !this._currencyString ? null : this._currencyPipe.transform(
      this.convertCurrencyStringToCurrencyValue(this._currencyString),
      this.ciCurrencyCode,
      'symbol',
      currencyDecimals);
    return retVal ? retVal : this._currencyPipe.transform(0, this.ciCurrencyCode, 'symbol', currencyDecimals);
  }

  private buildControlValue() {
    return this.convertCurrencyStringToCurrencyValue(this._currencyString);
  }

  private convertCurrencyValueToCurrencyString(currencyValue: number): string {
    return currencyValue.toString().replace(/[^0-9]/g, '');
  }

  private convertCurrencyStringToCurrencyValue(currencyString: String): number {
    const strippedValue: string = currencyString.replace(/[^0-9]/g, '');

    if (strippedValue.length === 0) {
      return 0;
    }
    const parsedInt: number = parseInt(strippedValue, 10);
    return parsedInt / Math.pow(10, this.ciDecimals);
  }
}

Here's my setValue() 这是我的setValue()

myForm.get('myField').setValue(5.00);

Turns out that writeValue was being called when FormControl.setValue() was called. 事实证明,在调用FormControl.setValue()时正在调用writeValue。 Here's the updated directive in case it helps anyone out. 这是更新的指令,以防它帮助任何人。

import {
  Directive,
  ElementRef,
  EventEmitter,
  forwardRef,
  HostListener,
  Input,
  Output,
  Renderer2
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {CurrencyPipe} from '@angular/common';
import {isNullOrUndefined} from '../../util';

@Directive({
  selector: '[currencyInput]',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CurrencyInputDirective),
      multi: true
    }
  ]
})
export class CurrencyInputDirective implements ControlValueAccessor {

  private _currencyString: string = '';

  @Input() ciDecimals = 2;
  @Input() ciCurrencyCode = 'USD';
  @Input() ciMaximumAmount = 10000;
  @Input() ciAllowNull = false;

  @Output() ciExceededMax: EventEmitter<any> = new EventEmitter();

  onChangeCallback = (value: any) => {
  };
  onTouchedCallback = () => {
  };

  @HostListener('blur', []) onBlur() {
    this.onTouchedCallback();
  }

  @HostListener('focus', []) onFocus() {
    this.onTouchedCallback();
  }

  @HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent) {
    switch (e.key) {
      case 'Tab':
        // Allow default
        break;
      case 'Backspace':
        this.handleBackspaceKeyPress(e);
        break;
      default:
        if (isNaN(+e.key)) {
          e.preventDefault();
        } else {
          this.handleNumericKeyPress(e);
        }
        break;
    }
  }

  constructor(
    private _renderer: Renderer2,
    private _elementRef: ElementRef,
    private _currencyPipe: CurrencyPipe
  ) {

  }

  writeValue(value: any) {
    const currencyString: string = this.convertCurrencyValueToCurrencyString(value);

    if (currencyString !== this._currencyString) {
      const currencyValue: number = this.convertCurrencyStringToCurrencyValue(currencyString);
      if (currencyValue > this.ciMaximumAmount) {
        setTimeout(() => {
          this.ciExceededMax.emit({amount: currencyValue, maxAmount: this.ciMaximumAmount});
        }, 1);
        return;
      }
      this._currencyString = currencyString;
    }

    this._renderer.setProperty(this._elementRef.nativeElement, 'value', this.buildElementValue());
    this.onChangeCallback(this.buildControlValue());
  }

  registerOnChange(fn: any) {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any) {
    this.onTouchedCallback = fn;
  }

  setDisabledState(isDisabled: boolean) {
    this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
  }

  private handleBackspaceKeyPress(e: KeyboardEvent) {
    e.preventDefault();
    this.writeValue((this._currencyString.length === 0) ? this._currencyString : this._currencyString.slice(0, this._currencyString.length - 1));
  }

  private handleNumericKeyPress(e: KeyboardEvent) {
    e.preventDefault();
    this.writeValue(this._currencyString + e.key);
  }

  private buildElementValue() {
    const currencyDecimals = '1.' + this.ciDecimals + '-' + this.ciDecimals;
    const controlValue: number = this.buildControlValue();

    // Currency format return value
    if (!isNullOrUndefined(controlValue)) {
      return this._currencyPipe.transform(
        controlValue,
        this.ciCurrencyCode,
        'symbol',
        currencyDecimals);
    }
    return '';
  }

  private buildControlValue(): number {
    return this.convertCurrencyStringToCurrencyValue(this._currencyString);
  }

  private convertCurrencyValueToCurrencyString(currencyValue: any): string {
    if (isNullOrUndefined(currencyValue)) {
      return '';
    }

    if (typeof currencyValue === 'number') {
      const currencyDecimals = '1.' + this.ciDecimals + '-' + this.ciDecimals;
      currencyValue = this._currencyPipe.transform(currencyValue, this.ciCurrencyCode, 'symbol', currencyDecimals);
    }

    return currencyValue.toString().replace(/[^0-9]/g, '');
  }

  private convertCurrencyStringToCurrencyValue(currencyString: String): number {
    const strippedValue: string = currencyString.replace(/[^0-9]/g, '');

    if (strippedValue.length === 0) {
      return (this.ciAllowNull) ? null : 0;
    }
    const parsedInt: number = parseInt(strippedValue, 10);
    return parsedInt / Math.pow(10, this.ciDecimals);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以在 Angular 2+ 中访问我的自定义 ControlValueAccessor 的 formControl 吗? - Can I access to formControl of my custom ControlValueAccessor in Angular 2+? 在 Angular2+ 中订阅 FormControl 真实值更改的最佳方法? - Best way to subscribe to true value changes of FormControl in Angular2+? 从 ControlValueAccessor 更改 FormControl 值 - Change FormControl value from ControlValueAccessor ControlValueAccessor 和 FormControl Value 不同步 - ControlValueAccessor and FormControl Value are out of sync 如何从 Javascript 设置 Angular formcontrol 字段的值? - How can I set the value of an Angular formcontrol field from Javascript? 提交和值更改事件时的Angular 4 ControlValueAccessor值 - Angular 4 ControlValueAccessor value on submit and value changes events 如何使用 ControlValueAccessor Angular 在自定义输入中使用指令 - How To Use A Directive In A Custom Input With ControlValueAccessor Angular Angular FormControl 未检测到输入指令所做的更改 - Angular FormControl not detecting changes made by input directive 如何从实现 ControlValueAccessor 的组件获取对 FormControl 的引用? - How do I get reference to the FormControl from a component that implements ControlValueAccessor? 如何在 formControl 指令获取 angular 之前更改输入值 - how to change value of input before formControl directive get it in angular
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM