Search code examples
javascriptangulartypescriptangular-reactive-formsangular-forms

Angular Reactive Forms Array


I am having problem accesssing products array located inside opticanOrders which is inside orderForm. In the console, I see that in order to access products array, I should reference it like that:

orderForm.controls.opticianOrders.controls.products.controls

But it doesn't work.

This is my component:

  constructor(private customerService: CustomerService, private fb: FormBuilder) { }

  orderForm: FormGroup;

  ngOnInit() {
    this.orderForm = this.fb.group({
      name: [''],
      surName: [''],
      opticianOrders: this.fb.group({
        orderDescription: [''],
        products: this.fb.array([
          this.initProduct()
        ])
      }),
    });
  }

  save(model: Customer) {
    // call API to save customer
    console.log(model);
  }

  onCancel(form: NgForm){
    this.createState.emit(false);
  }

  initProduct(){
    return this.fb.group({
      name: [''],
      manufacturerName: ['']
    })
  }

  addProduct(){
    const control = <FormArray>this.orderForm.controls['products'];
    control.push(this.initProduct());
  }

  removeProduct(i: number){
    const control = <FormArray>this.orderForm.controls['products']
  }

Html

<form [formGroup]="orderForm" novalidate (ngSubmit)="save(orderForm)">

  <!-- name -->
  <div class="form-group">
      <label>Name</label>
      <input type="text" formControlName="name">
  </div>

  <!-- surName -->
  <div class="form-group">
      <label>Last Name</label>
      <input type="text" formControlName="surName">
  </div>

  <div formGroupName="opticianOrders" class="form-group">
      <label>Order Description</label>
      <input type="text" formControlName="orderDescription">
  </div>
  <div formArrayName="products">
          <div *ngFor="let product of orderForm.controls.opticianOrders.controls.products.controls; let i=index">
              <div>
                  <span>Address {{i + 1}}</span>
                  <span *ngIf="orderForm.controls.opticianOrders.controls.products.controls.length > 1" 
                      (click)="removeProduct(i)">
                  </span>
               </div>

               <div [formGroupName]="i">
                  <div>
                      <label>Product name</label>
                      <input type="text" formControlName="name">
                  </div>
              </div>
          </div>
        </div>
    <button type="submit" [disabled]="!orderForm.valid">Submit</button>
</form>

Solution

  • Your stackblitz code does not work. You did not import the ReactiveFormsModule and you implemented the forms code in the hello.component.ts also you put the template code in the app.component.html.

    See my working sample on stackblitz. It let you add (click on Add) and remove (click on 'x') products from your FormArray.

    Form value with two products:

    {
      name: 'John',
      surName: 'Doe',
      opticianOrders: {
        orderDescription: '1234',
        products: [
          { name: 'Cookies', manufacturerName: '' },
          { name: 'More Cookies', manufacturerName: '' }
        ]
      }
    }
    

    For typescript this.orderForm.controls.opticianOrders is an AbstractControl which has no controls property. You have to cast it to a FormGroup first. Same with products, you have to cast it to a FormArray.

    removeProduct(i: number){
      const aFormGroup = this.orderForm.controls.opticianOrders as FormGroup;
      const aFormArray = aFormGroup.controls.products as FormArray;
      aFormArray.removeAt(i);    
    }