Angular指令从NgModel或FormControlName获取值

问题描述

我有一条指令,用于检查表单组件是使用Reactive Forms还是由Template-Driven创建的。我使用它来维护使用同一指令的其他组件之间的兼容性。在Angular 8中所有工作都很好,但在Angular 9中却没有。

在我的指令中,我有一个方法获取和设置字段值,将文件状态设置为脏或被触摸,但是要做到这一点,我有条件知道组件使用了什么形式。

不幸的是,在Angular 9中,我无法做到这一点,因为我不知道该怎么做。拜托,如果有人可以帮助我,我会很感激。

遵循我的指令

@Directive({
  selector: '[appSelect2Cities]',})
export class Select2CitiesDirective implements OnChanges,AfterContentChecked,AfterViewInit {
  input: any;

  // Cast one of the types to a variable
  @ContentChild(NgModel,{ static: false }) model: NgModel;
  @ContentChild(FormControlName,{ static: false }) control: FormControlName;

  // ... some code here

  ngOnChanges(changes: SimpleChanges): void {
    if (this.model) {
      // ... some code here
    } else if (this.control) {
      // ... some code here
    }
  }

  ngAfterViewInit(): void {
    this.input = this.model || this.control;

    if (this.input === undefined) { // <-- Always undefined here
      throw new Error('That directive needs to be used by ngModel or FormControlName');
    }

    this.initializeSelect2();
  }
    
  // ... some code here
}

我使用反应式表单的示例

<select
  appSelect2Cities
  class="form-control"
  formControlName="cities"
  [value]="citiesValue"
  placeholder="Select a City"
  (itemSelected)="onCitySelected($event)"
>
</select>

和我使用模板驱动的示例

<select
  appSelect2Cities
  class="form-control"
  name="city"
  [(ngModel)]="address.city"
>
</select>

解决方法

最后,我找到了解决问题的答案!

我不完全知道为什么@ContentChild在这种情况下不能以相同的方式工作,但是我认为这与Angular团队所做的更改有关,以解决查询中的错误和令人惊讶的行为。看到https://angular.io/guide/static-query-migration

在我的情况下,解决方案只是在构造函数中声明NgModelFormControlName,而不是像波纹管那样使用@ContentChold

@Directive({
  selector: '[appSelect2Cities]',})
export class Select2CitiesDirective implements OnChanges,AfterContentChecked,AfterViewInit {
  input: any;

  constructor(@Optional() private model: NgModel,@Optional() private control: FormControlName) {}

  // ... some code here

  ngOnChanges(changes: SimpleChanges): void {
    if (this.model) {
      // ... some code here
    } else if (this.control) {
      // ... some code here
    }
  }

  ngAfterViewInit(): void {
    this.input = this.model || this.control;

    if (this.input === undefined) { // <-- Now works fine
      throw new Error('That directive needs to be used by ngModel or FormControlName');
    }

    this.initializeSelect2();
  }
    
  // ... some code here
}