12

During runtime I am getting all records from the JSON object and in form-control I am getting the values. However, in form-array I am getting only the first of many records.

The error shown in the console:

Cannot find form control at index 1 at FormArray._throwIfControlMissing

Image of JSON object and error:

enter image description here

  1. Interface

export interface IService {
    ServiceID: number,
    Name: string,
    Staffs: IStaffView[]
}

export interface IStaffView {
    StaffServiceID: number,
    StaffID: number,
    Name: string
}

  1. Component

import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { Observable } from 'rxjs/Rx';
import { FormBuilder, FormGroup, FormControl, FormArray , Validators } from '@angular/forms';

import { RESTService } from '../../Service/REST.service';
import { IService, IStaffView } from '../../Model/service.model';
import { DBOperation } from '../../Shared/enum';
import { Global } from '../../Shared/global';

@Component({
    selector: 'app-service-detail',
    templateUrl: 'app/Components/service-detail/service-detail.component.html'
})

export class ServiceDetailComponent implements OnInit {
   
    service: IService;
    services: IService[];
    staffview: IStaffView;
    staffsview: IStaffView[];

    serviceFrm: FormGroup;
    Staffs: FormArray;

    msg: string;
    indLoading: boolean = false;
    btnTitle: string;
    dbops: DBOperation;

    constructor(
        private fb: FormBuilder,
        private _restService: RESTService,
        private location: Location,
        private _route: ActivatedRoute
    ) {
        const id = this._route.snapshot.params['id'];
        if (!id || id == 0) {
            this.btnTitle = 'Save';
            this.dbops = DBOperation.create;
        } else {
            this.btnTitle = 'Edit';
            this.dbops = DBOperation.update
        }
    }

    ngOnInit(): void {
        //this.Staffs = this.fb.array([
        //    this.initStaff()
        //]);


        this.serviceFrm = this.fb.group({
            ServiceID: [''],
            Name: ['', Validators.required],
            Staffs: this.fb.array([
                this.initStaff()
            ])
        });

        this.getService();
    }

    initStaff() {
        return this.fb.group({
            StaffServiceID: [''],
            StaffID: [''],
            Name: ['']
        });
    }

    getService(): void {
        const id = parseInt(this._route.snapshot.params['id']);
        if (id && id > 0) {
            this.indLoading = true;
            this._restService.getById('/api/serviceapi/', id)
                .subscribe(resp => this.serviceFrm.setValue(resp)
                , error => this.msg = <any>error);
        }
    }

}

  1. HTML Code

  <div class="row form-group">
                    <div class="col-md-3">
                        <label for="message-text" class="control-label">Staffs</label>
                    </div>
                    <div formArrayName="Staffs">
                        <div *ngFor="let staff of serviceFrm.controls.Staffs.controls; let i=index" formGroupName="{{i}}">
                            <div>
                                <label>Name</label>
                                <input type="text" class="form-control" formControlName="Name">
                            </div>
                        </div>
                    </div>
                </div>

3 Answers 3

22

The mentioned error is caused by calling this.serviceFrm.setValue(resp) (https://github.com/angular/angular/blob/master/packages/forms/src/model.ts#L1382).

This method performs strict checks, so it will throw an error if you try to set the value of a control that doesn't exist or if you exclude the value of a control.

You are trying to assign an array of 3 items (according to your snapshot) to FormArray having only one initial FormGroup at index 0, so assigning value at index 1 fails, as it does not exist.

To solve it empty your form array before patching value, use patchValue() (which accepts partial value) instead of setValue() and then push each value in a loop:

getService(): void {
  const id = parseInt(this._route.snapshot.params['id']);
  if (id && id > 0) {
    this.indLoading = true;
    this._restService.getById('/api/serviceapi/', id).subscribe(
      resp => {
        // get form array reference
        const staffs = this.serviceFrm.get('Staffs') as FormArray;
        // empty form array
        while (staffs.length) {
          staffs.removeAt(0);
        }
        // use patchValue instead of setValue
        this.serviceFrm.patchValue(resp);
        // add form array values in a loop
        resp.Staffs.forEach(staff => staffs.push(this.fb.group(staff));
      }, 
      error => this.msg = <any>error
    );
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you @Andriy, patchValue is working. and while loop also working thank a lot
you are right (about removing items), I updated my answer with while loop removing all previous values
You sir had save my day!
tried this and it seems to work but for some reason valueChanges no longer emits a value
@Roj, please note that this was answere 1.5 years ago, for different Angular version. If you want please create a stackblitz example with your issue and I will try to help. Or post a new question mentioning Angular version
2

It is possible to use setControl with FormArrays. The code below is an example from Deborah Kurata:

this.productForm = this.fb.group({
productName: ['', [Validators.required,
                       Validators.minLength(3),
                       Validators.maxLength(50)]],
productCode: ['', Validators.required],
starRating: ['', NumberValidators.range(1, 5)],
tags: this.fb.array([]),
description: ''
});

...

// Update the data on the form
this.productForm.patchValue({
    productName: this.product.productName,
    productCode: this.product.productCode,
    starRating: this.product.starRating,
    description: this.product.description
});

// This line makes the array update!
this.productForm.setControl('tags', this.fb.array(this.product.tags || []));

Comments

0

Try this:

<div class="row form-group">
                    <div class="col-md-3">
                        <label for="message-text" class="control-label">Staffs</label>
                     </div>
                    <div >
                        <div *ngFor="let staff of serviceFrm.controls.Staffs.controls; let i=index" formArrayName="Staffs">
                            <div [formGroupName]="i">
                                <label>Name</label>
                                <input type="text" class="form-control" formControlName="Name">
                            </div>
                        </div>
                    </div>
                </div>

1 Comment

thanks for helping, but unfortunately this code is not working for me, giving this -- Error: Cannot find control with name: '0'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.