10

I have an issue related with Angular Reactive Form which I am not capable of sort out.

Code

form.html and form.ts

import {Component, OnInit} from '@angular/core';
import {FormArray, FormBuilder, FormGroup} from '@angular/forms';
import {ProcessService} from "../../../service/process.service";


@Component({
  selector: 'app-check-order-form',
  templateUrl: './check-order-form.component.html',
  styleUrls: ['./check-order-form.component.css']
})
export class CheckOrderFormComponent implements OnInit {
  submitted = false;

  X: FormGroup = this._fb.group({
    field: '',
    Y: this._fb.array([])
  });

  Yg: FormGroup = this._fb.group({
    subfield: '',
    Z: this._fb.array([])
  });

  Zg: FormGroup = this._fb.group({
    subsubfield: ''
  });

  constructor(private _fb: FormBuilder) {
  }

  ngOnInit() {
    this.createYg();
    this.createZg();
  }

  ngOnChanges() {
  }

  onSubmit(formValue) {
    this.submitted = true;
    console.warn(this.X.value);
  }

  createYg() {
    return this.Yg;
  }

  createZg() {
    return this.Zg;
  }

  
  get Y(): FormArray {
    return this.X.get('Y') as FormArray;
  }

  getCurrentZ(index): FormArray {
    return this.Y.at(index).get('Z') as FormArray;
  }
  
  addY(): void {
    this.Y.push(this.createYg());
  }

  addZ(index): void {
    let Z = this.Y.at(index).get('Z') as FormArray;
    Z.push(this.createZg());
  }


  deleteY(index) {
    this.Y.removeAt(index);
  }

  deleteZ(Yindex, index) {
    this.getcurrentZ(Yindex).removeAt(index);
  }
}
<form class="form-inline" [formGroup]="X" (ngSubmit)="onSubmit(X.value)">
  
  <div class="form-group col-3 mb-2">
    <label for="field">Field</label>
    <input type="text" class="form-control" formControlName="field" id="field">
  </div>

  <div class="form-inline" formArrayName="Y">
    <div *ngFor="let y of Y?.controls; let k=index">
      <hr/>
      <div [formGroupName]="k" class="row pt-1 pb-1">
        <div class="col-12">
          <label>Y {{k + 1}}</label>
        </div>
        <div class="form-group col-3 mb-2">
          <input type="text" class="form-control" formControlName="subfield" placeholder="subfield">
        </div>
        <div class="form-inline" formArrayName="Z">
          <div *ngFor="let fondo of getCurrentZ(k)?.controls; let j=index">
            <hr class="bg-secondary"/>
            <div [formGroupName]="j" class="pt-1 pb-1">
              <label>Z {{j + 1}}</label>
              <div class="form-group col-3 mb-2">
                <input type="text" class="form-control" formControlName="subsubfield" placeholder="subsubfield">
              </div>        
              <div class="form-group col-3 mb-2">
                <button (click)="deleteZ(k, j)" class="btn btn-danger mr-1">Remove</button>
              </div>
            </div>
          </div>
        </div>        

        <div class="form-group col-12 mb-2 pr-1">
          <button class="btn btn-info mr-1" (click)="addZ(k)">+ Z</button>
        </div>
        <div class="form-group col-12 mb-2 pr-1">
          <button (click)="deleteY(k)" class="btn btn-danger mr-1">
            Remove
          </button>
        </div>
      </div>
    </div>
  </div>

  <div class="form-group col-12 mt-2">
    <button type="submit" class="btn btn-primary mr-2">Submit</button>
    <button (click)="addY()" class="btn btn-success">+ Y</button>
  </div>
</form>

Problem

Here are the steps that lead to the issue (please, have a look at the code above, please):

  1. I click on + Y button in order to add a Y FormGroup
  2. I click another time on + Y
  3. I click on + Z on one of the two Y FormGroup

Result: the Z FormGroup is rendered on both of the Y elements.

The ideal for me is that each FormGroup is related only with the parent one in order to compile the form properly. I tried many solutions, but I cannot find the issue despite of the specification of the index of the parent array (Y).

Thank you in advance.

2 Answers 2

8
+50

