Skip to content

Instantly share code, notes, and snippets.

@BioPhoton
Created March 8, 2020 08:57
Show Gist options
  • Save BioPhoton/bf44ec0933d1d9e32d4ca80dfab02453 to your computer and use it in GitHub Desktop.
Save BioPhoton/bf44ec0933d1d9e32d4ca80dfab02453 to your computer and use it in GitHub Desktop.

Reactive Forms

In this section we will learn the basics of reactive forms in angular. We will learn how to set reactive forms up, use built-in validators and display validation errors.

Before we start there are some things to know. Ractive forms are fully functional without any HTML. All of the form depending logic is placed in the conponent as code and not in the template. This make our html very easy to read and edit. We can move parts of the HTML without break form related logic.

1. Setup and Connection to HTML

1.1. Setup

To be able to use reactive forms you have to import the related Module. Open your app.module.ts and add ReactiveFormsModule to your imports:

// app/app.module.ts

...
import { ReactiveFormsModule } from '@angular/forms';
...

@NgModule({
  ...
  imports: [
    ...
    ReactiveFormsModule
  ],
  ...
})
export class AppModule {
}

After this we can try to check if everything is working fine by using the FormGroup as the type of a class property in flight-edit.component.ts. We will use the property editForm to test this. We add the FormGroup as a type of the editForm property.

// app/pages/edit-flight/edit-flight.component.ts

...
import {FormGroup} from '@angular/forms';

@Component({
...
})
export class FlightEditComponent implements OnInit {

  editForm: FormGroup;
  
  ...
}

If no error occurs we did everything right and are ready to go on.


In our ngOnInit method we start to create our editForm. We create a new FormControl and initialize it with 1 and assign it to a const called idControl. We also create a new FormGroup. As constructor parameter we insert an object with the key id and use the created contorl idControl as value and assign it to the property editForm.

// app/pages/edit-flight/edit-flight.component.ts

...
import {FormGroup} from '@angular/forms';

@Component({
...
})
export class FlightEditComponent implements OnInit {
 
  ...
  
  ngOnInit(): void {
    const idControl = new FormControl(1);
    this.editForm = new FormGroup({id: idControl});
  }
}

To test it we log the formsGroups value to the console.

// app/pages/edit-flight/edit-flight.component.ts
...
  ngOnInit(): void {
    ...
    console.log(this.editForm.value);
  }
 ...

1.2. Using the FormBuilder

There is a angular built-in service called FormBuilder. This service allows us to set up a form without all the instanciation of controls.

We start to use the FormBuilder by importing it into our constructor.

// app/pages/edit-flight/edit-flight.component.ts
import {... FormBuilder} from '@angular/forms';

@Component({
...
})
export class FlightEditComponent implements OnInit {
  ...  
  constructor(private fb: FormBuilder) {
    
  }
  ...
}

Next we replace the code for formEdit in our ngOnInit method and use the FormBuilder to create the FormGroup. We also create controls for the rest of the flight properties.

// app/pages/edit-flight/edit-flight.component.ts

export class FlightEditComponent implements OnInit {
  ...  
  ngOnInit() {
    this.editForm = this.fb.group({
      id:   [1],
      from: [],
      to:   [],
      date: []
    });
    console.log(this.editForm.value);
  }
  ...
}

In the console we should see the id with the value 1 property as well as all other new properties with the value null.


As reactive forms are fully functional without any template we can also check all other states of the form i.e. valid, touched, pristine.

// app/pages/edit-flight/edit-flight.component.ts

export class FlightEditComponent implements OnInit {
  ...  
  ngOnInit() {
    ...
    console.log(this.editForm.valid);
    console.log(this.editForm.touched);
    console.log(this.editForm.pristine);
  }
  ...
}

As a result in we should see true for the valid property, false for touched and true for pristine.

1.3. Connecting a form to it's template

To connect a reactive form to a template we need the formGroup and the formControlName directive. We apply the formGroup to the form tag and bind the editFrom property to it. Next we use the formControlName directive and assign the controls name as a string to it. i.e. id.

