Tips to organize better Reactive Forms in Angular

When building applications at some point you will need the user input, either a value or a whole set of data. That’s when forms come into play. 

Angular provides two types of forms: Template Driven and Reactive Forms. Depending on your requirements you will have to choose between the two. Template driven forms are easier to set up, but less scalable. If you have an easy form such as login where you will have only two inputs it might be a good idea to use template driven forms. Reactive forms are more customizable and scalable. If you have lots of data you are expecting from the user you need to go with reactive forms. I will list below some tips and tricks how to manage large forms. 

  1. Group your form controls

When creating a form you might have a large number of form controls. My advice is to group them. Each of the groups will correspond to a component. Advantages of grouping form controls are scalability and reusability. Now I will show you an easy example to demonstrate why it is a good practise to group the form controls. 

Let’s suppose you are creating a poll to ask students some questions. This will be managed by a form and we’ll send the request to backend to save the results.  We will ask the students general information, subjects related information and if the student is a freshman we will ask them a couple more questions. 

getPollFormGroup(): FormGroup {
    return new FormGroup({
        firstName: new FormControl([Validators.required]),
        lastName: new FormControl([Validators.required]),
        isFreshman: new FormControl(true),
        freshmanForm: this.getFreshmanFormGroup(),
        mathematicsForm: this.getSubjectFormGroup(),
        programmingForm: this.getSubjectFormGroup(),
        englishForm: this.getSubjectFormGroup()
    })
  }
  getSubjectFormGroup(): FormGroup {
    return new FormGroup({
       subjectRating: new FormControl([Validators.required]),
       professorRating: new FormControl([Validators.required]),
       difficultiesUnderstanding: new FormControl(),
       opinion: new FormControl()
    })
  }
  getFreshmanFormGroup(): FormGroup {
    return new FormGroup({
       gapsSubjects: new FormControl([Validators.required]),
       changes: new FormControl([Validators.required]),
       improvements: new FormControl([Validators.required])
    })
  }
}

This will be our initial component and we will modify it as we proceed. As we see in the constructor we are creating a new form group using getPollFormGroup function. We need a form control, which we will later visualize as a checkbox to manage the form if the student is a freshman. In that case we will ask for some more information such as if they have gaps in understanding the subjects, things they do not like and would change and improvements on the current curricula. On the other side we are asking for information for three subjects: mathematics, informatics and english. 

By grouping our form controls related to the subjects the form is more scalable and reusable. Without grouping the form controls our getPollFormGroup would look like this: 

getNonGroupedPollFormGroup(): FormGroup {
    return new FormGroup({
        firstName: new FormControl([Validators.required]),
        lastName: new FormControl([Validators.required]),
        isFirstYear: new FormControl(true),
        freshmanForm: this.getFreshmanFormGroup(),
        mathematicsForm: this.getSubjectFormGroup(),
        mathematicsRating: new FormControl([Validators.required]),
        mathematicsProfessorRating: new FormControl([Validators.required]),
        mathematicsDifficultiesUnderstanding: new FormControl(),
        mathematicsOpinion: new FormControl(),
        programmingRating: new FormControl([Validators.required]),
        programmingRatingProfessorRating: new FormControl([Validators.required]),
        programmingRatingDifficultiesUnderstanding: new FormControl(),
        programmingRatingOpinion: new FormControl(),
        englishRating: new FormControl([Validators.required]),
        englishProfessorRating: new FormControl([Validators.required]),
        englishDifficultiesUnderstanding: new FormControl(),
        englishOpinion: new FormControl()
    })
  }

The definition is longer and if later on we would like to add more subjects it would become unmanageable. The grouped form controls on the other hand is very scalable, we would just add new subjects polls quite easy such as: 

geographyForm: this.getSubjectFormGroup()

  1. Use smart and dumb components .

We need a smart component that will manage validation, error handling  and a dumb component which will be responsible to only visualize form controls. Subject Form will expect as input a form group and then will display the form controls. Splitting the logic from the visualisation will make it easier to manage the code when you need to make changes or add new form controls. Dumb components that only visualize data are easy to reuse as you can see below. Grouping form controls made it easier to reuse the subject form component. 

 <app-subject-form
        [form]="form.get('programmingForm')"
    ></app-subject-form>
 
    <app-subject-form
        [form]="form.get('englishForm')"
    ></app-subject-form>
  1. Subscribe to value changes.

When using the parent-child component structure where parent is the smart component and children are dumb components, if we want to do a specific action when a value changes we can subscribe to Event Emitters. Let’s suppose we want to show the user a message every time the rating of a subject changes we can subscribe and in the smart component we manage the function. From my experience instead of using @OutputEmitter in child component and subscribing to parent component we can use value changes such as:

ngOnInit() {
    this.form.get('programmingForm.subjectRating').valueChanges.
              pipe(filter(v => v != null))
              .subscribe(this.programmingRatingInterceptor);
 
  }
  programmingRatingInterceptor = (rating: number): void => {
      if (rating < 3) {
        console.log('We are sorry you share this opinion!');
      }
  }

So every time the rating inside the programming form changes we will print in console this message. It’s easier and more straightforward than subscribing to event emitters. Another advantage is that you can use rxjs operators to filter or manipulate data upon requests. 

As you can see grouping your form controls and then creating one smart component to manage the logic, validation and custom operations and some dumb components to visualise the form will make it easier to manage and scale. 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s