Angular Reactive Forms Tutorial

by | Aug 15, 2018

In this blog post we will explore model driven forms using Angular’s FormBuilder, FormGroup and FormControl classes. Angular provides developers two ways to build forms, one uses it’s template syntax (which we explored in my previous post) and the other is this model-driven approach. Choosing which form building technique to use is completely up to the developer. Angular reactive forms are usually easier to test since you are working with objects and models. In my experience, if you are going to need to use a robust set of validation, then you should build Angular reactive forms. 

 

NgExample

A quick note, this post is a part of a series of Angular posts. All of the code in this post, as well as links to the other posts in this series can be found on GitHub (links to other posts also included below). In this example I will be building a simple user admin form for creating a new user. Bootstrap 4 has been added to the project and will be used in building the Angular reactive form.

 

Module Setup

In the previous template driven forms example, we setup our module for the Angular reactive form example. If you already setup this module then you can skip this portion. In case you missed it we created a FormsExampleModule and added three components with the following steps. The “core” portion of the following commands reflects how this example repo has been setup, if you aren’t following along from previous examples or your project is setup differently, then “core” can be omitted.

  1. ng g module core/forms-example
  2. ng g component core/forms-example
  3. ng g component core/forms-example/reactive-example
  4. ng g component core/forms-example/template-example

Next I updated the routing inside the example application as I have previously for other examples. I won’t demonstrate those steps again here.

Our generated FormsExampleModule is a bit like a stand alone application at this point. We are going to have to provide the module with the correct application pieces to build our template driven Angular form. What I mean by that; we are going to be using form specific directives to build our Angular form, thus we need to bring those directives into our module to use them. We do this by importing the “FormsModule” and providing it to the “imports” array of our FormsExampleModule. After that we can get started building our template driven Angular form.

 

User Class

Again, if you followed the template driven post you can skip this portion because you’ve already done this. Next we are going to create our “User” class. This will act as our object model that will hold the information for our user. Create a user.ts file somewhere in your project and then create the following user class.

export class User {
    firstName: string;
    lastName: string;
    role: string;
    notes?: string;
}

It’s a pretty standard barebones class that we can use to demonstrate binding data between objects and forms.

 

Creating an Angular Reactive Form with FormGroup, FormControl and FormBuilder

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. I encourage you to check out those links to see the full class definitions of all three. Below I am going to demonstrate how to build a FormGroup using FormBuilder for our user form.

Inside of the reactive-example.component.ts file we will add the following. We will start with a basic form and add on to it as we go.

export class ReactiveExampleComponent implements OnInit {
  userForm: FormGroup;
  roles: Array<string> = [
    'Guest',
    'Admin',
    'Owner',
    'Operator'
  ];
  constructor(private formBuilder: FormBuilder) { 
    this.userForm = this.formBuilder.group({
      'firstName': [''],
      'lastName': [''],
      'role': [''],
      'notes': ['']
    });
  }
  ngOnInit() {
  }
}

You will need to import FormBuilder and FormGroup from @angular/forms. What we now have done is created a userForm of type FormGroup with 4 FormControls using the FormBuilder. The roles array will be used in our template. These controls are pretty bare bones at the moment but we will enhance these down the line. Next let’s build out our template.

 

Binding to a Template with formControlName

Our template is going to look very similar to what we built in the template driven forms example. However, instead of using ngModel and various form directives, we will bind our userForm to our template with [FormGroup] and formControlName. Add the following html code to the reactive-forms-example.component.html

<form [formGroup]="userForm">
  <div class="form-group">
    <label for="firstName">First Name</label>
    <input class="form-control" name="firstName" id="firstName" type="text" formControlName="firstName">
  </div>
  <div class="form-group">
    <label for="lastName">Last Name</label>
    <input class="form-control" name="lastName" id="lastName" type="text" formControlName="lastName">
  </div>
  <div class="form-group">
    <label for="role">User Role</label>
    <select class="form-control" name="role" id="role" formControlName="role">
      <option *ngFor="let userRole of roles" [ngValue]="userRole">{{userRole}}</option>
    </select>
  </div>
  <div class="form-group">
    <label for="notes">Additional Notes</label>
    <textarea class="form-control" name="notes" id="notes" formControlName="notes" rows="6" 
        placeholder="Add additional notes here."></textarea>
  </div>
</form>

So what we have done is provided the <form> an instance of a FormGroup (the userForm we initialized in the constructor). After doing that, we created input fields for each FormControl in the userForm and we bound the input fields to the userForm using formControlName, setting the formControlName to the name of our form control. Simple enough right? Now we have a form template built with data bound to our ReactiveExampleComponent. To see the state of our form I am going to add a button that just calls a function in our component that console.logs(this.userForm).

This is what we get when the form is logged after initialization and before it is touched. Logging your form is a good way to see what Angular has for the state of your form and can be valuable for debugging. As you dig in, you can see the state of the form controls and their values.

 

Initializing Form Data and Validation

Next we will add some simple validation to the form, as well as demonstrate how to initialize the form with some data. Use the User class to initialize a user with default data. After that we use a built in Validators class to set validation properties for each control. This is an optional section and can remain blank. You’ll notice that I have used an array here, that allows for multiple Validators to be used on a control. There are some pretty handy validators that are built in by angular. Custom validators can be written but that is outside the scope of this post.

  user: User = {
    firstName: 'New',
    lastName: 'User',
    role: 'Guest',
    notes: undefined
  };
  constructor(private formBuilder: FormBuilder) { 
    this.userForm = this.formBuilder.group({
      'firstName': [this.user.firstName, [Validators.required]],
      'lastName': [this.user.lastName, [Validators.required]],
      'role': [this.user.role, [Validators.required]],
      'notes': [this.user.notes, [Validators.maxLength(45)]]
    });
  }

It should be noted that there are a couple ways to do this, and this is just one of them. You could choose to create individual controls, but that would loose the functionality of the FormGroup. The nice this about the FormGroup is that it allows the developer to easily check if the whole form is valid. As you see in the console.log(this.userForm), the form has an invalid property that is a boolean.

 

NgSubmit

Finally I just want to demonstrate how to use ngSubmit with your Angular reactive forms. It’s pretty straight forward, you tag the form with a function you want to be called when the form is submitted. Then you just add a button of type submit somewhere in the form, usually the bottom. Looks something like this.

<form [formGroup]="userForm" (ngSubmit)="logFormValue()">
  ....
  <button type="submit" class="btn btn-default" [disabled]="!userForm.valid">Submit</button>
</form>

Using the valid property of the userForm we are able to prevent submitting an invalid form. Once valid and submitted, the logFormValue() function will be called. This same functionality can be used in template driven forms.

 

Conclusion

Above I have demonstrated how to build an Angular Reactive Form, with some basic validation and data binding. I would encourage you to familiarize yourself with the FormGroup, FormControl, and FormBuilder classes. There is a bunch of functionality that in those classes that will make building robust forms easier. I have added some additional functionality to my example repo, I encourage you to check it out and use it as a reference for your own projects. The repo link has links to my other posts as well, thanks for reading.