Angular forms: best practise for complex nested reactive forms and validation at root component
Asked Answered
S

2

12

I've been struggling for a few days to find a good pattern for my use case. I have a very complex reactive form, full of nested components, some fields are required, some fields may appear given certain conditions, etc... and this is creating a huge problem maintaining the code. The approach I used so far it passing the formControl to the children components and populate from there, but things started getting really hard to debug given the size of the form. A must for this form is that on submit of the whole form, a validation of all the nested fields is performed, and markAsTouched any field that is required but hasn't been inserted. I have been exploring 2 approaches but none of the 2 seems to be of any help:

  • Control Value Accessor: the concept is perfect, the logic is split perfectly among the children without the parent having to concern about it, but the CON is that the children controllers are not exposed and I can't markAsTouched all the sub forms to show the respective errors
  • Control Container: This approach seems to be needing all of the controls to be defined at the parent level, but being a very big form, it seems really counterproductive and not really solving my problem.

I wonder if anyone has any experience with such forms and able to provide some guidance on what are the best practises in this case. I have made a very simple stackblitz with just one sub child using Control Container, unfortunately I wasn't able to make it run https://stackblitz.com/edit/angular-ivy-axbgr5

Subserve answered 16/6, 2020 at 19:2 Comment(2)
Some reading in the topic link.medium.com/TUIPSUU6m7 The Best Way to build reactive sub-forms with Angular by Thomas TrajanGoggin
thank you @AlexBiro I will readSubserve
F
15

Complex forms in Angular can be a big trouble. From my experience the best approach is to create a stateful parent component and many children stateless components.

Parent component needs to be dedicated for particular form. Child components can be reused everywhere many times.

Parent component rules:

  • stateful
  • creates and holds form definition
  • emits form state (value, valid, pristine) on every form change
  • holds custom validation logic

Children components rules:

  • stateless
  • receives form parts (nested FormGroups) from parent
  • no custom validation logic

In above scenario children components are "reusable views" without any validation logic. It will always comes from parent.

How to pass form parts to children components`?

You can pass nested FormGroups in following ways:

ControlValueAccessor

In my opinion using ControlValueAccessor for creating form parts is not a good idea. Validation logic is encapsulated inside. It is good approach for create some really hard parts like a "color picker" but not just a "customer address" with several fields.

Business logic out of component

I have also tried to move business logic out of component with following simple code:

constructor(public businessLogic: VendorBusinessLogicService) { }

ngOnInit() {
    this.form = this.businessLogic.createForm(this.initialValue);

    this.subscription = this.form.valueChanges.subscribe(value => {
        this.businessLogic.applyBusinessLogic(value);
        this.emitFormState();
    });

    this.emitFormState();
}

Of course the requirement is to hold form reference inside the service. To be honest I cannot see benefits of it. This service for business logic looks scary. (https://github.com/p-programowanie/angular-forms/tree/forms-separated-business-logic)

Fullfaced answered 16/6, 2020 at 22:15 Comment(2)
I really like this answerWast
To point out more why not to use ControlValueAccessor: if you have complex forms (split up across several tabbed views i.e.) and you want to get the validation status of that complete form but without forcing the user to step through all tabs again, then you wont get the validation status encapsulated in the child components because it will only be computed as soon as the child component gets rendered.Alsatia
G
1

I found it is best to create one dedicated service with all the form logic and structure. All the validations, dependencies, subscriptions, filling in the form groups and array are there. This way it is really easy to test the form parts and reuse them.

Gaultiero answered 16/6, 2020 at 19:10 Comment(2)
I thought of that, but then I would still have a really really massive form to deal with and the logic is very complex, I prefer to deal it in their respective componentsSubserve
we tried it once. our team found it is difficult to support. I honestly see downsides and advantages in both approaches (:Gaultiero

© 2022 - 2024 — McMap. All rights reserved.