Search code examples
angularangular-reactive-forms

Property 'controls' does not exist on type 'AbstractControl<any, any>'


I have a reactive form and its working properly on Angular version below 13, When I try the same in Angular new version, it's showing me errors.

I have used FormGroup and FormArray and typecasted, but still getting the issue. What would be the fix?

Angular 11: working code

Angular 15: showing error

Here is the ts code

form: FormGroup;
  items: FormArray;
  sum: number;
  data = [
    {
      name: 'Device 1',
      model: 'Model 1',
      serial: 'Serial 1',
      selection: 'takePayment',
      owedInDollars: 100,
    },
    {
      name: 'Device 2',
      model: 'Model 2',
      serial: 'Serial 2',
      selection: 'takePayment',
      owedInDollars: 200,
    },
    {
      name: 'Device 3',
      model: 'Model 3',
      serial: 'Serial 3',
      selection: 'takePayment',
      owedInDollars: 300,
    },
  ];
totalsum: number;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.fb.group({
      items: this.fb.array([]),
    });
    const items = this.form.get('items') as FormArray;
    for (let i = 0; i < this.data.length; i++) {
      items.push(
        this.createItem(
          this.data[i].name,
          this.data[i].model,
          this.data[i].serial,
          this.data[i].selection,
          this.data[i].owedInDollars
        )
      );
    }
    this.sumOfOwed();
  }

  createItem(
    deviceName: string,
    modelNumber: string,
    serialNumber: string,
    takePayment: string,
    owedInDollars: number
  ): FormGroup {
    return this.fb.group({
      deviceName: deviceName,
      modelNumber: modelNumber,
      serialNumber: serialNumber,

      dispute: '',
      return: '',
      takePayment: takePayment,
      reason: '',
      owedInDollars: owedInDollars,
    });
  }
  sumOfOwed() {
    this.items = this.form.get('items') as FormArray;
    this.sum = 0;
    this.items.controls.forEach((control) => {
      this.sum += control.get('owedInDollars').value;
      this.totalsum=this.sum
    });
    console.log(`Total Owed in Dollars: ${this.sum}`);
  }
  selectOption(item: FormGroup, option: string) {
    item.controls.dispute.setValue(option === 'dispute' ? 'dispute' : '');
    item.controls.return.setValue(option === 'return' ? 'return' : '');
    item.controls.takePayment.setValue(
      option === 'takePayment' ? 'takePayment' : ''
    );

    let total = 0;
    this.items.controls.forEach((control) => {
      let owed = control.get('owedInDollars').value;
      if (control.get('dispute').value || control.get('return').value) {
        total += owed;
      }
    });
    console.log(`New Total Owed in Dollars: ${this.sum - total}`);
    this.totalsum =this.sum - total
  }
}

Html code

<form [formGroup]="form">
<table id="customers">
  <thead>
    <tr>
      <th>Device Name</th>
      <th>Model Number</th>
      <th>Serial Number</th>
      <th>Dispute</th>
      <th>Return</th>
      <th>Take Payment</th>
      <th>Owed in Dollars</th>
    </tr>
  </thead>
  <tbody formArrayName="items">
    <tr *ngFor="let item of items.controls; let i=index" [formGroupName]="i">
      <td>{{ item.controls.deviceName.value }}</td>
      <td>{{ item.controls.modelNumber.value }}</td>
      <td>{{ item.controls.serialNumber.value }}</td>
      <td><input type="radio" formControlName="dispute" value="dispute" (click)="selectOption(item, 'dispute')"></td>
      <td><input type="radio" formControlName="return" value="return" (click)="selectOption(item, 'return')"></td>
      <td><input type="radio" formControlName="takePayment" value="takePayment" (click)="selectOption(item, 'takePayment')" [checked]="true"></td>
      <td>{{ item.controls.owedInDollars.value }}</td>
    </tr>
    <tr *ngFor="let item of items.controls; let i=index" [formGroupName]="i">
      <td colspan="7" *ngIf="item.controls.dispute.value">
        <label>Reason:</label>
        <select formControlName="reason">
          <option value="">--Select--</option>
          <option value="Damaged">Damaged</option>
          <option value="Wrong Item">Wrong Item</option>
          <option value="Missing Part">Missing Part</option>
          <option value="Other">Other</option>
        </select>
      </td>
    </tr>
  </tbody>
</table>
</form>
<div><h2>{{totalsum}}</h2></div>

Solution

  • The error is due to the typing of FormArray

    FormArray<TControl extends AbstractControl<any, any> = any>

    In angular 14 It is important to specify expected type of the element in the FormArray

    You could fix it by passing items FormArray expected type

     items: FormArray<FormGroup>;
    

    Forked Example