Tutoriels Backend
Template Driven Forms avec angular
Les formulaires sont des éléments essentiels d'une Application Web.
Angular nous offre plusieurs moyens de les gèrer .
Dans ce tutoriel nous utiliserons le moyen le plus simple les Template Driven Forms.
Nous créerons une série de Formulaires (Forms) à partir de rien (from scratch).
Ce que nous allons faire
- Avant de Commencer
Qu'est ce qu'un Formulaire, comment Angular gère les Forms.
Template Driven Forms ou Reactive form ?
- Création de notre projet Angular
Nous utiliserons un projet existant contenant les fonctionnalités essentielles.
Le projet a été généré avec Angular CLI.
Il utilise le Routing et le Lazy Loading.
Il intègre le Framework CSS Bootstrap.
- Prototype bootstrap
Comment créer des formulaires avec bootstrap.
- Intégration dans notre projet angular
Comment Intégrer nos formulaires dans notre projet
- Effectuer les Tests
Réaliser les tests unitaires.
- Code source
Le code complet du projet sur github.
Qu'est ce qu'un formulaire
Les Formulaires sont des éléments incontournables d'une application Web.
Ils permettent de visualiser toute sorte de données.
Ils permettent de les créer, de les modifier ou de les supprimer.
Ils sont facilement reconnaissables et les utilisateurs sont totalement habitués à les utiliser.
Si vous regardez l'exemple suivant vous comprenez très vite qu'il s'agit d'un formulaire de film.
Il contient des informations comme son nom, sa date de sortie ou ses résultats au box-office.
De surcroît il dispose de plusieurs boutons (create, Update ou Delete) qui permettront d'effectuer des actions précises.
Et les plus habitués reconnaitront évidemment le style visuel du célèbre Framework CSS bootstrap.
Dans notre tutoriel nous allons voir comment intégrer ce type de formulaire dans une application angular.
Le développement informatique étant à forte connotation anglo saxonne, les exemples utiliseront la langue de shakespeare.
Le cinéma étant un domaine plus que connu les exemples seront simplement basés sur des films de la pop culture.
Angular et les Forms
La documentation Angular concernant les forms est accessible ici
https://angular.io/guide/forms
Nous essaierons de suivre les conseils qui y sont prodigués en respectant les Best Practices préconisées par Angular.
Angular gère les forms de deux manières.
- Template Driven Forms
- Reactive Forms
On pourrait diviser le développement d'une Application Web en 2 parties.
- La partie graphique
L'interface utilisateur ou UI
C'est à dire tout ce que nous voyons à l'écran.
- La partie logique
Que l'on pourrait aussi appelée partie Métier ou Partie traitement.
En faisant simple comment tout ceci fontionne.
De façon assez sommaire on pourrait résumer le fonctionnement des forms sous angular ainsi
- Template Driven Forms
La partie graphique et la partie logique sont traitées au même endroit au niveau du template (les fichiers html).
Le développement est simple mais s'avère peu performant sur des projets complexes.
- Reactive Forms
La partie graphique et la partie logique sont séparées très distinctement.
Partie grapique dans des fichiers HTML et partie logique dans les fichiers Typescript.
Cette méthode est plus difficile à mettre en place mais plus efficace sur des projets complexes.
Dans ce tutoriel nous allons traiter le cas des Template Driven Forms.
Traduisons en français tout d'abord ces 3 mots
- Template : modèle
- Driven : conduit , dirigé
- Form : formulaire
En traduisant mot à mot cela pourrait être Gestion des Formulaires via les templates.
Création du projet Angular
Pour pouvoir continuer ce tutoriel nous devons bien evidemment disposer de certains éléments
- Node.js : La plateforme javascript
- Git : Le logiciel de gestion de versions.
- Angular CLI : L'outil fourni par Angular.
- Visual Studio code : Un éditeur de code.
Vous pouvez consulter le tutoriel suivant qui vous explique en détails comment faire
Nous allons utiliser un projet existant dont les caractéristiques sont
- Genéré avec Angular CLI
- Gestion du Routing
- Gestion du Lazy loading
- Utilisation du Framework CSS Bootstrap
# Créez un répertoire demo (le nom est ici arbitraire)
mkdir demo
# Allez dans ce répertoire
cd demo
# Récupérez le code source sur votre poste de travail
git clone https://github.com/ganatan/angular-react-bootstrap.git
# Allez dans le répertoire qui a été créé
cd angular-react-bootstrap
cd angular
# Exécutez l'installation des dépendances (ou librairies)
npm install
# Exécutez le programme
npm run start
# Vérifiez son fonctionnement en lançant dans votre navigateur la commande
http://localhost:4200/
Notre prototype contient la partie graphique finale.
Pour avoir plus de détails vous pouvez consulter le tutoriel sur Angular et Bootstrap
Prototype avec Bootstrap et Angular
Prototype Bootstrap
Pour créer notre prototype nous utiliserons un exemple fourni par Bootstrap.
L'exemple est ici
https://getbootstrap.com/docs/5.2/forms/form-control/
Vous retrouverez cet exemple dans le répertoire UI de notre code source.
Il s'agit du fichier template-driven-forms.html.
Je vous donne ci-dessous les éléments spécifique à l'affichage du formulaire.
<main>
<div class="container-fluid">
<div class="row justify-content-center pt-2">
<div class="text-center col">
<h1 class="h3">Feature : Template-driven Forms</h1>
<hr>
</div>
</div>
<div class="row justify-content-center p-2">
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header text-center">
<h4>Movie : <span class="text-primary font-weight-bold">Avengers: Endgame</span></h4>
</div>
<div class="card-body">
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label for="inputEmail4">Name</label>
<input type="text" class="form-control" id="inputEmail4" value="Avengers: Endgame">
</div>
<div class="form-group col-md-6">
<label for="inputPassword4">Release Date</label>
<input type="text" class="form-control" id="inputPassword4" value="April 24, 2019">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="inputAddress">Domestic</label>
<input type="text" class="form-control" id="inputAddress" placeholder="" value="$858,373,000">
</div>
<div class="form-group col-md-4">
<label for="inputAddress">International</label>
<input type="text" class="form-control" id="inputAddress" placeholder="" value="$1,939,427,564">
</div>
<div class="form-group col-md-4">
<label for="inputAddress2">Worldwide</label>
<input type="text" class="form-control" id="inputAddress2" placeholder="" value="$2,797,800,564">
</div>
</div>
<div class="form-row justify-content-center">
<button type="submit" class="btn btn-primary mr-4">Create</button>
<button type="submit" class="btn btn-primary mr-4">Update</button>
<button type="submit" class="btn btn-primary">Delete</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
Nouvelles fonctionnalités
Nous allons rajouter une page dédiée au Template Driven Form dans notre projet.
C'est dans cette page que nous ferons tous nos tests et modifications.
Tout d'abord créons la nouvelle page avec les commandes Angular CLI correspondantes.
# Création de la page dédiée au Template Driven Forms
ng generate component modules/application/template-driven-forms --module=app
ng generate module modules/application/template-driven-forms --routing --module=app
{
path: 'template-driven-forms',
loadChildren: () => import('./modules/application/template-driven-forms/template-driven-forms.module')
.then(mod => mod.TemplateDrivenFormsModule)
},
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { TemplateDrivenFormsComponent } from './template-driven-forms.component';
const routes: Routes = [
{ path: '', component: TemplateDrivenFormsComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TemplateDrivenFormsRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TemplateDrivenFormsComponent } from './template-driven-forms.component';
import { TemplateDrivenFormsRoutingModule } from './template-driven-forms-routing.module';
@NgModule({
imports: [
CommonModule,
TemplateDrivenFormsRoutingModule
],
exports: [
TemplateDrivenFormsComponent
],
declarations: [
TemplateDrivenFormsComponent
],
providers: [
],
})
export class TemplateDrivenFormsModule { }
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NotFoundComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
{
name: 'Template Driven Forms',
description: 'Build an Angular form with a component and template',
icon: 'far fa-file-alt',
link: 'template-driven-forms'
},
Integration dans notre projet
FormsModule et name sur un input sont les deux éléments à ne pas oublier.
Nous allons utiliser la directive ngModel.
Pour cela importons FormsModule dans la liste des modules.
- template-driven-forms.module.ts
Ensuite copions le prototype bootstrap dans notre projet.
Nous allons intégrer la partie graphique.
Le CSS et le HTML seront indiqués dans les fichiers suivants.
- template-driven-forms.component.css
- template-driven-forms.component.html
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TemplateDrivenFormsComponent } from './template-driven-forms.component';
import { TemplateDrivenFormsRoutingModule } from './template-driven-forms-routing.module';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
CommonModule,
TemplateDrivenFormsRoutingModule,
FormsModule
],
exports: [
TemplateDrivenFormsComponent
],
declarations: [
TemplateDrivenFormsComponent
],
providers: [
],
})
export class TemplateDrivenFormsModule { }
.card {
display: block;
background-color: rgba(255, 255, 255, .8);
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);
border-radius: 2px;
transition: all .2s ease-in-out;
cursor: pointer;
}
.card:hover {
box-shadow: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);
}
<div class="row justify-content-center pt-2">
<div class="text-center col">
<h1 class="h3">Feature : Template-driven Forms</h1>
<hr>
</div>
</div>
<div class="row justify-content-center p-2">
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header text-center">
<h4>Movie : <span class="text-primary font-weight-bold">Avengers: Endgame</span></h4>
</div>
<div class="card-body">
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" value="Avengers: Endgame">
</div>
<div class="form-group col-md-6">
<label for="releaseDate">Release Date</label>
<input type="text" class="form-control" id="releaseDate" value="April 24, 2019">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="domestic">Domestic</label>
<input type="text" class="form-control" id="domestic" placeholder="" value="$858,373,000">
</div>
<div class="form-group col-md-4">
<label for="international">International</label>
<input type="text" class="form-control" id="international" placeholder="" value="$1,939,427,564">
</div>
<div class="form-group col-md-4">
<label for="worldwide">Worldwide</label>
<input type="text" class="form-control" id="worldwide" placeholder="" value="$2,797,800,564">
</div>
</div>
<div class="form-row justify-content-center">
<button type="submit" class="btn btn-primary mr-4">Create</button>
<button type="submit" class="btn btn-primary mr-4">Update</button>
<button type="submit" class="btn btn-primary">Delete</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row justify-content-center pt-2">
<div class="text-center col">
<h1 class="h3">Feature : Template-driven Forms</h1>
<hr>
</div>
</div>
<div class="row justify-content-center p-2">
<div class="col-12 col-sm-12 col-md-6 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header text-center">
<h4>Movie : <span class="text-primary font-weight-bold">Avengers: Endgame</span></h4>
</div>
<div class="card-body">
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" value="Avengers: Endgame">
</div>
<div class="form-group col-md-6">
<label for="releaseDate">Release Date</label>
<input type="text" class="form-control" id="releaseDate" value="April 24, 2019">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="domestic">Domestic</label>
<input type="text" class="form-control" id="domestic" placeholder="" value="$858,373,000">
</div>
<div class="form-group col-md-4">
<label for="international">International</label>
<input type="text" class="form-control" id="international" placeholder="" value="$1,939,427,564">
</div>
<div class="form-group col-md-4">
<label for="worldwide">Worldwide</label>
<input type="text" class="form-control" id="worldwide" placeholder="" value="$2,797,800,564">
</div>
</div>
<div class="form-row justify-content-center">
<button type="submit" class="btn btn-primary mr-4">Create</button>
<button type="submit" class="btn btn-primary mr-4">Update</button>
<button type="submit" class="btn btn-primary">Delete</button>
</div>
</form>
</div>
</div>
</div>
</div>
Développer et expérimenter
Nous pouvons dès lors effectuer une série d'essais et de tests pour comprendre comment fonctionne les forms.
Commençons par créer une classe Simple (nommée Movie) et modifions la en fonction des données souhaitées.
# Création de la classe Movie dans le répertoire template-driven-forms
ng generate class modules/application/template-driven-forms/Movie
export class Movie {
name: string;
releaseDate: string;
domestic: string;
international: string;
worldwide: string;
}
La technique que nous allons utiliser est celle du binding.
Elle consiste à lier le formulaire au modèle.
Le modèle sera notre classe nous le déclarerons au niveau du fichier TypeScript.
Le formulaire sera contenu dans le fichier HTML.
Pour relier par exemple le champ name (input) nous utiliserons au choix deux types de syntaxe.
- One way binding
Les données vont du modèle vers le formulaire
[ngModel]
- Two way binding
Les données vont du modèle vers le formulaire et du formulaire vers le modèle
[(ngModel)]
Remarque
si la directive ngModel est utilisée il faut obligatoirement indiquer l'attribut name.
Rajoutons enfin la syntaxe {{ movie | json }}
Pour vérifier le flux de données entre le modèle et les input.
Ce qui donnera dans nos fichiers le code suivant.
import { Component, OnInit } from '@angular/core';
import { Movie } from './movie';
@Component({
selector: 'app-template-driven-forms',
templateUrl: './template-driven-forms.component.html',
styleUrls: ['./template-driven-forms.component.css']
})
export class TemplateDrivenFormsComponent implements OnInit {
movie: Movie = new Movie();
constructor() {
this.movie.name = 'Avengers : Endgame';
this.movie.releaseDate = '04/04/2019';
this.movie.domestic = '$858,373,000';
this.movie.international = '$2,797,800,564';
this.movie.worldwide = '$2,797,800,564';
}
ngOnInit() {
}
}
<div class="row justify-content-center pt-2">
<div class="text-center col">
<h1 class="h3">Feature : Template-driven Forms</h1>
<hr>
</div>
</div>
<div class="row justify-content-center p-2">
<div class="col-12 col-sm-12 col-md-4 col-lg-4 col-xl-4">
{{ movie | json }}
</div>
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-8">
<div class="card">
<div class="card-header text-center">
<h4>Movie : <span class="text-primary font-weight-bold">Avengers: Endgame</span></h4>
</div>
<div class="card-body">
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label for="name">Name</label>
<input type="text" class="form-control" name="name" id="name" [ngModel]="movie.name">
</div>
<div class="form-group col-md-6">
<label for="release">Release Date</label>
<input type="text" class="form-control" name="release" id="release" [ngModel]="movie.releaseDate">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="domestic">Domestic</label>
<input type="text" class="form-control" name="domestic" id="domestic" [(ngModel)]="movie.domestic">
</div>
<div class="form-group col-md-4">
<label for="international">International</label>
<input type="text" class="form-control" name="international" id="international" [(ngModel)]="movie.international">
</div>
<div class="form-group col-md-4">
<label for="worldwide">Worldwide</label>
<input type="text" class="form-control" name="worldwide" id="worldwide" [(ngModel)]="movie.worldwide">
</div>
</div>
<div class="form-row justify-content-center">
<button type="submit" class="btn btn-primary mr-4">Create</button>
<button type="submit" class="btn btn-primary mr-4">Update</button>
<button type="submit" class="btn btn-primary">Delete</button>
</div>
</form>
</div>
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TemplateDrivenFormsComponent } from './template-driven-forms.component';
import { FormsModule } from '@angular/forms';
describe('TemplateDrivenFormsComponent', () => {
let component: TemplateDrivenFormsComponent;
let fixture: ComponentFixture<TemplateDrivenFormsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
],
declarations: [ TemplateDrivenFormsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TemplateDrivenFormsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
ngForm
En HTML un sélecteur ID (ID selector) est un nom précédé du caractère hash (#).
ngForm est une directive angular qui va nous permettre de visualiser les données du formulaire.
L'accès à chaque input va se faire via le DOM directement et les sélecteurs d'id.
Par exemple sur le input dédié au champ name on rajoute dans le DOM
#name
Nous pourrons dès lors accèder aux input directement dans le code HTML.
Tests
Il ne reste plus qu'à tester les différents scripts Angular.
# Développement
npm run start
http://localhost:4200/
# Tests
npm run lint
npm run test
# Compilation
npm run build
Code source
Le code source utilisé en début de tutoriel est disponible sur github
https://github.com/ganatan/angular-react-bootstrap
Les étapes suivantes vous permettront d'obtenir une application prototype.
- Etape 5 : Modules avec Angular
- Etape 6 : Search Engine Optimization avec Angular
- Etape 7 : Progressive Web App avec Angular
- Etape 8 : Server Side Rendering avec angular
- Etape 9 : HttpClient avec Angular
- Etape 10 : Transfer State avec Angular
Les étapes suivantes vous permettront d'améliorer ce prototype
- Components avec Angular
- Services avec Angular
- Template Driven Forms avec Angular
- Charts avec Angular
Cette dernière étape permet d'obtenir un exemple d'application
Le code source de cette application finale est disponible sur GitHub
https://github.com/ganatan/angular-app