1

I have setup a nested formgroup. So far everything works fine. But I am not sure how to set up the validation alerts properly when there are nested form elements.

One particular thing that happens is during the build I get error: Property 'controls' does not exist on type 'AbstractControl'

Here is my code:

import { Component, OnInit } from '@angular/core';
import { Validators, FormControl, FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  name = 'Reactive Form - Nested FormGroups';

  myForm: FormGroup;

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.buildForm();
  }

  buildForm() {

    this.myForm = this.fb.group({

      user : this.fb.group({
        'firstName': new FormControl('', Validators.required),
        'lastName': new FormControl('', Validators.required)
      }),

      'bio': new FormControl()
    });
  }
}

My problem is with the validation see the div with class="validation-message"

<hello name="{{ name }}"></hello>

<form [formGroup]="myForm">
  <div class="my-box" formGroupName="user">
    <label>
      First Name
      <input type="text" formControlName="firstName">
      <div class="validation-message" *ngIf="!myForm.controls.user.controls['firstName'].valid && myForm.controls.user.controls['firstName'].dirty">
        This field is invalid
      </div>
    </label>
  </div>
  <div class="my-box" formGroupName="user">
    <label>
      Last Name
      <input type="text" formControlName="lastName">
    </label>
  </div>
  <div class="my-box">
    <label for="bio" class="block-me">Bio</label>
    <textarea formControlName="bio"></textarea>
  </div>
</form>

<p>
  Data from form: {{ myForm.value | json }}
</p>
iChido
  • 3,972
  • 6
  • 26
  • 29
  • try... `myForm.get('user.firstName')` – AT82 Mar 27 '18 at 17:49
  • 1
    Does this answer your question? [Property 'controls' does not exist on type 'AbstractControl' Angular 4](https://stackoverflow.com/questions/46926182/property-controls-does-not-exist-on-type-abstractcontrol-angular-4) – Uriy MerkUriy Oct 29 '19 at 19:55
  • Can use a [custom interface](https://stackoverflow.com/a/58614356/1669165) – Uriy MerkUriy Oct 29 '19 at 19:56

2 Answers2

5

You have typo in your TypeScript code:

 user : this.fb.group({
    'firstname': new FormControl('', Validators.required),
    'lastName': new FormControl('', Validators.required)
  })

Field firstname is all lowercase but in HTML is camelcase:

<input type="text" formControlName="firstName">

Field names are case sensitive.

In your case you have TypeScript should be like this:

 user : this.fb.group({
    'firstName': new FormControl('', Validators.required),
    'lastName': new FormControl('', Validators.required)
  })

UPDATE:

To access user form group in HTML you can do by creating public property in your TypeScript:

  public get userGroup(): FormGroup {
    return this.myForm.get('user') as FormGroup;
  }

Now you can use in HTML in following way:

<div class="validation-message" *ngIf="!userGroup.controls['firstName'].valid && userGroup.controls['firstName'].dirty">
          This field is invalid
</div>
kat1330
  • 5,134
  • 7
  • 38
  • 61
  • Good catch @kat1330. That was me just putting together that stackblitz really quick. The only problem that remains is now fixing the build issue where I get "Property 'controls' does not exist on type 'AbstractControl'.". I'll make an edit. – iChido Mar 27 '18 at 18:27
  • @iChido I updated answer. You can also extract `user` group in your ts file as property and use that property in HTML. Please see updated answer. This way is also good approach if you have many nested groups. – kat1330 Mar 27 '18 at 19:49
2

The solution that fixes the error "Property 'controls' does not exist on type 'AbstractControl'" was to separate the nested form items in the typescript file into its own control. Code follows:

import { Component, OnInit } from '@angular/core';
import { Validators, FormControl, FormGroup, FormBuilder } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  name = 'Reactive Form - Nested FormGroups';

  myForm: FormGroup;
  userControl: FormGroup;

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.buildForm();
  }

  buildForm() {

    this.userControl = this.fb.group({
      'firstName': new FormControl('', Validators.required),
      'lastName': new FormControl('', Validators.required)
    })

    this.myForm = this.fb.group({

      user : this.userControl,

      'bio': new FormControl()
    });
  }
}

Html file...

<hello name="{{ name }}"></hello>

<form [formGroup]="myForm">
  <div class="my-box" formGroupName="user">
    <label>
      First Name
      <input type="text" formControlName="firstName">
      <div class="validation-message" *ngIf="!userControl.controls['firstName'].valid && userControl.controls['firstName'].dirty">
        This field is invalid
      </div>
    </label>
  </div>
  <div class="my-box" formGroupName="user">
    <label>
      Last Name
      <input type="text" formControlName="lastName">
      <div class="validation-message" *ngIf="!userControl.controls['lastName'].valid && userControl.controls['lastName'].dirty">
        This field is invalid
      </div>
    </label>
  </div>
  <div class="my-box">
    <label for="bio" class="block-me">Bio</label>
    <textarea formControlName="bio"></textarea>
  </div>
</form>

<p>
  Data from form: {{ myForm.value | json }}
</p>

Here's a working sample: https://stackblitz.com/edit/angular-nestedform

iChido
  • 3,972
  • 6
  • 26
  • 29