When you add a Y form (and a Z form), you don't create a new object but use the same one again and again (Yg and Zg). So, when you add a "new" Z on a Y form, every Y form is impacted because they are the same.

Remove Yg and Zg and replace createYg() and createZg() by this :

  createYg() {
    return this._fb.group({
      subfield: '',
      Z: this._fb.array([])
    });
  }

  createZg() {
    return this._fb.group({
      subsubfield: ''
    });
  }
Sign up to request clarification or add additional context in comments.

Comments

4

Please check the code and I added the working module in the https://angular-4dt3sa.stackblitz.io or with code https://stackblitz.com/edit/angular-4dt3sa?file=src%2Fapp%2Fapp.component.ts

html content

<form class="form-inline" [formGroup]="X" (ngSubmit)="onSubmit(X.value)">

  <div class="form-group col-3 mb-2">
    <label for="field">Field</label>
    <input type="text" class="form-control" formControlName="field" id="field">
  </div>

  <div class="form-inline" formArrayName="Y">
    <div *ngFor="let y of Y?.controls; let k=index">
      <hr/>
      <div> test {{k}}</div>
      <div [formGroupName]="k" class="row pt-1 pb-1">
        <div class="col-12">
          <label>Y {{k + 1}}</label>
        </div>
        <div class="form-group col-3 mb-2">
          <input type="text" class="form-control" formControlName="subfield" placeholder="subfield">
        </div>
        <div class="form-inline" formArrayName="Z">
          <div *ngFor="let fondo of getCurrentZ(k)?.controls; let j=index">
            <hr class="bg-secondary"/>
            <div [formGroupName]="j" class="pt-1 pb-1">
              <label>Z {{j + 1}}</label>
              <div class="form-group col-3 mb-2">
                <input type="text" class="form-control" formControlName="subsubfield" placeholder="subsubfield">
              </div>        
              <div class="form-group col-3 mb-2">
                <button (click)="deleteZ(k, j)" class="btn btn-danger mr-1">Remove</button>
              </div>
            </div>
          </div>
        </div>        

        <div class="form-group col-12 mb-2 pr-1">
          <button class="btn btn-info mr-1" (click)="addZ(k)">+ Z</button>
        </div>
        <div class="form-group col-12 mb-2 pr-1">
          <button (click)="deleteY(k)" class="btn btn-danger mr-1">
            Remove
          </button>
        </div>
      </div>
    </div>
  </div>

  <div class="form-group col-12 mt-2">
    <button type="submit" class="btn btn-primary mr-2">Submit</button>
    <button (click)="addY()" class="btn btn-success">+ Y</button>
  </div>
</form>

Typescript

import {Component, OnInit} from '@angular/core';
import {FormArray, FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  submitted = false;

  X: FormGroup = this._fb.group({
    field: '',
    Y: this._fb.array([])
  });

  Yg: FormGroup = this._fb.group({
    subfield: '',
    Z: this._fb.array([])
  });

  Zg: FormGroup = this._fb.group({
    subsubfield: ''
  });

  constructor(private _fb: FormBuilder) {
  }

  ngOnInit() {
    this.createYg();
    this.createZg();
  }

  ngOnChanges() {
  }

  onSubmit(formValue) {
    this.submitted = true;
    console.warn(this.X.value);
  }

  createYg() {
    return this._fb.group({
      subfield: '',
      Z: this._fb.array([])
    });
  }

  createZg() {
    return this._fb.group({
      subsubfield: ''
    });
  }


  get Y(): FormArray {
    return this.X.get('Y') as FormArray;
  }

  getCurrentZ(index): FormArray {
    return this.Y.at(index).get('Z') as FormArray;
  }

  addY(): void {
    this.Y.push(this.createYg());
  }

  addZ(index): void {
    let Z = this.Y.at(index).get('Z') as FormArray;
    let Zg = this.createZg();
    Z.push(Zg);
  }


  deleteY(index) {
    this.Y.removeAt(index);
  }

  deleteZ(Yindex, index) {
    let Z = this.Y.at(Yindex).get('Z') as FormArray;
    Z.removeAt(index);
  }
}

As LP154 mentioned . You should not create new object. @teskin

Comments

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.