从Angular中的自定义表单组件访问FormControl

时间:2017-06-24 01:29:52

标签: angular angular-forms

我的Angular应用程序中有一个自定义表单控件组件,它实现了ControlValueAccessor接口。

但是,我想访问与我的组件关联的FormControl实例。我使用FormBuilder的反应式表单,并使用formControlName属性提供表单控件。

那么,如何从我的自定义表单组件中访问FormControl实例?

6 个答案:

答案 0 :(得分:41)

此解决方案诞生于Angular存储库中的the discussion。如果您对此问题感兴趣,请务必阅读或更好地参与。

我研究了FormControlName指令的代码,这激励我编写以下解决方案:

@Component({
  selector: 'my-custom-form-component',
  templateUrl: './custom-form-component.html',
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: CustomFormComponent,
    multi: true
  }]
})
export class CustomFormComponent implements ControlValueAccessor, OnInit {

  @Input() formControlName: string;

  private control: AbstractControl;


  constructor (
    @Optional() @Host() @SkipSelf()
    private controlContainer: ControlContainer
  ) {
  }


  ngOnInit () {

    if (this.controlContainer) {
      if (this.formControlName) {
        this.control = this.controlContainer.control.get(this.formControlName);
      } else {
        console.warn('Missing FormControlName directive from host element of the component');
      }
    } else {
      console.warn('Can\'t find parent FormGroup directive');
    }

  }

}

我正在将父FormGroup注入组件,然后使用通过FormControl绑定获得的控件名称从中获取特定的formControlName

但是,请注意,此解决方案专门针对在host元素上使用FormControlName指令的用例而定制。它在其他情况下不起作用。为此,您需要添加一些额外的逻辑。如果您认为这应该由Angular解决,请务必访问the discussion

答案 1 :(得分:21)

通过const mousePositionControl = new MousePosition({ coordinateFormat: createStringXY(4), projection: 'EPSG:4326', className: 'custom-mouse-position', target: document.getElementById('mouse-position'), undefinedHTML: ' ' }); const map = new Map({ controls: defaultControls({ attributionOptions: { collapsible: false } }).extend([mousePositionControl]), layers: [ new TileLayer({ source: new OSM() }) ], target: 'map', view: new View({ center: [0, 0], zoom: 2 }) }); 指令绑定时,使用formControlName作为输入参数是无效的。

这是一种无需任何输入参数即可双向工作的解决方案。

[formControl]

答案 2 :(得分:6)

基于先前的答案和评论中发现的documentation,这是我认为是基于ControlValueAccessor的组件的最干净的解决方案。

// No FormControl is passed as input to MyComponent
<my-component formControlName="myField"></my-component>
export class MyComponent implements AfterViewInit, ControlValueAccessor  {

  constructor(@Optional() @Self() public ngControl: NgControl) {
    if (ngControl != null) {
      // Setting the value accessor directly (instead of using
      // the providers) to avoid running into a circular import.
      ngControl.valueAccessor = this;
    }
  }

    ngAfterContentInit(): void {
       const control = this.ngControl && this.ngControl.control;
       if (control) {
          // FormControl should be available here
       }
    }
}

答案 3 :(得分:4)

由于@Ritesh已在评论中写入,您可以将表单控件作为输入绑定传递:

<my-custom-form-component [control]="myForm.get('myField')" formControlName="myField">
</my-custom-form-component>

然后您可以在自定义表单组件中获取表单控件实例,如下所示:

@Input() control: FormControl;

答案 4 :(得分:1)

对于任何在2019年使用Angular 6/7 +来此访问的人,此答案中所描述的解决方案均不会引发弃用警告:

https://stackoverflow.com/a/56061527/134120

有关更多详细信息,请观看this presentation,如上所述。

答案 5 :(得分:0)

这里是接受的答案的简化/清理版本,适用于FormControlName和FormControl输入:

export class CustomFormComponent implements ControlValueAccessor, OnInit {

  @Input() formControl: FormControl;

  @Input() formControlName: string;

  // get ahold of FormControl instance no matter formControl or formControlName is given.
  // If formControlName is given, then controlContainer.control is the parent FormGroup/FormArray instance.
  get control() {
    return this.formControl || this.controlContainer.control.get(this.formControlName);
  }

  constructor(private controlContainer: ControlContainer) { }
}