Taking NG2 Forms to the next level

This article is based on Angular 2 final - using Reactive Forms

Github repo for demo: https://github.com/recurship/angular2-multicomponent-form

Angular 2.0 final has now been released and the new forms module is fully operational! As before, we have the ability to create Template Driven forms or Reactive forms (or a mixture of two). In this article I shall show you the true power of Reactive Forms by going through an implementation of a dynamic form.

The github repo linked above contains a fully functional demo. There is a service (mocked) which sends over the “components” and their respective fields, along with specific properties for each field. At the moment I am dynamically rendering the form in just one component and in the second just dynamically customizing it via this service. This means we can change its label, default values, visibility and other properties but cant add or remove fields.

Lets start with building the basic classes which will define our JSON structure. In our project I call the base class FormField. Each field can then be extended into multiple other fields including TextFields, SelectFields, Checkboxes and Radio buttons. We will add additional properties like options in select field and radio buttons in their respective classes.

Once we have the JSON structure, we will then create a simple service to fetch the JSON - again very standard Angular2 code here:

Then comes the class managing the form creation. Since we are using Reactive Forms we are generating the FormGroup and FormControl objects in this class. Essentially we are converting the data class FormField into FormControl. This is simply done by looping over the JSON returned and generating either the FormGroup (each group can have multiple FormControls) or FormControl. If we not too worried about giving the controls specific names, then we can also use FormArray. Since we are dealing with multiple components in this project, we are also storing all of the objects in this class (mainForm variable) so they are accessible over multiple components.

One final thing thats left is the validations. Each field can have an optional array of validations for that field. For this I have created several validation class as well which do basic checks from bring required, to more complex ones taking regex patterns for specific data entry. When we are initializing the form controls we pass these validations to the object as well, along with its default values. The final result looks something like this:

let sections = {};
for (let section of this.fields) {

// dynamically generate the control groups
let formGroup = {};
for (let field of section.fields) {
// Since we are using the FormBuilder we need to send each FormControl in the following format:
// field: [defaultValue, optionalArrayOfValidators]
formGroup[field.name] = [field.defaultValue].concat(this.getFieldValidators(field));
}

sections[section.section] = fb.group(formGroup);
}

this.mainForm = fb.group(sections);


Now that we have our form programatically generated and stored in the mainForm variable, we can render it to our view. In order to have a nice clean view and isolated logic, we will create a component which can take the FormControls and render them according to what kind of control it is. This component will also encapsulate all the validations view code and any other functionality needed for form fields. We can use the ngSwitch or ngIf attributes to check for the type of field we want to render and add the code like below:

And thats about it! With our fields being rendered, we now have a form which is being dynamically rendered / customised. The last part for this article is getting all the data once the form “submits”, and this is where the real magic happens. Since we have all the fields stored in the service (FormManager), all we need to do is ask for the value of the mainForm (mainForm.value) and it will return the all the values for all the inputs according to the formGroups generated! :mindblown: In case of the repo above, you will notice that in the FormManager class we are “subscribing” to the mainForm variable. This allows us to get an update whenever any field in the form changes, this can be done with groups or with individual form fields.

And essentially this is where the power of Angular2 really lies. We had to write virtually no code for serialization, validations or setting value for any of the fields - it was almost like we created a skeleton form and Angular just breathe life into it.

The repo linked above goes even further and utlises each FromGroup in its own component. This allows us to spread a very large form over several components and get all the results quickly at the end.

Would you guys do anything different while implementing the dynamic forms above? Let me know in the comments below!

Resources
- Angular Official Documentation on Forms
- Angular Official Documentation on Dynamic forms
- Webinar: latest Angular 2 Forms w/ Kara Erickson
- Ultimate guide to forms in Angular2
- Implementing Angular2 forms, beyond the basics (RC3 code)