Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling big reactive forms in Angular 7

I'm trying to find a better approach to handle complex angular forms. The form is really big and I need to find an approach to reduce complexity.

Here is an example of form structure:

{
    "fieldA" : ...,
    "fieldB" : ...,
    "fieldC" : ...,
    "profile": {
        "username": ...,
        "email": ...,
        "firstName": ...,
        "lastName": ...,
        ...
    },
    "settings": {
        "enableEmailNotification": ...,
        "notificationsEmail": ..., // required when enableEmailNotification
        ...
    },
    ...
}

There are cases when validators are changed on the fly, for example when enableEmailNotification=true, component will add Required validator to notificationsEmail

Here are researched options:

Option #0 - Classic

sample on github

This approach uses one form and one component.

Pros:

  • A lot of code, but very simple

Cons:

  • All logic is in one place. For my case this component becomes too big and hard readable or maintainable
  • UI also becomes big enough

Option #1 - Passing FormGroup to subcomponent

sample on github

This approach sends formGroup to inner components as @Input() property.

Pros:

  • Reduces part of the view

Cons:

  • Form creation and validation rules are still on the parent component
  • Only view size is reduced
  • Validation logic is created in root component, but display errors in sub-component

Option #2 - Creating custom ControlValueAccessor

sample on github

Based on a this article we can create custom ControlValueAccessor which will return an object for a part of a form.

Pros:

  • Splits form in multiple forms. Form can be splitten in smaller independent parts.

Cons:

  • Keeping JS object for a form value. Doesn't look very good
like image 868
Maxian Nicu Avatar asked Aug 15 '19 22:08

Maxian Nicu


People also ask

Is reactive forms are used for large size forms?

Reactive forms are more scalable than template-driven forms. They provide direct access to the underlying form API, and use synchronous data flow between the view and the data model, which makes creating large-scale forms easier.

What is difference between FormBuilder and FormControl?

In Angular, a reactive form is a FormGroup that is made up of FormControls. The FormBuilder is the class that is used to create both FormGroups and FormControls.

Why FormBuilder is used in Angular?

The FormBuilder provides syntactic sugar that shortens creating instances of a FormControl , FormGroup , or FormArray . It reduces the amount of boilerplate needed to build complex forms.


1 Answers

My strategy for large complex forms is to have a wrapper component and sub components. each sub component has it's own form service and the wrapper has a master form service with the sub form services injected, consider this

@Component({
  selector: 'form-wrapper',
  template: `
    <form [formGroup]="form" (submit)="save()">
      <sub-form-a></sub-form-a>
      <sub-form-b></sub-form-b>
      <input type="submit" value="Submit Form">
    </form>
  `,
  providers: [MasterFormService, FormAService, FormBService]
})
export class FormWrapper {
  constructor(private formService: MasterFormService) { }
  save() {
    // whatever save actions here
  }
}

@Component({ // form b compoent is basically the same
  selector: 'sub-form-a',
  template: `
    ... whatever template belongs to form a ..
  `
})
export class FormAComponent {
  form: FormGroup
  constructor(private formService: FormAService) {
    this.form = this.formService.form;
    // form a specific actions
  }
}

@Injectable()
export class MasterFormService {
  form: FormGroup;
  constructor(private fb: FormBuilder, formAService: FormAService, formBService: FormBService) {
    this.form = this.fb.group({
      groupA: this.formAService.form,
      groupB: this.formBService.form,
    });
  }
}

@Injectable() // formB service is basically the same
export class FormAService {
  form: FormGroup;
  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      .. whatever fields belong to form a ..
    });
  }
}

this method creates highly reusable sub forms and lets you modularize / isolate form logic and templates. I often find that sub forms typically belong in more than one place anyway, so it keeps my code very DRY. In your example in particular, you can easily reuse the settings form and profile form components elsewhere in your application. One or twice I've even nested this structure again for an extremely complex form.

The con is that the structure may appear complex but you get used to it quick.

like image 156
bryan60 Avatar answered Oct 24 '22 23:10

bryan60