skip to Main Content

I get json data from a backend which is converted to my model classes (it has nested classes and arrays). Now I want to create a reactive FormGroup by initializing it with my model class. Unfortunately this results in a FormGroup that uses a FormControl and not a FormArray for the arrays of the model class.

If I create the FormGroup manually (at least for the first class) and create a FormArray for the arrays, it is overwritten with the FormControl again if I patch my root model class into the FormGroup.

But I need the FormArray since I want to use nested Components in order to display the data of the model class and edit it. Or my approach is wrong. Either way maybe someone knows a solution to my problem.

Here is my example code (I simplified it with this simple Order model)

//MODEL
export class CustomerHistory {
  id?:number;
  orders:Order[];

  public constructor() {
    this.id = undefined;
    this.orders = []
  }
}

export class Order {
  id?:number;
  orderItems:OrderItem[];

  public constructor() {
    this.id = undefined;
    this.orderItems = []
  }
}

export class OrderItem {
  id?:number;
  itemName:string;

  public constructor() {
    this.id = undefined;
    this.itemName = undefined;
  }
}

//AppComponent
initFormGroup() {
    //Either init with Class, but then no FormArrays are created for the arrays
    this.myCustomerHistoryFormGroup = this.fb.group(new CustomerHistory());

    //Or init manually but doesn't go deep enough and is also overwritten by patch
    this.myCustomerHistoryFormGroup = this.fb.group({
      id: "",
      orders: this.fb.array([])
    })
}

//Patching fills all the data of customerHistory into the FormGroup, but also overwrites the FormArray
//of the manually created FormGroup to a FormControl
patchFormGroup(customerHistory: CustomerHistory) {
    this.myCustomerHistoryFormGroup.patchValue(customerHistory);
}

//AppTemplate
<form [formGroup]="myCustomerHistoryFormGroup">
 <div formArrayName='orders'>
  <div *ngFor="let order of myCustomerHistoryFormGroup.get('orders'); let i=index">
    <app-order [formGroup]="order"></app-order>
  </div>
 </div>
</form>

2

Answers


  1. Check your template code, too many syntax errors.

    <form [formGroup]="myCustomerHistoryFormGroup">
        <div formArrayName='orders'>
          <div *ngFor="let order of myCustomerHistoryFormGroup.get('orders')); let i=index">
            <app-order [formGroup]="order"></app-order>
          </div>
        </div>
    </form>
    
    Login or Signup to reply.
  2. When we manange a FormGroup with FormArrays it’s always the same

    1. Create functions that return the formArrays. If is a form array
      inside a formArray your function need the index

      cvForm: FormGroup;
      get lines()
      {
         return this.cvForm.get('lines') as FormArray;
      }
      players(index:number)
      {
         return this.lines.at(index).get('players') as FormArray;
      }
      
    2. Create functions that return a FormGroup empty or with data

      setPlayer(data:any=null)
      {
          data=data || {first_name:'',last_name:''}
          return new FormGroup({
             firstName:new FormControl(data.first_name),
             lastName:new FormControl(data.last_name),
          })
      }
      setLine(data:any=null)
      {
         data=data || {name:'',players:[]}
         return new FormGroup({
           name:new FormControl(data.name),
           players:new FormArray(data.players.map((x:any)=>this.setPlayer(x)))
         })
      }
      
      setForm(data:any=null)
      {
         data=data || {team_name:'',lines:[]}
         return new FormGroup({
            team_name:new FormControl(data.team_name),
            lines:new FormArray(data.lines.map((x:any)=>this.setLine(x)))
         })
      }
      

    The advantage of this is that it’s easy add/remove a player/lines and create a form with the data or empty

    this.cfForm=this.setForm() //create an empty FormGroup
    this.cvFrom=this.setForm(seedData) //create a formGroup with the data
    this.lines.push(this.setLine()) //add an empty line 
    
    //add to the first line of the array a new player
    players(0).push(this.setPlayer({
                first_name:'first Name',
                last_name:'last name'}) 
    etc.
    

    your forked stackblitz

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search