Intro
This article is the first of a 3 part series about Reactive Form, and its usage in complex scenarios.
From now on, "AngularJs" refers to Angular version 1, and "Angular" refers to Angular v2+.
The article consists of three major points:
- Introduction to AngularJs forms.
- Transitioning to Angular Template Driven Forms.
- Building blocks of Reactive Forms.
Without going into further detail on how forms worked in AngularJs,you used ngModel
in your template to build a form. Although it worked, was it easy, testable and scalable?
Template Driven Form
Before Angular arrived, everybody was scared, because there were many breaking changes. In response to that, the Angular team created strategies that are similar to the ones from AngularJs. One good example is the Template Driven Form.
Although many things changed, you can use forms in almost the same way. You just need to use ngModel
in your template to build a form.
Lets build the form shown above using Template Driven Forms. You will start in the name
field, but you will repeat the same steps for every field.
Declare name
property in the component.
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.scss' ]
})
export class AppComponent {
name: string
}
Add name
to ngModel
.
<!--app.component.html -->
<div class="form-control">
<label>Name: </label>
<input name="name" type="text" [(ngModel)]="name">
</div>
Now that the first input is ready, do it for the other fields.
By now, you should have your form ready. Now, lets try to print the form as you change it. You will work with ngModelChanges
from ngModel
, which calls a method when an input changes. Lets see how that would work in the example.
Create a nameChanged
method.
// app.component.ts
nameChanged(name) {
this.person = { ...this.person, name };
console.log(this.person);
}
Add nameChanged
to ngModelChanges
.
<!-- app.component.html -->
<div class="form-control">
<label>Name: </label>
<input name="name"
type="text"
[ngModel]="name"
(ngModelChange)="nameChanged($event)">
</div>
It's done! After doing it for every input, you should see something like this.
Reactive Form
ReactiveForm provides a model-driven approach to handling form inputs with values that change over time.
Angular Docs
Is important to notice that it is based on RxJs streams, and manages its internal state in an immutable way. By allowing you to define the form programmatically, you are able to unit test it. It comes with a set of helper methods to make your life easier.
AbstractControl: This is the base abstract class for FormControl, FormArray and FormGroup. Thanks to this, you are able to compose forms with any combination of those.
FormControl: This is the main building block for a Reactive Form. You will see a lot of these, if you haven't already.
name = new FormControl('');
You have a lot of properties and methods available in name
, allowing you to play with the FormControl.
FormArray: If you need a list of values, it will help you a lot.
phones = new FormArray([
new FormControl('')
]);
FormGroup: If you have complex data structures, you are going to need this.
car = new FormGroup({
brand: new FormControl(''),
model: new FormControl(''),
year: new FormControl(null)
});
FormBuilder: FormBuilder is a service that allows you to create ReactiveForms more elegantly. It's almost always the best choice in complex scenarios.
It is totally up to you which alternative to use. This is how it would look in both cases. car and carFb behave the same way
import { FormGroup, FormControl, FormArray, FormBuilder } from '@angular/forms'
@Component({...})
class ExampleComponent {
// Using Controls directly
car = new FormGroup({
brand: new FormControl(''),
model: new FormControl(''),
year: new FormControl(null),
availableColors: new FormArray([])
});
// Using FormBuilder
carFb = this.fb.group({
brand: [''],
model: [''],
year: [null],
availableColors: this.fb.array([])
});
constructor(private fb: FormBuilder) {}
}
The main difference is that you have to inject the FormBuilder service. Also, instead of using the constructors directly, you use FormBuilder.
Now, the moment you were waiting for: how to use this. You are going to build the same form you did with Template Driven Forms, but while using Reactive Forms.
Inject FormBuilder
, and create personForm
with it.
// app.component.ts
import { Component } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';
interface Person {
name: string;
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.scss' ]
})
export class AppComponent {
personForm = this.fb.group({
name: ''
});
constructor(private fb: FormBuilder) {}
submit(form: FormGroup) {
console.log(form.value);
}
}
Add personForm
to formGroup
, and name
to formControlName
.
<!-- app.component.html -->
<div class="container" [formGroup]="personForm">
<h1>Reactive Form</h1>
<div class="form-control">
<label>Name: </label>
<input type="text" formControlName="name">
</div>
<div id="submit-control">
<button (click)="submit(personForm)">Submit</button>
</div>
</div>
Use valueChanges
observable to log form changes.
ngOnInit() {
this.personForm.valueChanges.subscribe(
(person: Person) => console.log(person)
);
}
You did it! Both of your solutions should do the exact same thing. Go and finish adding the missing form controls.
Conclusion
By now, you should be convinced of the power of ReactiveForms. In future parts of this series, you'll learn how to test and validate them, which is where it starts to shine. Whether or not you like this form's implementation, it came to stay, and you should if you haven't already, start using it in your day to day as a Modern Web Developer.
Code Snippets:
References:
This article was written by Daniel Marin who is a Software Developer at This Dot.
You can follow him on Twitter at @danm_t.
Need JavaScript consulting, mentoring, or training help? Check out our list of services at This Dot Labs.
Top comments (1)
Hey, Dan! Do you think you'll be interested in a paid freelance writing opportunity for blog.soshace.com?