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> 
3
  • 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? Commented Jan 12, 2022 at 3: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 }); } ); } Commented Jan 13, 2022 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: stackoverflow.com/questions/62397992/… Commented Jan 13, 2022 at 22:49

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.