Angular7:需要帮助理解我在构建具有多级嵌套字段组的表单时遇到的问题

Angular 7 : Need help understanding issues I face when building a form with multilevel nested field groups

如下图所示,我在Angular中构建了一个表单 7. 表单包含多层嵌套字段组。当按下 "Add Track" 按钮时,外场组被推到 dom。然后在里面你可以按“+”在每个外部字段组中添加字段。

问题一: 我不断收到以下错误,"Add Track" 仅将完整的字段组推送到索引 1,通过它缺少带有“+”按钮的字段,这些按钮由 HTML 中的嵌套 *ngFor 循环填充。

错误:

ERROR Error: Cannot find control with path: 'albumTracks -> 1 -> audioSources -> 1'
    at _throwError (forms.js:1775)
    at setUpFormContainer (forms.js:1757)
    at FormGroupDirective.push../node_modules/@angular/forms/fesm5/forms.js.FormGroupDirective.addFormGroup (forms.js:4541)
    at FormGroupName.push../node_modules/@angular/forms/fesm5/forms.js.AbstractFormGroupDirective.ngOnInit (forms.js:1887)
    at checkAndUpdateDirectiveInline (core.js:9243)
    at checkAndUpdateNodeInline (core.js:10507)
    at checkAndUpdateNode (core.js:10469)
    at debugCheckAndUpdateNode (core.js:11102)
    at debugCheckDirectivesFn (core.js:11062)
    at Object.eval [as updateDirectives] (AddAlbumsDinamicComponent.html:103)

问题二: 当我填写表单并单击 "Save" 按钮,然后执行 onSubmit() 方法并通过以下方式获取所有输入的表单值时:"this.albumForm.value",我丢失了我输入到字段中的一堆值,这些值被推送通过按 "Add Track" 和“+”按钮动态。

试图在网上找到答案,但找不到任何与我正在尝试做的类似的东西。所有在线示例均适用于简单表单,不适用于多层嵌套表单组。 Angular 也是新手,需要帮助。 S.O.S,非常感谢您的帮助。

component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { PostRequestService } from '../post-request.service';

@Component({
  selector: 'app-add-albums-dinamic',
  templateUrl: './add-albums-dinamic.component.html',
  styleUrls: ['./add-albums-dinamic.component.css']
})

export class AddAlbumsDinamicComponent implements OnInit {

  albumForm: FormGroup;
  items: FormArray;
  arrayElement: FormArray;
  submitted = false;

  constructor(private formBuilder: FormBuilder, private postRequestService: PostRequestService) {}

  ngOnInit() {
    this.albumForm = this.formBuilder.group({
      albumTitle: '',
      albumCoverImage: '',
      datePublished: '',
      albumPurchaseSources: this.formBuilder.array([ this.createAlbumPurchaseSource() ]),
      albumTracks: this.formBuilder.array([ this.albumTracksDetails() ])
    });
  }

  createAlbumPurchaseSource(): FormGroup {
    return this.formBuilder.group({
      albumPurchaseSourceName: '',
      albumPurchaseURL: ''
    });
  }

  createAudioSource(): FormGroup {
    return this.formBuilder.group({
      audioSourceName: '',
      audioSourceURL: ''
    });
  }

  createVideoSource(): FormGroup {
    return this.formBuilder.group({
      videoSourceName: '',
      videoSourceURL: ''
    });
  }

  createTrackPurchaseSource(): FormGroup {
    return this.formBuilder.group({
      trackPurchaseSourceName: '',
      trackPurchaseSourceURL: ''
    });
  }

  albumTracksDetails(): FormGroup {
    return this.formBuilder.group({
      trackTitle: '',
      trackGenre: '',
      audioSources: this.formBuilder.array([ this.createAudioSource() ]),
      videoSources: this.formBuilder.array([ this.createVideoSource() ]),
      trackPurchaseSources: this.formBuilder.array([ this.createTrackPurchaseSource() ]),
      downloadURL: ''
    });
  }


  addAudioSource(i: number): void {
    const control = (<FormArray>this.albumForm.controls['albumTracks']).at(i).get('audioSources') as FormArray;
    control.push(this.createAudioSource());
  }

  addVideoSource(i: number): void {
    const control = (<FormArray>this.albumForm.controls['albumTracks']).at(i).get('videoSources') as FormArray;
    control.push(this.createVideoSource());
  }

