【问题标题】:Get access to FormControl from the custom form component in Angular从 Angular 中的自定义表单组件访问 FormControl
【发布时间】:2017-11-27 15:37:47
【问题描述】:

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

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

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

【问题讨论】:

    标签: angular angular-forms


    【解决方案1】:

    这个解决方案诞生于 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 注入组件,然后使用通过 formControlName 绑定获得的控件名称从中获取特定的 FormControl

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

    【讨论】:

    • 你从哪里得到this.control
    • @DAG 正如我在答案中所说:I'm injecting the parent FormGroup to the component and then getting the specific FormControl from it using control name obtained through formControlName binding.
    • @SlavaFominII: 与其注入父表单组,然后使用 formControllerName 输入绑定访问控件,我们不能只将表单控件作为输入绑定传递吗?我有一个类似的要求,我想从自定义表单组件访问表单控件,并将该表单控件作为输入绑定传递给自定义表单组件。
    • 其他对此印象深刻的人应该看这里material.angular.io/guide/…
    • IMO 在控件中发送违背了答案的目的。当然,您可以发送控件并对其执行任何操作...(并且您不需要此答案中的 ctor 中的任何内容)。问题的实质是询问如何从内部访问您正在自定义的控件。这应该是可行的。
    【解决方案2】:

    通过[formControl] 指令绑定时,使用formControlName 作为输入参数不起作用。

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

    export class MyComponent implements AfterViewInit {
    
      private control: FormControl;
    
      constructor(
        private injector: Injector,
      ) { }
    
      // The form control is only set after initialization
      ngAfterViewInit(): void {
        const ngControl: NgControl = this.injector.get(NgControl, null);
        if (ngControl) {
          this.control = ngControl.control as FormControl;
        } else {
          // Component is missing form control binding
        }
      }
    }

    【讨论】:

    • 这个解决方案肯定比公认的答案好。
    • 虽然这个解决方案对我也很有效,而且看起来比公认的解决方案优雅得多,但在 Angular 7 中我得到了这个 tslint 错误:get is deprecated: from v4.0.0 use Type<T> or InjectionToken<T>
    • 调整此解决方案以使用 InjectionToken 应该很容易。
    • 试试const ngControl = this.injector.get<NgControl>(NgControl as Type<NgControl>);
    • ngAfterViewInit() 给了我不一致的状态错误,但将其更改为 ngOnInit() 就像一个魅力!比接受的答案好多了。
    【解决方案3】:

    基于之前的答案和评论中的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
           }
        }
    }
    

    【讨论】:

    • 不错的解决方案。您可能需要在组件声明中删除 provide: NG_VALUE_ACCESSOR 以防止循环依赖。见:material.angular.io/guide/…
    【解决方案4】:

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

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

    然后您可以像这样在自定义表单组件中获取表单控件实例:

    @Input() control: FormControl;
    

    【讨论】:

      【解决方案5】:

      以下是适用于 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) { }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-04-18
        • 2017-08-17
        • 1970-01-01
        • 2019-02-21
        • 1970-01-01
        • 2017-10-27
        • 2017-07-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多