角度:使用自定义ControlValueAccessor测试组件时,永远不会调用registerOnChange

时间:2018-08-11 00:28:33

标签: angular typescript angular-directive controlvalueaccessor

正如我描述in this answer一样,我创建了一个自定义ControlValueAccessor指令以控制何时触发组件的onChange事件,并且一切正常,除非进行测试,否则永远不会调用registerOnChange,因此,我的测试失败。

我的指令如下:

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

@Directive({
  selector: "[testMask]",
  providers: [MASK_CONTROL_VALUE_ACCESSOR]
})

export class MaskDirective implements ControlValueAccessor {

  private onChange;
  private nativeElement;

  constructor(private element: ElementRef) {
    this.nativeElement = this.element.nativeElement;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
  }
  setDisabledState(isDisabled: boolean): void {
    this.nativeElement.disabled = isDisabled;
  }
  writeValue(newValue) {
    newValue = newValue == null ? "" : newValue;
    this.nativeElement.value = newValue;
  }
  @HostListener("input", ["$event"])
  onInput(event: KeyboardEvent) {
    /*DO YOUR STUFF HERE*/
    // Call onChange to fire the valueChanged listeners
    this.onChange(newValue);
  }
}

我的测试:

describe("Test Custom Directive", () => {
  @Component({
    template:
        `<input type="text" [formControl]=inputFormControl testMask/>`
  })
  class TestComponent {

    _inputFormControl: FormControl;

    constructor(private element: ElementRef) {
      this._inputFormControl = new FormControl();
    }

    get inputFormControl() {
      return this._inputFormControl;
    }
  }

  let fixture: ComponentFixture<TestComponent>;
  let inputField: HTMLInputElement;

  const getInput = (fix: ComponentFixture<TestComponent>) => {
    const inputDebug = fix.debugElement.query(By.directive(MaskDirective));
    return inputDebug.nativeElement as HTMLInputElement;
  };

 beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestComponent,
        MaskDirective
      ]
    });
  });
    beforeEach(() => {
      fixture = TestBed.createComponent(TestComponent);
      fixture.detectChanges();
      inputField = getInput(fixture);
    });

    it("test", async () => {
      //Tests fails because there's no onChange callback
    });
}

基于我在"Never again be confused when implementing ControlValueAccessor in Angular forms"上所读的内容,我假设仅向我的输入字段添加一个FormControl应该会触发setupControl,但事实并非如此。我想念什么?

2 个答案:

答案 0 :(得分:0)

您需要在configureTestingModule声明中导入FormsModule。

我有点不同。通过使用ReactiveFormsModule和名为“ directiveForm”的指令

@Component({
  template: `<form [formGroup]="testForm"><input type="text" formControlName="testControl" directiveForm></form>`
})
class TestComponent {

  public testForm: FormGroup;

  constructor(private fb: FormBuilder) {

    this.testForm = this.fb.group({
      testControl: new FormControl()
    });
  }
}

describe('Directive Test', () => {

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        DirectiveForm,
        TestComponent
      ],
      imports: [DirectiveFormModule],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    }).compileComponents();
  }));

这种方式对我有用

答案 1 :(得分:0)

在这里遇到了同样的问题,对我来说,原因是在主测试后调用了registerOnChange,并且此修复程序将TestBed.createComponent(TestComponent)包装在fakeAsync中