Reputation: 4634
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>
Upvotes: 1
Views: 6219
Reputation: 4634
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
Upvotes: 2
Reputation: 5332
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>
Upvotes: 5