If you started learning Angular from version 1 (AngularJs), maybe you will be more familiarized with configuring a clean Html to handle states and validations in the form. This approach is known as Template-Driven Forms.
In contrast Angular count with Model-Dirven Forms or Reactive Forms which delegates most of the responsability to apply validations and keep states (valid, invalid, touched and dirty) in the form over an object called FormControl located in the TypeScript and not in the HTML like the Template-Driven Forms.
Template Driven Forms
| Pros | Cons |
| -Very easy to write | Require more lines of HTML code and it can turn it difficult to mantain. |
| -Easy to read and understand | It can not be tested, there is no way to apply unit test to their validators. |
Reactive Forms
This powerful module provides a way to define a form as a FormGroup in the TypeScript class to handle states and validations rules that will be associated to each input in the form throught a formControlName.
Personally, I think that both are good ways to create Forms in Angular, but your decision over one or another approach is up your needs, the complexity of the forms, if you must create smalls forms with a low complexity you can use Template-Driven Forms, but if you need to create complex forms with Unit test, Reactive Forms will be a smart decision.
Let's create our first Reactive Form.
Open your console and type:
ng new reactive-forms
? Would you like to add Angular routing? (y/N) y
cd reactive-forms
cd .\src\app
Import the module ReactiveFormsModule in the app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './layout/header/header.component';
import { FooterComponent } from './layout/footer/footer.component';
import { HttpClientModule } from '@angular/common/http';
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [AppComponent, HeaderComponent, FooterComponent],
imports: [BrowserModule, AppRoutingModule, HttpClientModule, ReactiveFormsModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
Create a folder inside app named components and create a component called create-fruits
ng g c create-fruits
Define the structure of the Form in the class CreateFruitsComponent
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Record } from 'src/app/model/record';
@Component({
selector: 'app-create-fruits',
templateUrl: './create-fruits.component.html',
styleUrls: ['./create-fruits.component.scss'],
})
export class CreateFruitsComponent implements OnInit {
title!: string;
form!: FormGroup;
fruit!: Record;
constructor() {}
ngOnInit(): void {
this.form = new FormGroup({
name: new FormControl('', [Validators.required]),
description: new FormControl('', [Validators.required]),
image: new FormControl('', [Validators.required]),
price: new FormControl(0, [Validators.required]),
});
}
onSubmit(): void {
console.log(this.form);
}
}
As I mentioned before, defining the form in the TypeScript provides a good way to handle the states of our form and all the formControls inside it.
create-fruits.component.html
<section class="section">
<div class="container">
<h1>New Fruit</h1>
<div class="form" [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="form-group">
<label class="label">Name :</label>
<input
type="text"
id="name"
formControlName="name"
placeholder="Name"
class="input"
/>
<p
class="help is-danger walkP"
*ngIf="
(form.controls['name'].dirty || form.controls['name'].touched) &&
form.controls['name'].errors &&
form.controls['name'].errors['required']
"
>
Name is required!
</p>
</div>
<div class="form-group">
<label class="label">Description :</label>
<input
type="text"
id="description"
formControlName="description"
placeholder="Name"
class="input"
/>
<p
class="help is-danger walkP"
*ngIf="
(form.controls['description'].dirty ||
form.controls['name'].touched) &&
form.controls['description'].errors &&
form.controls['description'].errors['required']
"
>
Description is required!
</p>
</div>
<div class="form-group">
<label class="label">Image :</label>
<input
type="text"
id="image"
formControlName="image"
placeholder="Image"
class="input"
/>
<p
class="help is-danger walkP"
*ngIf="
(form.controls['image'].dirty || form.controls['name'].touched) &&
form.controls['image'].errors &&
form.controls['image'].errors['required']
"
>
Image is required!
</p>
</div>
<div class="form-group">
<label class="label">Price :</label>
<input
type="number"
id="price"
formControlName="price"
placeholder="Price"
class="input"
/>
<p
class="help is-danger walkP"
*ngIf="
(form.controls['price'].dirty || form.controls['name'].touched) &&
form.controls['price'].errors &&
form.controls['price'].errors['required']
"
>
Image is required!
</p>
</div>
<div class="field is-grouped">
<div class="control">
<button
type="submit"
class="button is-success"
(click)="onSubmit()"
[disabled]="!form.valid"
>
Save
</button>
</div>
<div class="control">
<button class="button is-warning">Cancel</button>
</div>
</div>
</div>
</div>
</section>
Note that I am not using the classic tag form to encapsulate all my html elements, it is not necesary , because I am using a div with a [formGroup]="form" , additionally I am associating each input with its right formControl defined in the TypeScript.
I hope that this post help you in your learning of Angular.
Top comments (0)