0

Hope you can help me in the following question.

I have an dynamically set up form in Angular, with the option to add and remove fields from a nested array.

Now the next step is to pre-populate the array in case somebody wants to edit the list.

This all works fine, I create the list in my component:

 ngOnInit() {
  
      this.isLoggedIn = !!this.tokenStorageService.getToken();
  
      if (this.isLoggedIn) {
        const user = this.tokenStorageService.getUser();
        this.roles = user.roles;
        this.username = user.username;
      }
  
  //  --------------------- LIST CREATION ------------------------   //
  

     this.newList = this.fb.group({
       listTitle:  '',
       id:  '',
       chapters: '',
       sublist: this.fb.array([])
     })
    this.addSublist()
    this.addItem(0)
    this.addItem(0)
    this.addItem(0)
    this.prefillList();

   }

I have some functions to create/remove the sublist and the items below:

 //  --------------------- SUBLIST CREATION ---------------------   //
  
   get sublistArray() {
     return this.newList.get('sublist') as FormArray
   }
  
   addSublist() {
    const sublistGroup = this.fb.group({
      subListCreator: this.username,
      item: this.fb.array([])
    })
    this.sublistArray.push(sublistGroup);
  }
  
  deleteSublist(i) {
    this.sublistArray.removeAt(i);
  }
  
  
  //  ---------------------- ITEM CREATION ------------------------   //
  
   getItemArray(index) {
     return this.sublistArray.get([index, 'item']) as FormArray;
   }
  
   addItem(index: number) {
     const itemGroup = this.fb.group({
       itemTitle: [],
       itemContext: []
      })
  
     this.getItemArray(index).push(itemGroup);
   }
  
   deleteItem(userIndex: number, colorIndex: number) {
     this.getItemArray(userIndex).removeAt(colorIndex)
   }
  

And then finally I retrieve my complete document from the database to prefill the form:



  prefillList(): void {
    const listId = this.route.snapshot.paramMap.get('listId');
    this.listService.getListNo404(listId)
          .subscribe(list=> {
           this.list = list;
           console.log(this.list);
           this.newList.patchValue({
            listTitle: this.list.listTitle,
            id: this.route.snapshot.paramMap.get('listId'),
            sublist: this.list.sublist
           });     
         }
       );
      }

However, and this is where my question comes into play. I get that I create my form by calling three times the

this.addItem(0) Function

And now my form is fixed on three items always as a starting point. If my list would be 7 items long to start with, I now lose 4 items on the way.

Can you help me in creating the right (I assume) for-loop to push the array in a way that actually is sustainable ?

In case useful, hereby the complete component and template as well.

Complete component:

import { Component, Input } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from "@angular/forms";
import { ListService } from '../list.service';
import { TokenStorageService } from '../token-storage.service';

import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { map  } from 'rxjs/operators';


@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.css']
})
export class MainComponent  {

  @Input() list
  newList: FormGroup;
  private roles: string[] = [];
  isLoggedIn = false;
  username?: string;
  
  
   constructor(
      private tokenStorageService: TokenStorageService,
      private fb: FormBuilder,
      private listService: ListService,
      private route: ActivatedRoute
    ) { }
  
   ngOnInit() {
  
      this.isLoggedIn = !!this.tokenStorageService.getToken();
  
      if (this.isLoggedIn) {
        const user = this.tokenStorageService.getUser();
        this.roles = user.roles;
        this.username = user.username;
      }
  
  //  --------------------- LIST CREATION ------------------------   //
  

     this.newList = this.fb.group({
       listTitle:  '',
       id:  '',
       chapters: '',
       sublist: this.fb.array([])
     })
    this.addSublist()
    this.addItem(0)
    this.addItem(0)
    this.addItem(0)
    this.prefillList();

   }
  
  
  //  --------------------- SUBLIST CREATION ---------------------   //
  
   get sublistArray() {
     return this.newList.get('sublist') as FormArray
   }
  
   addSublist() {
    const sublistGroup = this.fb.group({
      subListCreator: this.username,
      item: this.fb.array([])
    })
    this.sublistArray.push(sublistGroup);
  }
  
  deleteSublist(i) {
    this.sublistArray.removeAt(i);
  }
  
  
  //  ---------------------- ITEM CREATION ------------------------   //
  
   getItemArray(index) {
     return this.sublistArray.get([index, 'item']) as FormArray;
   }
  
   addItem(index: number) {
     const itemGroup = this.fb.group({
       itemTitle: [],
       itemContext: []
      })
  
     this.getItemArray(index).push(itemGroup);
   }
  
   deleteItem(userIndex: number, colorIndex: number) {
     this.getItemArray(userIndex).removeAt(colorIndex)
   }
  
  
  