To check everything we add the controls value below the control. We access the formcontrol over the .controls property or the .get() method. As accessing the value over the .control property is more to type: editForm?.controls?.from.value we use the .get() method instead: editForm.get('from')

<!-- app/pages/edit-flight/edit-flight.component.html -->

<form [formGroup]="editForm">
  <input formControlName="id">
  id: {{editForm.get('id').value}}

  <input formControlName="from">
  from: {{editForm.get('from').value}}

  <input formControlName="to">
  to: {{editForm.get('to').value}}

  <input formControlName="date">
  date: {{editForm.get('date').value}}
</form>

If everything works fine you should be able to assigne data to the control and update it by typing into the inputbox. The result should also visible below the control.

2. Basic Form API and Validation

2.1. Basic Form API

Angular has some built in reflection of the forms and controls state into the related html elements.

If you take a look at you forms html you will see the states for valid/invalid, pristine/dirty and touched/untouched reflected into the emelents hhtml as css classes.

<form class="ng-pristine ng-valid ng-touched" ...>

The same classes are also reflected for the forms control state.

<input class="ng-pristine ng-valid ng-touched" ...> 

You can also access the controls state by access it over the form.controls property.

<!-- app/pages/edit-flight/edit-flight.component.ts -->

<input formControlName="from">
from.value: {{editForm.get('from').value}}<br>
from.valid: {{editForm.get('from').valid}}<br>
from.touched: {{editForm.get('from').touched}}<br>
from.pristine: {{editForm.get('from').pristine}}<br>
...

This should print the controls value and it's states to the screen.


One last unique feature to reactive froms you should know is that they expost their value changes and status changes over an observable.

Let's set this up and subscribe to it. In your ngOnInit method insert following:

// app/pages/edit-flight/edit-flight.component.ts
...
ngOnInit(): void {
    ...
   
    this.editForm
      .valueChanges
      .subscribe(
        (value => console.log('editForm value:', value))
      );

    this.editForm
      .statusChanges
      .subscribe(
        (status => console.log('editForm status:', status))
      );

    this.editForm
      .get('id')
      .valueChanges
      .subscribe(
        (value => console.log('id control value:', value))
      );

    this.editForm
      .get('id')
      .statusChanges
      .subscribe(
        (status => console.log('id control status:', status))
      );
}
...

If we type into the "id" control we should see logs for the controls and forms value and state changes.

2.2. Built-in Sync Validation

Angular ships with a set of default validators. They are related to the basic html5 built in form validations. In reactive forms they are implemented as pure functions and you can use them by importing the Validators class. The validators class contains all default validators as static methods.

Let's implement some default validators to our from control. First we import the Validators class into our file. Now we can access the Validators class static methods. We apply validators as the second argument in the array in form group.

 // app/pages/edit-flight/edit-flight.component.ts
 ...
 import {... Validators} from '@angular/forms';
 ...
  ngOnInit(): void {
    this.editForm = this.fb.group({
      id:   [1, Validators.required],
      ...
    });
    
  }
 ...		

If we want to use multiple validators for one control we put them in an array instead of inserting them directly:

// app/pages/edit-flight/edit-flight.component.ts
...
ngOnInit(): void {
  this.editForm = this.fb.group({
    id:   [1, Validators.required],
    from: [null, [Validators.required, Validators.minLength(3)]],
    to:   [null, [Validators.required, Validators.minLength(3)]],
    date: [null, [Validators.required]]
  });
  
}
...		

To check and display errors there are some methods that you can use. With the .errors property you receive an object containing all errors.

<!-- app/pages/edit-flight/edit-flight.component.html -->		
  	
<input  formControlName="from">		
       		
errors: {{editForm.get('from').errors | json}}		
...		

You can check for a specific error of a control over the .hasError() method, and get the value of a specific error over the .getError() method.

<!-- app/pages/edit-flight/edit-flight.component.html -->		
  	
<input  formControlName="from" ...>		
<div class="text-danger" *ngIf="editForm.get('from').hasError('minlength')">		
 A min length of {{editForm.get('from').getError('minlength').requiredLength}} is required		
</div>		
...		
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment