控制值访问器转移重点

时间:2018-10-22 19:52:22

标签: angular typescript

import { Component, Input, forwardRef, OnChanges } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'formatted-currency-input',
  templateUrl: '../views/formattedCurrencyInput.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => FormattedCurrencyInputComponent),
      multi: true
    }
  ],
  styles: ['.formatted-input {text-align: right; height: 100%; border: none; padding-right: 5px;}']
})

export class FormattedCurrencyInputComponent implements ControlValueAccessor {
  
  @Input()
  private _stringValue: string;

  //private _stringValue: string;

  @Input()
  private _numberValue: number;
  
  textVisible: boolean = false;

  get numberValue() {
    return this._numberValue;
  }

  set numberValue(val: number) {
    this._numberValue = val;
    this.propagateChange(this._numberValue);
  }

  get stringValue() {
    return this._stringValue;
  }

  set stringValue(val: string) {
    this._stringValue = val;
    this.propagateChange(this._stringValue);
  }

  setValue(e: any) {
    let val = e.target.value;
    this._numberValue = parseInt(val);
    this.convertToString(this._numberValue);
    this.propagateChange(this._stringValue);
  }

  toggleActive() {
    this.textVisible = !this.textVisible;
    //console.log(input);
    //input.focus();
  }

  convertToString(num: any) {
    this._stringValue = parseFloat(num).toLocaleString();
  }

  /*
   * Writes a new value from the form model into the view
   * or (if needed) Dom property
   */
  writeValue(obj: any) {
    // Only set the value when it is not undefined
    if (obj !== undefined) {
      this.convertToString(obj);
    }
  }

  propagateChange = (_: any) => {};

  /*
   * Method that registers a handler that should be called when
   * something in the view has changed. It gets a function (propagateChange)
   * that tells other forn directives and form controls to update
   * their values
   */
  registerOnChange(fn: any) {
    this.propagateChange = fn;
  }

  /*
   * Similar to registerOnChage(), this registers a handler
   * specifically for when a control receives a touch event
   */
  registerOnTouched(fn: any) {
    console.log('onTouched()');
  }

  setDisabledState(isDisabled: boolean) {

  }

}
<input class="formatted-input" [hidden]="textVisible" type="text" (focus)="toggleActive()" value="{{_stringValue}}"/>
<input class="formatted-input" [hidden]="!textVisible" type="number" (change)="setValue($event)" (blur)="toggleActive()"/>

好的,我有点奇怪。我为工作中的项目构建了一个自定义表单控件,该控件基本上包含两个输入。其中之一是数字输入,其隐藏直到用户单击输入。另一个是文本输入,用于以逗号显示数字。一切工作正常,只是当用户单击组件时,他们必须单击两次以获取数字输入以获得焦点。我该如何解决这个问题,请让我知道是否可以澄清这个问题,因为我知道这是一种奇怪的情况enter image description here

1 个答案:

答案 0 :(得分:0)

您可以查看this stackblitz。该演示使用了简化的代码版本,没有实现ControlValueAccessor。您会注意到,我对stringValue应用了一种怪异的格式,以清楚显示哪个输入字段是活动的,并且我颠倒了标记中textVisible的含义。

主要变化如下:

  • 将输入的数字的value属性绑定到numberValue
  • 在输入的数字上定义模板引用变量#numberInput
  • 将该变量传递给文本输入的focusInput事件处理程序中的(focus)函数
  • 在将焦点放在数字输入上之前先呼叫ChangeDetectorRef.detectChanges

标记

<input [hidden]="!textVisible" type="text" value="{{stringValue}}"
       (focus)="toggleActive(); focusInput(numberInput)"/>

<input #numberInput [hidden]="textVisible" type="number" [value]="numberValue" 
       (change)="setValue($event)" (blur)="toggleActive()"/>

代码(简化版)

textVisible = true;
numberValue = 7248;

constructor(private cd: ChangeDetectorRef) { }

toggleActive() {
  this.textVisible = !this.textVisible;
}

get stringValue(): string {
  return this.numberValue.toLocaleString();
}

setValue(event: Event) {
  this.numberValue = parseInt((event.target as HTMLInputElement).value, 10);
}

focusInput(input: HTMLInputElement) {
  this.cd.detectChanges();
  input.focus();
}