Angular Reactive Forms,将数据对象与FormArray的每个对象绑定的方法

时间:2019-03-08 05:30:11

标签: angular angular-reactive-forms reactive-forms

我正在建立反应式,如下图所示-

如图所示,每个文件(在代码中称为附件)中都有多个议程。议程可以添加,更新和删除

enter image description here

buildForms() {
this.agendaForms = this.formBuilder.group({
  attachements: this.formBuilder.array([

  ])
});

从Web服务获取表单数据,并以这种方式创建表单模型-

data => {
    this.submission = data;

    // New blank agenda for all attachements
    if (
      !isNullOrUndefined(this.submission) &&
      !isNullOrUndefined(this.submission.attachments)
    ) {
      this.submission.attachments.forEach(attachment => {
        const agenda = new NLAgenda();
        agenda.dwgNo = attachment.filename;
        agenda.dWGNo = attachment.filename;
        this.submission.agendas.push(agenda);

        if (!isNullOrUndefined(attachment)) {
          attachment.agendas = this.getAgendasForAttachment(attachment);

          if (!isNullOrUndefined(attachment.agendas)) {
            this.attachmentFormArray = this.agendaForms.controls
              .attachements as FormArray;
            this.attachmentFormArray.push(
              this.createAttachmentAgendasControl(attachment.agendas)
            );
          }
        }
      });
    }
  }

模板看起来像这样

<form [formGroup]="agendaForms">
    <div formArrayName="attachements">
      <div *ngFor="let attachmentFormGroup of attachmentFormArray.controls;
          let attachmentId = index">

        <!-- Attachment header-->
        <div>
          <!-- File Name-->
          <div>
            {{ submission.attachments[attachmentId].filename }}
          </div>

          <!-- Action Buttons-->
          <div>
            <input type="button" value="Link To"/>
          </div>
        </div>

        <!-- Agendas -->
        <div formGroupName="{{ attachmentId }}">
          <div formArrayName="agendas">
            <div *ngFor="let agendaFormGroup of attachmentFormGroup.controls.agendas.controls;
                let agendaId = index">
              <div formGroupName="{{ agendaId }}" >
                <mat-form-field>
                  <input
                    type="text"
                    id="project"
                    placeholder="Sheet No."
                    formControlName="sheetNumber"
                    matInput
                  />
                  <!-- <mat-error>{{ getErrorMessage(f.project) }}</mat-error> -->
                </mat-form-field>
                <mat-form-field>
                  <input
                    type="text"
                    id="project"
                    placeholder="Title"
                    formControlName="title"
                    matInput
                  />
                </mat-form-field>
                <mat-form-field>
                  <input
                    type="text"
                    id="project"
                    placeholder="Description"
                    formControlName="description"
                    matInput
                  />
                </mat-form-field>

                <div>
                  <a
                    matTooltip="Add Agenda"
                    aria-label="Add Agenda"
                    (click)="
                      createOrUpdateAgenda(
                        submission.attachments[attachmentId].agendas[agendaId],
                        attachmentId,
                        agendaId
                      )">
                    <i class="fa fa-check"></i>
                  </a>
                  <a
                    *ngIf="submission.attachments[attachmentId].agendas[agendaId].created != null && submission.attachments[attachmentId].agendas[agendaId].created != undefined"
                    matTooltip="Delete Agenda"
                    aria-label="Delete Agenda"
                    (click)="deleteAgenda(submission.attachments[attachmentId].agendas[agendaId])">
                    <i class="fa fa-remove"></i>
                  </a>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </form>

问题: 每当添加或更新议程时,我都会从服务器中获取提交对象数据,以显示文件和议程的更新状态,提交对象发生更改,并在控制台中显示错误,例如“无法读取未定义的属性'agendas'”,因为旧对象处于新提交对象位置因新增或删除而更改

我相信我只需要使用一个数组(FormArray)而不是两个数组(Form array和Submission Object)来构建模板,否则如果一个更改直到其他更改控制台将引发错误。但是如何仅使用FormArray,我在Submission Object中有一些数据?有没有办法用FormArray绑定提交对象?

我很累https://github.com/angular/angular/issues/13845,但是由于我使用表单构建而没有成功,并且不知道如何使用表单构建器来完成此技巧

1 个答案:

答案 0 :(得分:0)

如果您首先考虑所获得的数据,则更容易。我想你会得到像

[{filename:..,agenda:[
                     {sheetNumber:..,title:...,description...},
                     {sheetNumber:..,title:...,description...}
                          ...
                     ]
  },
  {filename:..,agenda:[
                     {sheetNumber:..,title:...,description...},
                     {sheetNumber:..,title:...,description...}
                          ...
                     ]
  }
  ....
]

您看到您有一个FormArray(一系列FormGroup),它具有两个属性“文件名”和“议程”(议程是一个新的FormArray)

将变量声明为formArray

  form: FormArray

并创建两个函数。

createItemData(data):FormGroup
  {
    data=data|| {} as dataI;
    return new FormGroup(
      {
         filename:new FormControl(data.filename),
         agenda:new FormArray(data.agenda && data.agenda.length?
               data.agenda.map(agenda=>this.createAgendaSheet(agenda)):
               [])
      }
    )

  }
  createAgendaSheet(data):FormGroup{
    data=data|| {} as agendaI;
    return new FormGroup(
      {
        sheetNumber:new FormControl(data.sheetNumber),
        title:new FormControl(data.title),
        description:new FormControl(data.description),

      }
    )
  }

查看此函数如何创建数组的元素。我使用接下来的两个界面来帮助我创建表单

export interface agendaI {
  sheetNumber: number,
  title: string,
  desription: string
}
export interface dataI {
  filename: string,
  agenda: agendaI[]
}

好吧,创建表单时要小心:

<button (click)="addAgenda()">Add agenda</button>
<form *ngIf="form" [formGroup]="form">
    <!--form is a FormArray, so form.controls will be formGroup-->
    <div *ngFor="let control of form.controls;let i=index">
       <!--in this form group...-->
        <div [formGroup]="control">
            <!--we have a fromControl "filename"-->
            <input formControlName="filename"/><button (click)="add(i)">add</button>
      <!--and a FormArray "agenda"--->
      <div formArrayName="agenda">
        <div *ngFor="let c of control.get('agenda').controls;let j=index" >
          <div [formGroupName]="j">
            <input formControlName="sheetNumber">
            <input formControlName="title">
            <input formControlName="description">
            <button (click)="delete(i,j)">delete</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</form>

好吧,最后一步是创建函数add,delete和addAgenda

addAgenda()
  {
    this.form.push(this.createItemData(null))
  }
  add(i)
  {
    (this.form.at(i).get('agenda') as FormArray).push(this.createAgendaSheet(null))
  }
  delete(i,j)
  {
    (this.form.at(i).get('agenda') as FormArray).removeAt(j)
  }

然后,在ngOnInit中创建表单(我使用了常量“数据”)

 ngOnInit() {
    this.form=new FormArray(data.map(d=>this.createItemData(d)));
  }

您可以在stackblitz中看到“丑陋”的结果