将 FormControl 传递给子组件 - 没有未指定名称的表单控件的值访问器

Hea*_*ady 5 angular angular-reactive-forms angular-forms

我有一个输入组件customInput,它创建一个经典的输入字段并为其添加一些布局香料,没有额外的逻辑。

我想将一个 formControl 传递给它,将它绑定到它包含的输入。

应该这样使用:

<form [formGroup]="form">
  <custom-input [formControl]="form.controls['control']"></custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)

内部自定义输入:

export class HidInputFieldComponent  {
   @Input() formControl: AbstractControl

   ...
}

<div class="container">
  <input [formControl]="formControl"/>
    <label>label</label>
</div>
Run Code Online (Sandbox Code Playgroud)

现在当我初始化组件时,我得到

未指定名称的表单控件没有值访问器

在我的组件构造函数中记录控件,它是未定义的。

我做错了还是没有办法解决ControlValueAccessor?由于我实际上并没有构建自定义控件(我仍然使用经典输入),因此看起来很极端

Joh*_*ido 16

您不需要导入 ControlValueAccessor 或任何类似的东西来完成它。

您需要做的就是将 FormControl 对象传递给您的子组件,如下所示:

<form [formGroup]="form">
  <custom-input [control]="form.get('theControlName')">
  </custom-input>
</form>
Run Code Online (Sandbox Code Playgroud)

这意味着您的自定义输入组件应如下所示:

import {Component, Input} from '@angular/core';
import {FormControl} from '@angular/forms';

@Component({
  selector: 'custom-input',
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.scss']
})
export class InputComponent {
  @Input() control: FormControl;
}
Run Code Online (Sandbox Code Playgroud)

和模板:

<input [formControl]="control">
Run Code Online (Sandbox Code Playgroud)

就是这样。

如果您实现这样的自定义输入,您将不必将父 formGroup 带入子组件逻辑中,则在那里完全没有必要(除非您需要对其或其他表单控件进行一些操作)。

此外,将 FormControl 对象传递给自定义输入将使您可以访问它的属性,而无需引用 FormGroup 然后获取特定控件,因为这是在父组件上完成的工作。

我希望这个解决方案有助于简化许多人的工作,因为制作这种自定义控件很常见。


im.*_*tov 10

Johann Garrido 的这个答案很好,但是它引入了额外的输入[control],您在使用自定义组件时需要始终牢记这些输入。

另外,它直接与工作相关,ReactiveFormsModule因为它只接受FormControl实例。

我认为更好的方法是实现ControlValueAccessor接口,但利用一些解决方法来不重复控制处理:

export const NOOP_VALUE_ACCESSOR: ControlValueAccessor = {
  writeValue(): void {},
  registerOnChange(): void {},
  registerOnTouched(): void {}
};
Run Code Online (Sandbox Code Playgroud)

NOOP_VALUE_ACCESSOR在包装表单控件的组件中使用:

@Component({
  selector: "wrapped-input",
  template: `
    <mat-form-field class="example-full-width">
      <mat-label>Wrapped input</mat-label>

      <!--We pass NgControl to regular MatInput -->
      <input matInput [formControl]="ngControl.control" />
    </mat-form-field>
  `
})
export class WrappedInput {
  constructor(@Self() @Optional() public ngControl: NgControl) {
    if (this.ngControl) {
      // Note: we provide the value accessor through here, instead of
      // the `providers` to avoid running into a circular import.
      // And we use NOOP_VALUE_ACCESSOR so WrappedInput don't do anything with NgControl
      this.ngControl.valueAccessor = NOOP_VALUE_ACCESSOR;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这样 Angular 就会认为它WrappedInput像任何其他实现ControlValueAccessor接口的组件一样工作(MatInput例如)。

另外,它可以与ReactiveFormsModule和 常规一起使用FormsModule

WrappedInput可以像这样使用:

<wrapped-input [formControl]="sourceControl"></wrapped-input>
Run Code Online (Sandbox Code Playgroud)

这是您可以使用的完整工作 stackblitz: https://stackblitz.com/edit/angular-wrapped-form-control-example ?file=src/app/app.ts


Che*_*pan 4

使用FormGroup 指令

该指令接受现有的 FormGroup 实例。然后,它将使用此 FormGroup 实例将任何子 FormControl、FormGroup 和 FormArray 实例与子 FormControlName、FormGroupName 和 FormArrayName 指令相匹配。

这样做你可以从父级访问子 formControl

  @Component({
    selector: 'app-custom-input',
    templateUrl: './custom-input.html',
    viewProviders:[{ provide: ControlContainer, useExisting: FormGroupDirective}]
  })    
    export class HidInputFieldComponent  {
     constructor(private fcd:FormGroupDirective) {       
    }
    ngOnInit() {
        this.fcd.form.addControl('formControl',new FormControl(''));
    }
    }

    <div class="container">
      <input [formControl]="formControl"/>
        <label>label</label>
    </div>
Run Code Online (Sandbox Code Playgroud)

  • 您也可以以子形式进行验证。如果您想了解有关嵌套表单的更多信息,请观看此 https://www.youtube.com/watch?v=CD_t3m2WMM8 (2认同)