132

I am trying a nested reactive form in Angular 4. It is working fine but when I try to build AOT it's throwing the error

'controls' does not exist on type 'AbstractControl'

I googled and tried few things but no luck. Could anyone tell me how to fix this issue?

<div [formGroup]="myForm">
    <div formArrayName="addresses">
        <div *ngFor="let address of myForm.get('addresses').controls; let i=index" 
                    class="panel panel-default">
            <span *ngIf="myForm.get('addresses').length > 1"
                    (click)="removeAddress(i)">Remove</span>
            <div [formGroupName]="i">
                <mat-form-field>
                    <input matInput formControlName="city" placeholder="city" value="">
                </mat-form-field>
            </div>

        </div>
    </div>
    <a (click)="addAddress()" style="cursor: default"> Add +</a>
</div>

typescript code below

constructor(private _fb: FormBuilder) {     
}

ngOnInit() {
    this.myForm = this._fb.group({
        addresses: this._fb.array([
            this.initAddress(),
        ])
    });
}
initAddress() {
    return this._fb.group({
        city: ['']
    });
}
addAddress() {
    const control = <FormArray>this.myForm.get('addresses');
    control.push(this.initAddress());
}
removeAddress(i: number) {
    const control = <FormArray>this.myForm.get('addresses');
    control.removeAt(i);
}
Munna Babu
  • 5,496
  • 6
  • 29
  • 44

7 Answers7

270

Based on @Günter Zöchbauer comments , first i changed

myForm.controls['addresses'] to myForm.get('addresses') in both html and typescript

and then based on @yuruzi comment

changed myForm.get('addresses').controls to myForm.get('addresses')['controls']

Its working fine now. Thanks @gunter & yuruzi

Munna Babu
  • 5,496
  • 6
  • 29
  • 44
  • 4
    My case: form.get('user')['controls'].profile. its ok tnks – Rafael L R Jul 19 '19 at 12:33
  • 5
    This wrked for me seamlessly. Thanks Man! In the tempate ```myForm.get('addresses')['controls']``` In the ts ```const control = this.myForm.controls.addresses as FormArray``` – Kelvin Cayman Apr 04 '20 at 05:41
  • Kinda woodoo magic, it must be working without array notation.... – Majesty Jan 19 '21 at 10:05
  • I think @sunnykashyap's answer is preferred by keeping type safety in place. This way sets the type to 'any' which can still generate type errors (Element implicitly has an 'any' type because expression of type '"controls"' can't be used to index type 'AbstractControl') – Andrew Jan 29 '23 at 16:53
113

You can fix it easily though. Outsource the "get the controls" logic into a method of your component code (the .ts file):

getControls() {
  return (this.recipeForm.get('controlName') as FormArray).controls;
}

In the template, you can then use:

*ngFor="let ingredientCtrl of getControls(); let i = index"

This adjustment is required due to the way TS works and Angular parses your templates (it doesn't understand TS there).

ConnorsFan
  • 70,558
  • 13
  • 122
  • 146
sunny kashyap
  • 2,193
  • 2
  • 19
  • 29
12

Change myForm.get('addresses').controls to myForm.get('addresses').value will also fix the issue.

Advait Baxi
  • 1,530
  • 14
  • 15
10

As an update to @sunny kashyap solution, I would write it this way:

getControls() {
  return (this.recipeForm.get('controlName') as FormArray).controls;
}
Motasem Halawani
  • 156
  • 4
  • 12
2

for validation errors use...

<span *ngIf="f.YOUR_FORM_KEY.controls.YOUR_FORM_KEY.errors?.YOUR_FORM_VALIDATION">YOUR_FORM_KEY is YOUR_FORM_VALIDATION</span>

eg.

<span *ngIf="f.name.controls.name.errors?.required">Name is required</span>

ts file

get f(): any {
    return this.userForm.controls;
}
Mhar Daniel
  • 475
  • 5
  • 11
0

to get the length of a FormArray , use simply length :

<span *ngIf="myForm.controls['addresses'].length > 1" (click)="removeAddress(i)">Remove</span>

Hope it helps

Mohamed Ali RACHID
  • 3,245
  • 11
  • 22
0

Can use a custom interface

// Define AbstractFormGroup
export interface AbstractFormGroup extends FormGroup {
  controls: {
    [key: string]: AbstractFormGroup & AbstractFormGroup[] & AbstractControl & FormGroup & FormArray,
  }
}

// Usage example
class ... {
  myForm: AbstractFormGroup
  ...
  this.myForm = this.fb.group({...}) as AbstractFormGroup
}