Reputation: 231
Let's say we have an address and want to reuse it in multiple Forms (e.g. Person, Company, ...) In Angular, everything is components, so we should probably write a component.
What is the best way, to do so? Turns out, it's not that easy. It should encapsulate the data and also validate the embedded form fields. I found two solutions to the problem:
1. Custom Form Component
What I don't like about it: overly complex, everything is delegated inside the Subcomponent. With validation, you will need some kind of "inner Form" for the validation to work. The form controls must be defined in the parent, encapsulation is not really possible. Simple example, see: https://stackblitz.com/edit/angular-nested-form-custum-component-test
2. Component, that has two Inputs: FormGroup and form-submitted-state
Idea taken from https://medium.com/spektrakel-blog/angular2-building-nested-reactive-forms-7978ecd145e4
Much simpler than the Custom Form Component. We need to provide the FormGroup, that is built outside the nested component. If we want to show validation errors "onSubmit", we also need to provide the form's 'submitted state' to the Child Component. Simple example, see https://stackblitz.com/edit/angular-nested-formcomponent-test
Any comments or better ideas to solve the problem?
Upvotes: 3
Views: 1788
Reputation: 52847
You can support encapsulation by passing the FormGroup to the nested control with an initial value, and allowing the nested control to define its own inner form group along with any validators.
app.component.html
<nested-form-cmp
init="foo"
[formSubmitted]="f.submitted"
[grp]="myForm">
</nested-form-cmp>
AppComponent would only need to initialize its own form data:
export class AppComponent {
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = fb.group({
name: ['', Validators.required]
})
}
submit(form: NgForm) {
console.log("Reactive Form submitted: " + form.submitted);
}
}
nested-form.component.html
The nested component would be responsible for creating its own nested FormGroup, and initializing it with validators:
<div [formGroup]="grp">
<div formGroupName="innerGrp">
<label>
Inner name:
<input formControlName="name2" type="text" id="outer"/>
</label>
<span class="error" *ngIf="(formSubmitted || grp.get('innerGrp.name2').touched) && grp.get('innerGrp.name2').hasError('required')">
Inner name is required
</span>
</div>
</div>
nested-form.component.ts
export class NestedFormComponent implements OnInit{
// The FormGroup built in the parent
@Input() public grp: FormGroup;
@Input() public init: string;
// Needed, because the FormGroup does not get the forms submitted state
@Input() public formSubmitted: boolean;
constructor(private fb: FormBuilder){
}
ngOnInit() {
this.grp.setControl('innerGrp', this.fb.group({
name2: [this.init, Validators.required]
}))
}
}
Upvotes: 1