首页 > 解决方案 > Angular 指令从 NgModel 或 FormControlName 获取值

问题描述

我有一个指令来检查表单组件是使用反应式表单还是模板驱动创建的。我用它来维护使用相同指令的其他组件之间的兼容性。在 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>

标签: angularcompatibilitydirective

解决方案


终于,我找到了我的问题的答案!

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

在我的情况下,解决方案只是在构造函数中声明NgModelandFormControlName而不是使用@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
}

推荐阅读