  //  -------------- GET LIST TITLE FROM URL ------------------   //
  

  prefillList(): void {
    const listId = this.route.snapshot.paramMap.get('listId');
    this.listService.getListNo404(listId)
          .subscribe(list=> {
           this.list = list;
           console.log(this.list);
           this.newList.patchValue({
            listTitle: this.list.listTitle,
            id: this.route.snapshot.paramMap.get('listId'),
            sublist: this.list.sublist
           });     
         }
       );
      }



 //  ---------------------- SUBMISSION! ------------------------   //


  onSubmit() {
    this.listService.dropList(this.newList.value)      
        .subscribe(list => this.list.push(list));
        console.log(this.newList.value);
  }
  
  }

Complete template:

<form [formGroup]="newList" (ngSubmit)="onSubmit()">

  <!-- LIST -->
  <div><br></div>
  <span class="row d-flex align-items-baseline p-0">
    <span class="bd-highlight badge badge-dark mr-1 ">LIST</span>
    <div class="col d-flex p-2">
      <input class="form-control form-control-lg" placeholder="title" type="text" formControlName="listTitle" readonly>
    </div>
  </span>

  <!-- SUBLIST -->
  <div formArrayName="sublist">
    <div *ngFor="let user of sublistArray.controls; let i=index" [formGroupName]="i">

      <!-- ITEM -->
      <div formArrayName="item">
        <div *ngFor="let item of getItemArray(i).controls; let t=index" [formGroupName]="t">
          <span class="row">
            <div class="p-2  bd-highlight">
              <span class="badge badge-dark p-1 m-1">P{{t+1}}</span>
            </div>
            <input class="flex-grow-1 bd-highlightform-control" placeholder="{{t+1 }} item" type="text"
              formControlName="itemTitle">
            <div class="p-2 bd-highlight self-align-center"><button type='button' class="btn btn-light btn-sm"
                (click)="deleteItem(i, t)">x</button></div>
          </span>
          <div class="collapse" id="comments">
            <input class=" col d-flex p-2  form-control form-control-sm" placeholder="comment" type="text"
              formControlName="itemContext"><br>
          </div>
        </div>
      </div>
      <!-- ITEM END -->



      <div class="d-flex flex-row  bd-highlight mb-2">
        <div class="d-flex p-2 ml-4"><button type="button" class="btn btn-light btn-sm" (click)="addItem(i)">add
            item</button></div>
        <div class="d-flex p-2 "> <button class="btn btn-light btn-sm" type="button" data-toggle="collapse"
            data-target="#comments">
            show / hide comments </button></div>
        <div class="d-flex p-2 ">
          <button class="btn btn-light btn-sm collapse show" id="chapters" type="button" data-toggle="collapse"
            data-target="#chapters">add chapter</button>
        </div>
      </div>

    </div>

  </div>
  <span *ngIf=newList class="row d-flex align-items-baseline p-0 collapse" id="chapters">
    <span class="bd-highlight badge badge-light mr-1 collapse" id="chapters">CHAPTER</span>
    <div class="col d-flex p-2 collapse" id="chapters">
      <input class="form-control form-control-sm collapse" id="chapters" placeholder="chapter" type="text"
        formControlName="chapters">
    </div>
    <button class="btn btn-light btn-sm collapse" id="chapters" type="button" data-toggle="collapse"
      data-target="#chapters">hide chapter</button>
  </span>
  <div class="d-flex p-2 ml-4"> <button type="submit" class="btn btn-dark btn-primary">DROP</button></div>
  <br><br>
<pre>{{ newList.value | json }}</pre> 
  • After calling the prefill code, what does the json representation of the form look like? Also I noticed your original form group has a chapters property, but prefill doesn't put it. What will occur if you add it in? – SomeStudent Jan 12 '22 at 03:06
  • Hey sorry for the late reply. I fixed it with adding a forEach + removing 3x addItem, see here: this.listService.getListNo404(listId) .subscribe(list=> { this.list = list; console.log(this.list); if (list.sublist[0].item.length) { list.sublist[0].item.forEach(c => this.addItem(0) ); } this.newList.patchValue({ listTitle: this.list.listTitle, id: this.route.snapshot.paramMap.get('listId'), sublist: this.list.sublist }); } ); } – planetstarbucks Jan 13 '22 at 22:47
  • I wanted to elaborate more, but the answer form doesn't let me. Basically I call the addItem the number of times of the specific array length. Then the form is there, and when the data gets added it fits the exact form-length. This helped: https://stackoverflow.com/questions/62397992/how-to-patch-form-value-to-a-recursive-nested-angular-form – planetstarbucks Jan 13 '22 at 22:49

0 Answers0