嵌套的自定义 FormArray 组件不与具有 FormArrayName 的子表单绑定 [英] nested custom FormArray component doesn't bind with child form with FormArrayName

查看:24
本文介绍了嵌套的自定义 FormArray 组件不与具有 FormArrayName 的子表单绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用 CVA 构建 2 个嵌套表单.问题是当我将它绑定到 formControl 时,第二个 from 没有用数据初始化.

我有MAIN-FORM:

this.requestForm = this.fb.group({车库编号:0,路线:新的FormArray([新表单组({addressPointId:新的 FormControl,市政ID:新的FormControl,regionId:新的表单控件,rvId:新的表单控件,序列号:新的表单控件,结算ID:新的FormControl,区域名称:新的表单控件,市政名称:新的表单控件,结算名称:新的表单控件,描述:新的表单控件,})]),结束日期时间:0,});

在主表单 html 中,我使用 formArrayName 绑定路由.

 <app-cva-form-array formArrayName="routes"></app-cva-form-array>

组件 CVA-FORM-ARRAY 有.

form = new FormArray([新表单组({addressPointId:新的 FormControl,市政ID:新的FormControl,regionId:新的表单控件,rvId:新的表单控件,序列号:新的表单控件,结算ID:新的FormControl,区域名称:新的表单控件,市政名称:新的表单控件,结算名称:新的表单控件,描述:新的表单控件,})]);

这里的一切都很好.我将数组中的每个 formGroup 绑定到子组件 CVA-FORM.

<app-cva-form [formControl]="route" (blur)="onTouched()"></app-cva-form>

CVA-FORM我为每个 formGroup 创建了单独的组件,以防我想使用组件本身而不是整个数组.

 表单:FormGroup = new FormGroup({区域名称:新的表单控件,regionId:新的表单控件,市政名称:新的表单控件,市政ID:新的FormControl,序列号:新的表单控件,结算名称:新的表单控件,结算ID:新的FormControl,addressPointId:新的 FormControl,描述:新的表单控件,rvId:新的表单控件,});

主表单 <--to--> app-cva-form-array 绑定由于某种原因不起作用.

这些表单的想法来自 kara 关于 angulaconnect 的演讲. 解决方案

当您使用自定义表单控件"时,您需要考虑使用表单控件(不是 FormArray,不是 FormGroup)为 Cursom 表单控件提供内容.FormControl 的值是一个数组或一个对象,但您不必对此感到困惑.(*)

您可以在 堆叠闪电战

这就是你的表格

//在main.form中this.requestForm = new FormGroup({车库 ID:新的 FormControl(0),routes: new FormControl(routes),//<--routes 将是一个对象数组结束日期时间:新表单控件(0)})//在cva-form-array中this.form=new FormArray([new FormControl(...)]);//<-this.form 是一个//FormControls 的formArray 不是formGroup//最终以你的cva形式this.form=new FormGroup({});this.form=formGroup({addressPointId: new FormControl(),市政ID:新的FormControl(),...})

我创建了一个常量来导出为简单的代码.我的常量导出是

export const dataI = {addressPointId: "",市政编号:"",区域 ID: "",rvId: "",序列号: "",结算ID:"",区域名称:"",市政名称:"",结算名称:"",描述: "",}

所以,在 mainForm 中我们有

 ngOnInit() {让路线:任何[] = [];路线.push({...dataI});this.requestForm = new FormGroup({车库 ID:新的 FormControl(0),路线:新的FormControl(路线),结束日期时间:新表单控件(0)})}<mat-card [formGroup]="requestForm" style="background: #8E8D8A"><app-cva-form-array formControlName="routes"></app-cva-form-array></mat-card>

在cvc-form数组中,当我们给值时创建formArray

 writeValue(v: any) {this.form=new FormArray([]);对于(让 v 的值)this.form.push(new FormControl(value))this.form.valueChanges.subscribe(res=>{如果(this.onChange)this.onChange(this.form.value)})}<form [formGroup]="form" ><mat-card *ngFor="let route of form.controls;让 routeIndex = 索引;让 routeLast = last;"><按钮(点击)="deleteRoute(routeIndex)">取消<app-cva-form [formControl]="route" (blur)="onTouched()"></app-cva-form></表单>

