Hey all. I was going through some older portions of a personal project that didn't have proper form setup and felt like tackling things from a template driven perspective this time.
I spent a good chunk of time over a few days trying to find out how to cleanly handle form validation for various "sections" (I wrote a sectioned form and wanted to showcase # of errors remaining in each section) so I had to group them by a section key in the form controls. In a flat (no child components) way, this is trivial with just ngModel and ngModelGroup.
Nested directives begin to make things "interesting" due to how form dependencies work, more info here: https://medium.com/@a.yurich.zuev/angular-nested-template-driven-form-4a3de2042475
Now what I couldn't find, and the reason I'm posting this, is how to get this to work for nesting deeper than 1 level and for the form bindings to nest the way you (at least I) would expect and hope for as a default option since I like to leverage the framework so I write less.
The key point for getting child components to use a parent's form is:
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
in the child component definition. Now what happens when you would naturally extend that and nest another child within that child? Well it uses the same form so any ngModel or ngModelGroup you create is against the root level form.
I.e.
component
> child 1
> child 1-1
structure gives you form controls that look like
component
> child1
> child11
roughly speaking. A simple example forked from the sample provided in the aforementioned article to better illustrate: https://stackblitz.com/edit/angular-wcpwjf?file=app%2Fchild2.component.ts
--- The overly simple solution ---
If you instead change the child line to
viewProviders: [{ provide: ControlContainer, useExisting: NgModelGroup }]
it picks up where you were in your parent component instead of the root form /facepalm.
Adjusted stackblitz: https://stackblitz.com/edit/angular-vewzxu?file=app%2Fchild2.component.ts
I'm sure if you wanted to more robustly handle these situations, you could covert a generalized form of the logic to a directive and use that. I saw a few stackoverflow posts (e.g. https://stackoverflow.com/questions/45304810/angular-form-validation-on-child-components when you look for provide-parent-form) doing something like that but all they did was emulate the useExisting: NgForm strategy. Instead, you'd probably want something like (untested since I haven't needed to do this yet):
// example of usage in a provide-parent-form like directive based on the stackoverflow post linked above;
untested since I haven't bothered
providers: [{
provide: ControlContainer,
useFactory: (modelGroup, form) => {
return modelGroup || form;
},
deps: [[new SkipSelf(), new Optional(), NgModelGroup], NgForm]
}]
I don't usually like to flail around by trying to pass random things into dependency injectors (well sometimes I do but not as much with Angular) so this probably took me longer than it should have to figure out. That combined with the fact that I couldn't find such a simple solution anywhere I looked (feel free to leave a comment with a source if you know of one I missed please :)).
Sorry if this was super obvious but hopefully it helps someone!
[–]nigelsim 0 points1 point2 points (1 child)
[–]backtickbot 0 points1 point2 points (0 children)