Angular:使用自定义 ControlValueAccessor 测试组件时从不调用 registerOnChange

标签 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,但事实显然并非如此。我错过了什么?

最佳答案

您需要在 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();
  }));

这种方式适合我

关于Angular:使用自定义 ControlValueAccessor 测试组件时从不调用 registerOnChange,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51795426/

相关文章:

angular - 如何有条件地在 ng-content 周围包装一个 div

angular - 在子文件夹下部署 Angular SPA

angular - 如何使用 @Input 在 *ngFor 指令中设置管道

javascript - AngularJs 自定义指令未被调用

angular - 获取 OAuth token 以供 Azure DevOps API 使用

css - 未满足的同伴依赖

typescript - 成员的多种类型签名,TypeScript 中的联合类型

javascript - 观察者确认是如何运作的?

javascript - 访问表单 ElementRef 的 "form"属性显示 TypeScript 错误

javascript - AngularJS - 单击时调用指令的监听器并将数据传递给它