最后是 cva 形式

 writeValue(v: any) {this.form=new FormGroup({});Object.keys(dataI).forEach(x=>{this.form.addControl(x,new FormControl())})this.form.setValue(v, { emitEvent: false });this.form.valueChanges.subscribe(res=>{如果(this.onChanged)this.onChanged(this.form.value)})}<div [formGroup]="表单"><mat-form-field class="locationDate"><input formControlName="regionName"><mat-autocomplete #region="matAutocomplete"(optionSelected)="selectedLocation($event)"><mat-option *ngFor="让区域的区域"[值]="区域">{{region.regionName}}</mat-option></mat-autocomplete></mat-form-field><mat-form-field class="locationDate"><input formControlName="市政名称"[matAutocomplete]="市政"(模糊)="onTouched()"[只读]="已检查 || this.form.value.regionId <1">....</表单>

(*) 是的,我们习惯于看到一个 FormControl 的值是一个字符串或一个数字,但没有人禁止我们该值是一个对象或一个数组(例如,ng-bootstrap DatePicker 存储一个object {year: .. month: .., day ..},mat-multiselect 存储一个数组,...)

更新当然,我们可以使用来自服务或类似服务的数据来提供我们的控件.我们唯一必须考虑的是我们如何提供数据.通常我喜欢制作一个接收数据或空值并返回一个 FormControl 的函数

 getForm(data: any): FormGroup {数据 = 数据 ||{} 作为 IData;返回新的表单组({车库ID:新的FormControl(data.garageId),路线:新的FormControl(data.routes),endDateTime: new FormControl(data.endDateTime)})}

其中 IData 是一个接口

导出接口 IData {车库编号:编号;路线:IDetail[];结束日期时间:任意}

和 IDetail 另一个接口

导出接口 IDetail {addressPointId:字符串;...描述:字符串;}

然后我们可以有一个复杂的数据,比如(对不起,大对象)

