简体   繁体   English

将父FormArray注入Angular 14中的子组件

[英]Inject parent FormArray into child component in Angular 14

In my parent component, I have a FormGroup with a FormArray , and I want to handle that array in a child component.在我的父组件中,我有一个带有FormGroupFormArray ,我想在子组件中处理该数组。 The parent's HTML does this:父母的 HTML 这样做:

<ng-container [formGroup]="formGroup">
  <app-child formArrayName="theArrayName">

I assumed in the child I would inject the NgControl and then have access:我假设在孩子身上我会注入NgControl然后可以访问:

@Component({
  ...,
  providers: [
    {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => ChildComponent),
        multi: true
    }
  ]
})
export class ChildComponent implements ControlValueAccessor {
    constructor(private readonly control: NgControl) {
      this.formArray = control.control as FormArray<...>
    }

I always get a null injector saying there's no provider for NgControl .我总是得到一个 null 注射器说没有NgControl的提供者。

NG_VALUE_ACCESSOR is a provider to create a custom form control not a custom form array. NG_VALUE_ACCESSOR 是创建自定义表单控件而不是自定义表单数组的提供程序。 You should define a ChildComponent as a FormControl.您应该将 ChildComponent 定义为 FormControl。 Refer this how to do it.参考这个怎么做。

And then you should use FormArray like然后你应该像这样使用 FormArray

<ng-container [formGroup]="formGroup">
  <ng-container formArrayName="theArrayName" >
    <ng-container *ngFor="let control of formGroup.controls.theArrayName.controls; let i = index">
      <app-child [formControlName]="i"></app-child>
    </ng-container>  
  </ng-container>
</ng-container>

Maybe I'm missing some information, but the simplest way to do that is to just use an input property.也许我遗漏了一些信息,但最简单的方法就是使用输入属性。

parent ts父母 ts

export class ParentComponent {
  formGroup = new FormGroup(...);

  get formArray() {
    return this.formGroup.get('theArrayName') as FormArray;
  }
}

child ts儿童 ts

export class ChildComponent implements OnInit {
  @Input() formArray = new FormArray([]);

  ngOnInit() {
    console.log(this.formArray);
  }
}

parent html父 html

<app-child [formArray]="formArray"></app-child>

Julian lin could work, but there is a simple way to do it as well without the ControlValueAccessor if you know you are gonna use a FormGroup instead the array. Julian lin 可以工作,但是如果您知道要使用 FormGroup 而不是数组,那么也有一个简单的方法可以在没有 ControlValueAccessor 的情况下做到这一点。

So in the ChildComponent you have所以在 ChildComponent 你有

export class ChildComponent implements OnInit{
 formGroup: FormGroup
 constructer (private formCtrl: FormGroupDirective){}
 ngOnInit(){
   // only available after ngOnInit
   this.formGroup = formCtrl.form;
 }
}

in the parent component template you would set it something like this在父组件模板中,您可以将其设置为这样

<ng-container *ngFor="let someGroup of formArray.controls">
  <app-child-component [formGroup]="someGroup" ></app-child-component>
</ng-container>

then you can use the parent to still detect if there is an validation error in the child forms.那么您可以使用父级仍然检测子级 forms 中是否存在验证错误。

Complementary the Chris Hamilton's answer补充克里斯汉密尔顿的答案

<app-child [formArray]="form.get('formArrayNme')"></app-child>

The problem when you mannage a formArray in a child that you pass as input is that you can not use the "typical" constructor of manage FormArrays.当您在作为输入传递的孩子中管理 formArray 时,问题是您不能使用管理 FormArrays 的“典型”构造函数。 You should define a function (*)您应该定义一个 function (*)

    //if is a FormArray of FormControls
    getControl(index:number)
    {
        return this.formArray.at(index) as FormControl
    }
    //if is a FormArray of FormGroup
    getGroup(index:number)
    {
        return this.formArray.at(index) as FormGroup
    }

And use并使用

    <!--if is a formArray of FormControls-->
    <div *ngFor="let control of FormArray;let i=index">
       <input [formControl]=getControl(i)>
    </div>
    
    <!--if is a formArray of FormGroups-->
    <div *ngFor="let group of FormArray;let i=index" [formGroup]="getGroup(i)>
       <input formControlName="prop1">
       <input formControlName="prop2">
    </div>

If we want to use the typical FormArray with formArrayName we need viewProvider the FormGroupDirective and know the name.如果我们想使用带有 formArrayName 的典型 FormArray,我们需要 viewProvider 和 FormGroupDirective 并知道名称。 We can do using a child-control like我们可以使用类似的子控件

    @Component({
      selector: 'child-array',
      templateUrl: 'child-array.html',
      viewProviders:[
         { provide: ControlContainer, useExisting: FormGroupDirective }]
    })
    
    export class ChildComponent  {
      array:FormArray
      arrayName:string="fool"
      @Input() name: string;
      @Input('array') set _(value)
      {
        this.array=value as FormArray
        this.arrayName=Object.keys(this.array.parent.controls)
            .find(key=>this.array.parent.get(key)==this.array)
      }
    }

Now we can use现在我们可以使用

      <!--if is a formArray of FormControls-->
      <div [formArrayName]="arrayName">
        <div *ngFor="let control of array.controls;let i=index">
          <input [formControlName]="i">
        </div>
      </div>
    
      <!--if is a formArray of FormGroups-->
      <div [formArrayName]="arrayName">
        <div *ngFor="let control of array.controls;let i=index" [formGroupName]="i">
          <input formControlName="prop1">
          <input formControlName="prop2">
        </div>
      </div>

This second approach (in the case of formArray of FormControls) can be see in this stackblitz第二种方法(在 FormControls 的 formArray 的情况下)可以在这个stackblitz中看到

(*)I know that some authors use the variable of the loop to get the value of the formControl or the FormGroup (*)我知道有些作者使用循环的变量来获取formControl或FormGroup的值

<div *ngFor="let group of formArray.controls" [formGroup]="group">

Unfortunaly, this don't work since Angular 12, because group is only an "AbstractControl".不幸的是,这在 Angular 12 之后不起作用,因为 group 只是一个“AbstractControl”。 If you has strict mode you received an error saying you that an AbstractControl is not a FormGroup (works in early Angular versions).如果您有严格模式,您会收到一条错误消息,指出 AbstractControl 不是 FormGroup(适用于早期的 Angular 版本)。

Some suggest use the $any, but (personal opinion) is a "ugly work-around" or even de-activate the strict mode (it's a very very very bad idea and this last is not a personal opinion)有人建议使用 $any,但是(个人意见)是一种“丑陋的解决方法”,甚至停用严格模式(这是一个非常非常非常糟糕的主意,最后这不是个人意见)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM