最简单的ControlValueAccessor无法正常工作

时间:2019-07-08 12:32:56

标签: angular

我创建了一个BaseInputComponent实现ControlValueAccessor的组件。 然后创建一个组件QuickReplyButtonComponent,该组件在BaseInputComponent组件上方延伸。 然后,我将QuickReplyButtonComponent用作AppComponent中的表单控件。 但是我遇到了错误:

下面的事件代码。

错误:

AppComponent.html:4 ERROR Error: StaticInjectorError(AppModule)[InjectionToken NgValueAccessor -> BaseInputComponent]: 
  StaticInjectorError(Platform: core)[InjectionToken NgValueAccessor -> BaseInputComponent]: 
    NullInjectorError: No provider for BaseInputComponent!
    at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:8896)
    at resolveToken (core.js:9141)
    at tryResolveToken (core.js:9085)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:8982)
    at resolveToken (core.js:9141)
    at tryResolveToken (core.js:9085)
    at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:8982)
    at resolveNgModuleDep (core.js:21218)
    at NgModuleRef_.push../node_modules/@angular/core/fesm5/core.js.NgModuleRef_.get (core.js:21907)
    at resolveDep (core.js:22278)

BaseInputComponent:

import {ChangeDetectorRef, Component, Injector, Input, Type} from '@angular/core';
import {ControlValueAccessor} from '@angular/forms';

@Component({
  selector: 'app-base',
  template: ' '
})
export class BaseInputComponent<T = any> implements ControlValueAccessor {

  // @Input()
  // cid: string = uuid();

  @Input()
  disabled: boolean;

  @Input()
  set value(value: T) {
    this._value = value;
    this.notifyValueChange();
  }

  get value(): T {
    return this._value;
  }

  onChange: (value: T) => {};
  onTouched: () => {};

  protected _value: T;
  protected cdRef: ChangeDetectorRef;

  constructor(public injector: Injector) {
    this.cdRef = injector.get<ChangeDetectorRef>(ChangeDetectorRef as Type<ChangeDetectorRef>);
  }

  notifyValueChange(): void {
    if (this.onChange) {
      this.onChange(this.value);
    }
  }

  writeValue(value: T): void {
    this._value = value;
    setTimeout(() => this.cdRef.detectChanges(), 0);
  }

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

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

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

}

QuickReplyButtonComponent:

import {Component, forwardRef, OnInit} from '@angular/core';
import {NG_VALUE_ACCESSOR} from '@angular/forms';
import {BaseInputComponent} from './base-input/base-input.component';

@Component({
  selector: 'app-quick-reply-button',
  template: `
    <input type="text" [ngModel]="value">
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => BaseInputComponent),
      multi: true,
    },
  ]
})
export class QuickReplyButtonComponent extends BaseInputComponent implements OnInit {

  ngOnInit() {
  }

  updateButton($event, index) {

  }

}

AppComponent:

import {ChangeDetectionStrategy, Component, DoCheck, OnInit} from '@angular/core';
import {FormArray, FormBuilder, FormControl, FormGroup} from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
      <i>test</i>
      <form [formGroup]="form">
          <app-quick-reply-button formControlName="name"></app-quick-reply-button>
      </form>
  `,
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private formBuilder: FormBuilder) {
  }


  form: FormGroup;

  ngOnInit(): void {
    this.form = this.createTestForm();
  }

  createTestForm() {
    return this.formBuilder.group({
      name: 'test name',
    });
  }
}

AppModule:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {RouterModule} from '@angular/router';
import {TodosComponent} from './todos.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {QuickReplyButtonComponent} from './test-input.component';
import {BaseInputComponent} from './base-input/base-input.component';

@NgModule({
  declarations: [
    AppComponent,
    TodosComponent,
    QuickReplyButtonComponent,
    BaseInputComponent
    // BaseInputComponent,
  ],

  // entryComponents:[
  //   BaseInputComponent
  // ],

  imports: [
    BrowserModule,
    RouterModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

1 个答案:

答案 0 :(得分:1)

这是因为从未创建BaseInputComponent的实例。尽管QuickReplyButtonComponent扩展了BaseInputComponent,但是角度依存注入无法那样工作。您可以执行以下任一操作

1-更改

providers: [{
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => BaseInputComponent),
  multi: true,
}]

providers: [{
  provide: NG_VALUE_ACCESSOR,
  useExisting: QuickReplyButtonComponent,
  multi: true,
}]

2-首先提供BaseInputComponent,并保留您的NG_VALUE_ACCESSOR提供者。

providers: [{
  provide: BaseInputComponent,
  useExisting: QuickReplyButtonComponent,
  multi: true,
}, {
  provide: NG_VALUE_ACCESSOR,
  useExisting: BaseInputComponent,
  multi: true,
}]

但是第一个选择是更清洁。