24

we recently upgraded from ng 7.2.15 and typescript 3.2.4 to angular v8.2.14 and typescript v3.5.3. Passing the form group from a parent component to child component is no longer working.

Below is my child component: child component. ts

@Component({selector:'edit-overview'})
    export class ChildComponent implements OnInit {  
       public OverviewFormGroup : FormGroup
       constructor(private controlContainer: ControlContainer) {
       }

    ngOnInit() {
    this.OverviewFormGroup = <FormGroup>this.controlContainer.control;
    }
    }

child.component.html

<div [formGroup] ='OverviewFormGroup'>
</div>

and my parent component

Parent.component.html

<div [formGroup]="Form1">
      <edit-overview [formGroup] = "Form1.controls.OverviewFormGroup">
      </edit-overview>
</div>

Parent.component.ts

export class ParentComponent {
constructor(private readonly fb: FormBuilder) {
this.Form1 = this.fb.group({
 name: new FormControl('', [Validators.required]),
 OverviewFormGroup: new FormGroup({
        description: new FormControl('', [Validators.nullValidator])
      })
})
}
}

It throws this error: Type 'AbstractControl' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 3 more at the parent template line.

Earlier passing a formgroup from parent to child was no longer an issue. Am i missing something here? I have already given value for controls in the Form group declaration.

ccpizza
  • 28,968
  • 18
  • 162
  • 169
Priyanka
  • 368
  • 1
  • 3
  • 12
  • 1
    my mistake.Should have changed parent template to formGroupName instead of [formGroup]. Worked after that! – Priyanka Dec 12 '19 at 05:10

3 Answers3

31

Good question, it is an obscure error. The error comes from typesafety features! Because Angular cannot know that it is a form group from the way you're accessing it (it just knows it is an AbstractFormControl), you need to tell it!

Option 1: Use as to make the type explicit

So for example if you have:

  mainForm: FormGroup = this.formBuilder.group({
    name: ['Max'],
    address: this.formBuilder.group({
      street: ['PayneStreet'],
      zip: [50000]
    })
  });

In order to let Angular know that you're actually accessing a form control, you need to tell it:

  get addressForm() {
    return this.mainForm.get('address') as FormGroup;
  }

The important part is the as FormGroup. Like this Angular knows that it's actually a FormGroup and not just an abstract form control. Then you can go ahead and use it in the html:

<app-address-form [addressForm]="addressForm"></app-address-form>

Find the stackblitz here: https://stackblitz.com/edit/angular-nested-forms-input

Option 2 (preferred): Define the nested group separately

It is actually easier and more typesafe to just build the second form separately and then to attach it to the main form:

  addressForm = this.formBuilder.group({
    street: ["PayneStreet"],
    zip: [50000]
  });

  mainForm: FormGroup = this.formBuilder.group({
    name: ["Max"],
    address: this.addressForm
  });

and in the html (same as above)

  <app-address-form [addressForm]="addressForm"></app-address-form>

Like this, Angular knows already that addressForm is of type FormGroup and not just of type AbstractFormControl

Stackblitz: https://stackblitz.com/edit/angular-nested-forms-input-2

bersling
  • 17,851
  • 9
  • 60
  • 74
  • Good answer, solved my error, had to "interrogate" the AbstractFormControl using the as keyword, thanks!! – vicgoyso Feb 22 '21 at 18:44
  • 8
    Hi @bersling, I was wondering how you would use your trick within a for loop. `ng-container *ngFor="let addressForm of addresses.controls"` taking `address` is a `FormArray` filled with `FormGroup`. – Raphaël Balet Sep 08 '21 at 09:01
  • 1
    @RaphaëlBalet, check this out [link](https://stackoverflow.com/a/67366344/8607675). I tested it with strict templates and it`s working. – Alexander Apr 28 '22 at 08:16
  • @Alexander Thx, but in your example, what if you would like to do the following `demoForm.variable` ? – Raphaël Balet Apr 28 '22 at 08:55
  • @RaphaëlBalet, sorry for losing your time, after another test, I`ve got many errors related to reactive forms. – Alexander Apr 28 '22 at 18:39
  • @RaphaëlBalet I guess you already have found out but just create a getDemoForm() method in your ts file that returns your form as a FormGroup and use getDemoForm().variable in your html template file. – Tomas Andersen Oct 26 '22 at 20:23
  • This answer is correct, I add something complementary for my specific case, I had to create a function that receives an **AbstractControl** type parameter `function(parameter: AbstractControl): FormGroup { return form as FormGroup; }` – FRANCISCO J. BLANCO Dec 12 '22 at 14:39
0

Using the formbuilder api, you dont need to explicitly define form controls. Try:

export class ParentComponent {
  constructor(private readonly fb: FormBuilder) {
    this.Form1 = this.fb.group({
      name: ['', [Validators.required]],
      OverviewFormGroup: this.fb.group({
        description: ['', [Validators.nullValidator]]
      })
    })
  }
}
C.OG
  • 6,236
  • 3
  • 20
  • 38
-10

Dependending on how comfortable you are with your strictness, you could turn it off for Angular templates by setting strictTemplates to false in your tsconfig.json like this:

{
 ...,
  "angularCompilerOptions": {
    ...,
    "strictTemplates": false,
  }
}
pol
  • 457
  • 4
  • 5