实现DefaultValueAccessor不会触发“registerOnChange”

时间:2017-06-06 07:49:37

标签: angular

我正在尝试实现此指令(https://plnkr.co/edit/yftpJ4A1HwrKkpfcSIT7?p=preview):

export class PrevValDirective {
  constructor(private valueAccessor: DefaultValueAccessor, private model: NgModel) {
    valueAccessor.registerOnChange = (fn: (val: any) => void) => {
      valueAccessor.onChange = (val) => {
        if(val === 'false') {
          return fn(model.value);
        }
        return fn(val);
      };
    }
  }
}

但是通过实现DefaultValueAccessor / ControlValueAccessor(https://plnkr.co/edit/Vu9OJBmqhw8W5Rt9AIOJ?p=preview):

export class PrevValDirective implements DefaultValueAccessor {
  constructor(private model: NgModel){
  }

    registerOnChange(fn: (val: any) => void) {
        this.onChange = (val) => {
        if(val === 'false') {
          model.control.setValue(model.value);
          return;
        }
        return fn(val);
      };
    }
}

出于某种原因 - registerOnChange不会触发。

我还想将onChange更改为常规(非匿名)函数,但无法弄清楚如何(我需要fn var)。

1 个答案:

答案 0 :(得分:3)

您需要实施自定义ControlValueAccessor,而不是扩展DefaultValueAccessorhttps://github.com/angular/angular/issues/9146#issuecomment-241191899

要实施ControlValueAccessor,您应该为此指令提供NG_VALUE_ACCESSOR令牌。最简单的解决方案可能如下所示:

import { 
  Input, forwardRef, Renderer2, 
  Injector, ElementRef, Directive
} from '@angular/core'

import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';

export const PREV_VAL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => PrevValDirective),
  multi: true
};

@Directive({
  selector: '[ngModel][prevVal]',
  host: {
    '(input)': 'handleInput($event.target.value)',
    '(blur)': 'onTouched()',
  },
  providers: [PREV_VAL_VALUE_ACCESSOR]
})
export class PrevValDirective implements ControlValueAccessor {
  @Input() prevVal: string;

  onChange = (_: any) => {};
  onTouched = () => {};

  constructor(
    private _renderer: Renderer2, 
    private _elementRef: ElementRef, 
    private _inj: Injector) {
  }

  writeValue(value: any): void {
    const normalizedValue = value == null ? '' : value;
    this._renderer.setProperty(this._elementRef.nativeElement, 'value', normalizedValue);
  }

  registerOnChange(fn: (_: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

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

  handleInput(value: any): void {
    if (value === this.prevVal) {
      let model = this._inj.get(NgControl);  // get NgModel
      model.control.setValue(model.value);   // set previous value
      return;
    }
    this.onChange(value);
  }
}

很遗憾,您无法在实施NgControl的课程中注入NgModelControlValueAccessor)。它会上升错误

  

无法实例化循环依赖! NgControl

幸运的是,您可以利用Injector来获取它,就像我在此行所做的那样

let model = this._inj.get(NgControl);

我也将prevValue传递给@Input

<input type="text" [(ngModel)]="val" prevVal="false">

<强> Plunker Example