Tutoriel : Créé un formulaire progressif avec Angular et TailwindCSS
Tailwind CSS est un framework CSS utilitaire qui permet de concevoir rapidement des interfaces web en utilisant des classes prédéfinies pour styliser les éléments HTML. Dans ce tutoriel, on vas crée un formulaire progressif avec validation avancée en utilisant Angular et Tailwind CSS. Assurez-vous d’installer Angular et Tailwind CSS avant de commencer.
Étape 1 : Créez un projet Angular
Si vous n’avez pas déjà un projet Angular, vous pouvez en créer un à l’aide d’Angular CLI en exécutant la commande suivante :
ng new formulaire-progressif
Étape 2 : Créez les composants Angular
Créez trois composants Angular pour chaque section du formulaire : InformationPersonnel, InformationProfessionnel, et Resume. Vous pouvez utiliser la commande suivante pour générer les composants :
ng generate component InformationPersonnel
ng generate component InformationProfessionnel
ng generate component Resume
Étape 3 : Créez un service Angular pour stocker les données
Créez un service Angular qui stockera les données du formulaire à mesure qu’elles sont saisies. Vous pouvez utiliser la commande suivante pour générer un service :
ng generate service formData
Étape 4 : Installation et configuration de TailwindCSS
Pour installer et configurer Tailwind CSS dans votre projet Angular, suivez ces étapes
- Installation de Tailwind CSS : Assurez-vous d’être dans le répertoire de votre projet Angular et tapez la commande suivantes :
npm install tailwindcss
- Configuration de Tailwind CSS : Créez un fichier de configuration Tailwind CSS en utilisant la commande suivante :
npx tailwindcss init
Cela créera un fichier tailwind.config.js
on dans la racine de votre projet. Vous pouvez personnaliser ce fichier pour définir vos propres couleurs, polices, classes personnalisées, etc. Ensuite, modifier le fichier tailwind.config.json en ajoutant le code suivant :
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
Étape 5 : Créez le formulaire progressif
Dans chaque composant, créez un formulaire HTML en utilisant Angular Forms (Reactive Forms) pour collecter les informations personnelles, professionnelles et le résumé. Utilisez Tailwind CSS pour styliser le formulaire.
Composant InformationPersonnel : information-personnel.component.html
<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" [formGroup]="form" (ngSubmit)="onNextStep()">
<h1 class="text-center font-bold text-2xl">Informations Personnel</h1>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Nom</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="nom"
required
(input)="validateField('nom')"
[ngClass]="{ 'border-red-500': isFieldInvalid('nom') }"
>
<div *ngIf="isFieldInvalid('nom')" class="text-red-500">Nom invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Prénom</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="prenom"
required
(input)="validateField('prenom')"
[ngClass]="{ 'border-red-500': isFieldInvalid('prenom') }"
>
<div *ngIf="isFieldInvalid('prenom')" class="text-red-500">Prénom invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Adresse Email</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="email"
required
(input)="validateField('email')"
[ngClass]="{ 'border-red-500': isFieldInvalid('email') }"
>
<div *ngIf="isFieldInvalid('email')" class="text-red-500">Email invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Num. Téléphone</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="tel"
required
(input)="validateField('tel')"
[ngClass]="{ 'border-red-500': isFieldInvalid('tel') }"
>
<div *ngIf="isFieldInvalid('tel')" class="text-red-500">Numero téléphone invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Genre</label>
<div class="mt-2">
<label class="inline-flex items-center">
<input
class="form-radio text-indigo-600"
type="radio"
name="genre"
value="homme"
>
<span class="ml-2">Homme</span>
</label>
<label class="inline-flex items-center ml-6">
<input
class="form-radio text-indigo-600"
type="radio"
name="genre"
value="femme"
>
<span class="ml-2">Femme</span>
</label>
</div>
</div>
</form>
Composant InformationProfessionnel : information-professionnel.component.html
<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" [formGroup]="form" (ngSubmit)="onNextStep()">
<h1 class="text-center font-bold text-2xl">Informations Professionnel</h1>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Formation</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="formation"
required
(input)="validateField('formation')"
[ngClass]="{ 'border-red-500': isFieldInvalid('formation') }"
>
<div *ngIf="isFieldInvalid('formation')" class="text-red-500">Formation invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Niveau</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="niveau"
required
(input)="validateField('niveau')"
[ngClass]="{ 'border-red-500': isFieldInvalid('niveau') }"
>
<div *ngIf="isFieldInvalid('niveau')" class="text-red-500">Niveau invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Matricule</label>
<input
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="mat"
required
(input)="validateField('mat')"
[ngClass]="{ 'border-red-500': isFieldInvalid('mat') }"
>
<div *ngIf="isFieldInvalid('mat')" class="text-red-500">Matricule invalide</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Date d'inscription</label>
<input
type="date"
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
formControlName="date"
required
(input)="validateField('date')"
[ngClass]="{ 'border-red-500': isFieldInvalid('date') }"
>
<div *ngIf="isFieldInvalid('date')" class="text-red-500">Date d'inscription invalide</div>
</div>
</form>
Composant Resume : resume.component.html
<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" [formGroup]="form" (ngSubmit)="onSubmitForm()">
<h1 class="text-center font-bold text-2xl">Description</h1>
<label class="block text-gray-700 text-sm font-bold mb-2">Description</label>
<textarea
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline h-80"
formControlName="resume"
required
(input)="validateField('resume')"
[ngClass]="{ 'border-red-500': isFieldInvalid('resume') }"
></textarea>
<div *ngIf="isFieldInvalid('resume')" class="text-red-500">Description invalide</div>
</form>
Composant InformationPersonnel : information-personnel.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-information-personnel',
templateUrl: './information-personnel.component.html',
styleUrls: ['./information-personnel.component.css'],
})
export class InformationPersonnelComponent {
@Output() nextStep = new EventEmitter<void>();
form = this.fb.group({
nom: ['', [Validators.required, Validators.pattern(/^[A-Za-z]+$/)]],
prenom: ['', [Validators.required, Validators.pattern(/^[A-Za-z]+$/)]],
email: ['', [Validators.required, Validators.email,]],
tel: ['', [Validators.required, Validators.minLength(9)]],
});
constructor(private fb: FormBuilder) {}
onNextStep() {
if (this.form.valid) {
this.nextStep.emit();
}
}
validateField(field: string) {
const control = this.form.get(field);
if (control && control.dirty) {
control.updateValueAndValidity();
}
}
isFieldInvalid(field: string) {
const control = this.form.get(field);
return control && control.invalid && (control.dirty || control.touched);
}
}
Composant InformationProfessionnel : information-professionnel.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-information-professionnel',
templateUrl: './information-professionnel.component.html',
styleUrls: ['./information-professionnel.component.css'],
})
export class InformationProfessionnelComponent {
@Output() nextStep = new EventEmitter<void>();
form = this.fb.group({
formation: ['', [Validators.required]],
niveau: ['', [Validators.required]],
mat: ['', [Validators.required]],
date: ['', [Validators.required]],
});
constructor(private fb: FormBuilder) {}
validateField(field: string) {
const control = this.form.get(field);
if (control && control.dirty) {
control.updateValueAndValidity();
}
}
isFieldInvalid(field: string) {
const control = this.form.get(field);
return control && control.invalid && (control.dirty || control.touched);
}
onNextStep() {
if (this.form.valid) {
this.nextStep.emit();
}
}
}
Composant Resume : resume.component.ts
import { Component, EventEmitter, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-resume',
templateUrl: './resume.component.html',
styleUrls: ['./resume.component.css'],
})
export class ResumeComponent {
@Output() submitForm = new EventEmitter<void>();
form = this.fb.group({
resume: ['', [Validators.required]],
});
constructor(private fb: FormBuilder) {}
validateField(field: string) {
const control = this.form.get(field);
if (control && control.dirty) {
control.updateValueAndValidity();
}
}
isFieldInvalid(field: string) {
const control = this.form.get(field);
return control && control.invalid && (control.dirty || control.touched);
}
onSubmitForm() {
if (this.form.valid) {
this.submitForm.emit();
}
}
}
Le composant global AppComponent
qui englobe les étapes du formulaire progressif. Ce composant affiche dynamiquement les composants enfants en fonction de l’étape actuelle du formulaire :
Composant Global : formulaire-progressif.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
})
export class AppComponent {
currentStep = 'information-personnel';
nextStep() {
if (this.currentStep === 'information-personnel') {
this.currentStep = 'information-professionnel';
} else if (this.currentStep === 'information-professionnel') {
this.currentStep = 'resume';
}
// Vous pouvez ajouter davantage de logique de navigation ici
}
prevStep() {
if (this.currentStep === 'information-professionnel') {
this.currentStep = 'information-personnel';
} else if (this.currentStep === 'resume') {
this.currentStep = 'information-professionnel';
}
// Vous pouvez ajouter davantage de logique de navigation ici
}
submitForm() {
// Soumettez les données finales ou effectuez d'autres actions
}
}
Composant Global : formulaire-progressif.component.html
<!-- formulaire-progressif.component.html -->
<div class="container mx-auto p-4">
<h2 class="text-2xl font-bold mb-4">Formulaire Progressif</h2>
<!-- Affichez le composant en fonction de l'étape actuelle -->
<ng-container [ngSwitch]="currentStep">
<app-information-personnel
*ngSwitchCase="'information-personnel'"
(nextStep)="nextStep()"
></app-information-personnel>
<app-information-professionnel
*ngSwitchCase="'information-professionnel'"
(nextStep)="nextStep()"
></app-information-professionnel>
<app-resume *ngSwitchCase="'resume'" (submitForm)="submitForm()"></app-resume>
</ng-container>
<!-- Boutons de navigation -->
<div class="mt-4">
<button
(click)="prevStep()"
[disabled]="currentStep === 'information-personnel'"
class="bg-gray-300 hover:bg-gray-400 text-gray-700 font-bold py-2 px-4 rounded-l"
>
Précédent
</button>
<button
(click)="nextStep()"
[disabled]="currentStep === 'resume'"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r"
>
Suivant
</button>
</div>
</div>
Ce code HTML utilise la directive ngSwitch
pour afficher dynamiquement le composant correspondant à l’étape actuelle (currentStep
). Les composants enfants, tels que app-information-personnel
, app-information-professionnel
, et app-resume
, émettent des événements pour gérer la navigation entre les étapes. Les boutons « Précédent » et « Suivant » sont désactivés en fonction de l’étape actuelle pour empêcher la navigation incorrecte.
Étape 6 : Tester votre application
ng serve --start
Ce tutoriel fournit une base pour créer un formulaire progressif avec validation avancée en utilisant Angular et Tailwind CSS. Vous pouvez personnaliser davantage votre formulaire en fonction de vos besoins spécifiques et ajouter des fonctionnalités telles que la gestion des erreurs, la confirmation, etc…
769 commentaires