  addTrackPurchaseSource(i: number): void {
    const control = (<FormArray>this.albumForm.controls['albumTracks']).at(i).get('trackPurchaseSources') as FormArray;
    control.push(this.createTrackPurchaseSource());
  }

  addTracks(): void {
    this.items = this.albumForm.get('albumTracks') as FormArray;
    this.items.push(this.albumTracksDetails());
  }

  addAlbumPurchaseSources(): void {
    this.items = this.albumForm.get('albumPurchaseSources') as FormArray;
    this.items.push(this.createAlbumPurchaseSource());
  }


  onSubmit() {
      this.submitted = true;

      this.postRequestService.
      postRequest(this.albumForm.value)
      .subscribe(
          (val) => {
              console.log("POST call successful value returned in body",
                          val);
          },
          response => {
              console.log("POST call in error", response);
          },
          () => {
              console.log("The POST observable is now completed.");
          });
  }


}

component.html

<div class="container">
  <div id="outer-container">
    <div id="sidebar">
      <div class="mycontent-left">
        <app-artist-admin-sidebar></app-artist-admin-sidebar>
      </div>
    </div>
    <div id="content">
      <form [formGroup]="albumForm" (ngSubmit)="onSubmit()" novalidate>
        <div><font size="4">Album Details:</font></div>
        <br/>
        <div class="row">
          <div class="col">
            <div class="col-sm-2 control-label"><font size="3">Album Title:</font></div>
            <div class="col-sm-3 nopadding">
              <input type="text" class="form-control" id="album_title" name="album_title" formControlName="albumTitle"
                     placeholder="Album Title">
            </div>
          </div>
        </div>

        <br/>
        <div class="row">
          <div class="col">
            <div class="col-sm-3 control-label"><font size="3">Album Cover Image:</font></div>
            <div class="col-sm-3 nopadding">
              <input type="file" class="form-control" id="cover_image" name="cover_image"
                     formControlName="albumCoverImage" placeholder="Album Cover Image">
            </div>
          </div>
        </div>

        <br/>
        <div class="row">
          <div class="col">
            <div class="col-sm-3 control-label"><font size="3">Date Published:</font></div>
            <div class="col-sm-3 nopadding">
              <input type="date" class="form-control" id="date_published" name="date_published"
                     formControlName="datePublished" placeholder="Date Published">
            </div>
          </div>
        </div>

        <br/>
        <div formArrayName="albumPurchaseSources"
             *ngFor="let item of albumForm.get('albumPurchaseSources').controls; let i = index;">
          <div [formGroupName]="i">
            <div class="row">
              <div class="col">
                <div class="col-sm-2 control-label"><font size="3">Album Purchase Source:</font></div>
                <div class="col-sm-3 nopadding">
                  <input type="text" class="form-control" id="album_purchase_source_name"
                         name="album_purchase_source_name" formControlName="albumPurchaseSourceName"
                         placeholder="Source Name">
                </div>
                <div class="col-sm-3 nopadding">
                  <div class="form-group">
                    <div class="input-group">
                      <input type="text" class="form-control" id="album_purchase_url" name="album_purchase_url"
                             formControlName="albumPurchaseURL" placeholder="URL">
                      <div class="input-group-btn">
                        <button class="btn btn-success" type="button" (click)="addAlbumPurchaseSources();"><span
                          class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <hr/>
        <br/>
        <div><font size="4">Album Tracks Details:</font></div>
        <br/>
        <div formArrayName="albumTracks" *ngFor="let item of albumForm.get('albumTracks').controls; let i = index;">
          <div [formGroupName]="i">

            <div class="row">
              <div class="col">
                <div class="col-sm-2 control-label"><font size="3">Track Title:</font></div>
                <div class="col-sm-3 nopadding">
                      <input type="text" class="form-control" id="track_title" name="track_title"
                             formControlName="trackTitle" placeholder="Track Title">
                </div>
              </div>
            </div>

            <br/>
            <div class="row">
              <div class="col">
                <div class="col-sm-2 control-label"><font size="3">Track Genre:</font></div>
                <div class="col-sm-3 nopadding">
                  <input type="text" class="form-control" id="genre" name="genre" formControlName="trackGenre"
                         placeholder="Genre">
                </div>
              </div>
            </div>

            <br/>
            <div formArrayName="audioSources" *ngFor="let audioItem of item.controls['audioSources'].controls; let audioi=index;">
              <div [formGroupName]="i">
                <div class="row">
                  <div class="col">
                    <div class="col-sm-2 control-label"><font size="3">Audio:</font></div>
                    <div class="col-sm-3 nopadding">

                      <input type="text" class="form-control" id="audio_source_name" name="audio_source_name" formControlName="audioSourceName" placeholder="Audio Source Name">

                    </div>
                    <div class="col-sm-3 nopadding">
                      <div class="form-group">
                        <div class="input-group">

                          <input type="text" class="form-control" id="audio_url" name="audio_url" formControlName="audioSourceURL" placeholder="Audio Source URL">

                          <div class="input-group-btn">
                            <button class="btn btn-success" type="button" (click)="addAudioSource(i);"><span
                              class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>


            <br/>
            <div formArrayName="videoSources" *ngFor="let videoItem of item.controls['videoSources'].controls; let videoi = index;">
              <div [formGroupName]="i">
                <div class="row">
                  <div class="col">
                    <div class="col-sm-2 control-label"><font size="3">Video:</font></div>
                    <div class="col-sm-3 nopadding">

                      <input type="text" class="form-control" id="video_source_name" name="video_source_name" formControlName="videoSourceName" placeholder="Video Source Name">

                    </div>
                    <div class="col-sm-3 nopadding">
                      <div class="form-group">
                        <div class="input-group">

                          <input type="text" class="form-control" id="video_url" name="video_url" formControlName="videoSourceURL" placeholder="Video Source URL">

                          <div class="input-group-btn">
                            <button class="btn btn-success" type="button" (click)="addVideoSource(i);"><span
                              class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <br/>
            <div formArrayName="trackPurchaseSources" *ngFor="let trackPurchaseItem of item.controls['trackPurchaseSources'].controls; let purchasei = index;">
              <div [formGroupName]="i">
                <div class="row">
                  <div class="col">
                    <div class="col-sm-2 control-label"><font size="3">Track Purchase Source:</font></div>
                    <div class="col-sm-3 nopadding">

                      <input type="text" class="form-control" id="purchase_source_name" name="purchase_source_name" formControlName="trackPurchaseSourceName" placeholder="Purchase Source Name">

                    </div>
                    <div class="col-sm-3 nopadding">
                      <div class="form-group">
                        <div class="input-group">

                          <input type="text" class="form-control" id="purchase_url" name="purchase_url" formControlName="trackPurchaseSourceURL" placeholder="Purchase Source URL">

                          <div class="input-group-btn">
                            <button class="btn btn-success" type="button" (click)="addTrackPurchaseSource(i);"><span
                              class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>


            <br/>
            <div class="row">
              <div class="col">
                <div class="col-sm-2 control-label"><font size="3">Download:</font></div>
                <div class="col-sm-3 nopadding">
                  <input type="text" class="form-control" id="download_url" name="download_url"
                         formControlName="downloadURL" placeholder="Download URL">
                </div>
              </div>
            </div>
            <br/><hr><br/>
          </div>
        </div>
        <div class="form-group">
          <button class="btn btn-success" type="button" (click)="addTracks();">Add Track</button>
        </div>
        <div class="form-group">
          <button class="btn btn-primary">Save</button>
        </div>
      </form>
    </div>
  </div>
</div>

在这里工作:https://stackblitz.com/edit/so-53246132

问题 #1:

        <div formArrayName="audioSources" *ngFor="let audioItem of item.controls['audioSources'].controls; let audioi=index;">
          <div [formGroupName]="i">

但应该是:

        <div formArrayName="audioSources" *ngFor="let audioItem of item.controls['audioSources'].controls; let audioi=index;">
          <div [formGroupName]="audioi">

videoipurchasei 相同。

问题 #2:

  addTracks(): void {
    (this.albumForm.get('albumTracks') as FormArray).push(this.albumTracksDetails());
    // why? what is items?
    // this.items = this.albumForm.get('albumTracks') as FormArray;
    // this.items.push(this.albumTracksDetails());
  }

也可能发生变化的是:

  addAlbumPurchaseSources(): void {
    this.items = this.albumForm.get('albumPurchaseSources') as FormArray;
    this.items.push(this.createAlbumPurchaseSource());
  }