We've been facing at work a lot of issues with form and nested forms.
After a lot of research around that topic to simplify our lives we came up with a library that we decided to open source. It's a super tiny wrapper that you can use for both reactive or template forms (would definitely recommend reactive ones though).
The library is called ngx-sub-form: https://github.com/cloudnc/ngx-sub-form
The readme should contain everything you need to discover the library but I've also written an article here: https://dev.to/maxime1992/building-scalable-robust-and-type-safe-forms-with-angular-3nf9 to go more into details.
Now, I've transformed your stackblitz to use ngx-sub-form, here's how it'd look:
https://stackblitz.com/edit/user-nested-form-group?file=src/app/app.component.ts
Code:
app.component.ts
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
public userUpdate(user: User): void {
// every time the form changes
// this method will be called
console.log(user);
}
}
app.component.html
<app-user-form (userUpdate)="userUpdate($event)"></app-user-form>
user-form.component.ts
@Component({
selector: "app-user-form",
templateUrl: "./user-form.component.html",
styleUrls: ["./user-form.component.css"]
})
export class UserFormComponent extends NgxAutomaticRootFormComponent<User> {
@DataInput()
@Input("user")
public dataInput: Required<User>;
@Output("userUpdate")
public dataOutput: EventEmitter<User> = new EventEmitter();
protected getFormControls(): Controls<User> {
return {
name: new FormControl(null),
address: new FormControl(null),
};
}
}
user-form.component.html
<div [formGroup]="formGroup">
<input type="text" [formControlName]="formControlNames.name" placeholder="Name">
<app-address-control type="text" [formControlName]="formControlNames.address" placeholder="Address"></app-address-control>
</div>
<pre>{{ formGroupValues | json}}</pre>
<!-- So you can simply do: -->
<ul>
<li>Name: {{ formGroupValues.name }}</li>
<li>
Address
<ul>
<li>Country: {{ formGroupValues.address.country }}</li>
<li>State: {{ formGroupValues.address.state }}</li>
</ul>
</li>
</ul>
In the above, notice how easy it is to access the nested values
address-control.component.ts
@Component({
selector: "app-address-control",
templateUrl: "./address-control.component.html",
styleUrls: ["./address-control.component.css"],
providers: subformComponentProviders(AddressControlComponent)
})
export class AddressControlComponent extends NgxSubFormComponent<
Address
> {
protected getFormControls(): Controls<Address> {
return {
country: new FormControl(null),
state: new FormControl(null)
};
}
}
address-control.component.html
<div [formGroup]="formGroup">
<input type="text" [formControlName]="formControlNames.country" placeholder="Country">
<input type="text" [formControlName]="formControlNames.state" placeholder="State">
</div>
This is not the place to explain all the features of the library so I'll let you dig into the readme or article but by doing so you'll get extra type safety too when using AoT and many more things.
When looking at the demo also open the console to see that you can easily react whenever the form changes.