The piece I was missing was mentioned in rusev’s answer, and that is injecting the ControlContainer
.
Turns out that if you place formGroupName
on a component, and if that component injects ControlContainer
, you get a reference to the container which contains that form. It’s easy from here.
We create a sub-form component.
@Component({
selector: 'sub-form',
template: `
<ng-container [formGroup]="controlContainer.control">
<input type=text formControlName=foo>
<input type=text formControlName=bar>
</ng-container>
`,
})
export class SubFormComponent {
constructor(public controlContainer: ControlContainer) {
}
}
Notice how we need a wrapper for the inputs. We don’t want a form because this will already be inside a form. So we use an ng-container
. This will be striped away from the final DOM so there’s no unnecessary element.
Now we can just use this component.
@Component({
selector: 'my-app',
template: `
<form [formGroup]=form>
<sub-form formGroupName=group></sub-form>
<input type=text formControlName=baz>
</form>
`,
})
export class AppComponent {
form = this.fb.group({
group: this.fb.group({
foo: 'foo',
bar: 'bar',
}),
baz: 'baz',
})
constructor(private fb: FormBuilder) {}
}
You can see a live demo on StackBlitz.
This is an improvement over rusev’s answer in a few aspects:
- no custom
groupName
input; instead we use theformGroupName
provided by Angular - no need for
@SkipSelf
decorator, since we’re not injecting the parent control, but the one we need - no awkward
group.control.get(groupName)
which is going to the parent in order to grab itself.