让数据 = {车库编号:1,路线:[{addressPointId: "地址",市政ID:市政",regionId: "regionId",rvId: "rvId",序列号:序列号",结算ID:结算ID",区域名称:区域名称",市政名称:市政名称",结算名称:结算名称",描述:描述",},{addressPointId: "另一个地址",自治市Id:另一个自治市",regionId: "另一个 regionId",rvId: "另一个 rvId",sequenceNumber: "另一个序列号",结算ID:另一个结算ID",区域名称:另一个区域名称",市政名称:另一个市政名称",结算名称:另一个结算名称",描述:另一种描述",}],结束日期时间:新日期()}

然后只需要make

this.requestForm = this.getForm(data);

更新后的stackblitz

I tried to have 2 nested forms using CVA. the problem is the second from isn't initialized with data when I bind it to a formControl.

Stackblitz

I have MAIN-FORM:

this.requestForm = this.fb.group({
  garageId: 0,
  routes: new FormArray([
    new FormGroup({
      addressPointId: new FormControl,
      municipalityId: new FormControl,
      regionId: new FormControl,
      rvId: new FormControl,
      sequenceNumber: new FormControl,
      settlementId: new FormControl,
      regionName: new FormControl,
      municipalityName: new FormControl,
      settlementName: new FormControl,
      description: new FormControl,
    })
  ]),
  endDateTime: 0,
});

In main-form html I bind routes to with formArrayName.

 <app-cva-form-array formArrayName="routes"></app-cva-form-array>

Component CVA-FORM-ARRAY has.

form = new FormArray([
new FormGroup({
  addressPointId: new FormControl,
  municipalityId: new FormControl,
  regionId: new FormControl,
  rvId: new FormControl,
  sequenceNumber: new FormControl,
  settlementId: new FormControl,
  regionName: new FormControl,
  municipalityName: new FormControl,
  settlementName: new FormControl,
  description: new FormControl,
})
]);

Everything from here works just fine. I bind each formGroup in the array to child component CVA-FORM.

<app-cva-form [formControl]="route" (blur)="onTouched()"></app-cva-form>

CVA-FORM for each formGroup I created separate component in case I want to use component itself not the whole array.

  form: FormGroup = new FormGroup({
    regionName: new FormControl,
    regionId: new FormControl,
    municipalityName: new FormControl,
    municipalityId: new FormControl,
    sequenceNumber: new FormControl,
    settlementName: new FormControl,
    settlementId: new FormControl,
    addressPointId: new FormControl,
    description: new FormControl,
    rvId: new FormControl,
  });

the main-form <--to--> app-cva-form-array binding doesn't work for some reason.

The idea of these forms comes from kara's talk on angulaconnect. here are her slides.

help plz!

解决方案

When you use "custom Form Control", you need take account that you feed the cursom Form Control with a Form Control (not FormArray, not FormGroup). The FormControl has as value an array or an object, but you need not confussed about this.(*)

You can see in work in stackblitz

That's your form is like

//in main.form
this.requestForm = new FormGroup({
  garageId: new FormControl(0),
  routes: new FormControl(routes), //<--routes will be an array of object
  endDateTime: new FormControl(0)
})

//in cva-form-array
this.form=new FormArray([new FormControl(...)]); //<-this.form is a 
                             //formArray of FormControls NOT of formGroup

//finally in your cva-form
this.form=new FormGroup({});
this.form=formGroup({
      addressPointId: new FormControl(),
      municipalityId: new FormControl(),
      ...
})

I've create a const to export to simply the code. MY const expor is

export const dataI = {
  addressPointId: "",
  municipalityId: "",
  regionId: "",
  rvId: "",
  sequenceNumber: "",
  settlementId: "",
  regionName: "",
  municipalityName: "",
  settlementName: "",
  description: "",
}

So, in mainForm we have

  ngOnInit() {
    let routes:any[]=[];
    routes.push({...dataI});
    this.requestForm = new FormGroup({
      garageId: new FormControl(0),
      routes: new FormControl(routes),
      endDateTime: new FormControl(0)
    })
  }
<mat-card [formGroup]="requestForm" style="background: #8E8D8A">
    <app-cva-form-array formControlName="routes"></app-cva-form-array>
</mat-card>

In cvc-form array create the formArray when we give value

  writeValue(v: any) {
    this.form=new FormArray([]);
    for (let value of v)
        this.form.push(new FormControl(value))

    this.form.valueChanges.subscribe(res=>
    {
      if (this.onChange)
        this.onChange(this.form.value)
    })
  }

    <form [formGroup]="form" >
        <mat-card *ngFor="let route of form.controls; 
            let routeIndex = index; let routeLast = last;">
           <button (click)="deleteRoute(routeIndex)">
             cancel
           </button>
           <app-cva-form [formControl]="route" (blur)="onTouched()"></app-cva-form>
      </form>

Finally, the cva-form

  writeValue(v: any) {
    this.form=new FormGroup({});
    Object.keys(dataI).forEach(x=>{
      this.form.addControl(x,new FormControl())
    })

    this.form.setValue(v, { emitEvent: false });
    this.form.valueChanges.subscribe(res=>{
       if (this.onChanged)
        this.onChanged(this.form.value)
    })
  }

<div [formGroup]="form">
  <mat-form-field class="locationDate">
    <input formControlName="regionName">
    <mat-autocomplete #region="matAutocomplete" 
      (optionSelected)="selectedLocation($event)">
      <mat-option *ngFor="let region of regions" 
      [value]="region">
        {{region.regionName}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
  <mat-form-field class="locationDate">
    <input formControlName="municipalityName" 
      [matAutocomplete]="municipality"
      (blur)="onTouched()"
      [readonly]="checked || this.form.value.regionId < 1">
   ....
   </form>

(*) Yes, we are used to seeing that a FormControl has as a value a string or a number, but no one forbids us that the value is an object or an array (for example, ng-bootstrap DatePicker stores an object {year: .. month: .., day ..}, mat-multiselect stores an array, ...)

Update Of course we can feed our control with data from a service or similar. The only thing we must take account is how we give the data. As usually I like make a function that received a data or null and return a FormControl

  getForm(data: any): FormGroup {
    data = data || {} as IData;
    return new FormGroup({
      garageId: new FormControl(data.garageId),
      routes: new FormControl(data.routes),
      endDateTime: new FormControl(data.endDateTime)
    })
  }

where IData is an interface

export interface IData {
  garageId: number;
  routes: IDetail[];
  endDateTime: any
}

and IDetail another interface

export interface IDetail {
  addressPointId: string;
  ...
  description: string;
}

Then we can have a complex data like (sorry for the large object)

let data = {
  garageId: 1,
  routes: [{
    addressPointId: "adress",
    municipalityId: "municipallyty",
    regionId: "regionId",
    rvId: "rvId",
    sequenceNumber: "sequenceNumber",
    settlementId: "settlementId",
    regionName: "regionName",
    municipalityName: "municipalityName",
    settlementName: "settlementName",
    description: "description",
  },
  {
    addressPointId: "another adress",
    municipalityId: "another municipallyty",
    regionId: "another regionId",
    rvId: "another rvId",
    sequenceNumber: "another sequenceNumber",
    settlementId: "another settlementId",
    regionName: "another regionName",
    municipalityName: "another municipalityName",
    settlementName: "another settlementName",
    description: "another description",
  }],
  endDateTime: new Date()
}

Then only need make

this.requestForm = this.getForm(data);

The stackblitz if updated

这篇关于嵌套的自定义 FormArray 组件不与具有 FormArrayName 的子表单绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