Tutoriel Angular / Spring / Java - partie 2
Tutoriel Angular / Spring / Java - partie 2
Dans l'article précédent nous avons vu comment créer un squelette d'application de Kanban basique avec Angular.
Dans cet article nous allons rendre ce kaban utile en permettant de changer l'état des tâches (A faire, en cours et Fini).
Par ailleurs nous allons créer des tests unitaires pour un service.
Drag And Drop
Commençons par ajouter la fontion de drag and drop pour permettre la modification des tâches (TODO vers DOING, etc). Pour ceci nous allons utiliser Angular Material (cf https://material.angular.io/cdk) :
yarn add @angular/material @angular/cdk @angular/animations
Ajouter les modules BrowserAnimationsModule et DragDropModule dans app.mosule.ts :
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {DragDropModule} from '@angular/cdk/drag-drop';
@NgModule({
declarations: [
AppComponent,
CarteComponent,
DetailCarteComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
DragDropModule
Remplacer le code du detail-carte.component.html par celui du drag and drop :
<div
cdkDropList
id={{nom}}
[cdkDropListData]="liste"
class="tache"
(cdkDropListDropped)="drop($event)">
<div *ngFor="let tache of liste" cdkDrag class="tache">
<h2>
{{ tache.nom }}
</h2>
</div>
</div>
Ici on ajoute également un "nom" au composant pour permettre au drag-drop Angular de s'y retrouver entre les trois composants todo, doing et done lors du drop.
Ensuite modifier detail-carte.component.ts pour ajouter l'opération de drag and drop ainsi que le nom du composant :
import { Input, Component, OnInit } from '@angular/core';
import { Tache } from '../tache';
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-detail-carte',
templateUrl: './detail-carte.component.html',
styleUrls: ['./detail-carte.component.css']
})
export class DetailCarteComponent implements OnInit {
@Input() nom: string;
@Input() liste: Tache[];
constructor() { }
ngOnInit() {
}
drop(event: CdkDragDrop<Tache[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
// Change just l'item de place; pas la peine d'appeler le service
} else {
transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);
// Change l'item de liste; appel du service pour màj le kanban
}
}
}
Et modifier carte.component.html pour passer le nom en paramètre Input de la carte :
<h1 class="titre">{{carte.titre}}</h1>
<app-detail-carte [nom]="carte.titre" [liste]="carte.taches"></app-detail-carte>
Pour permettre aux éléments drag-drop répartis entre les composants détail de communiquer entre eux, ajouter l'attribut cdkDropListGroup dans app.component.html :
<div class="flex-container" cdkDropListGroup>
<div>
<app-carte [carte]="TODO"></app-carte>
</div>
Utiliser les Observables
La version actuelle du service renvoie la liste des tâches immédiatement et le code récupère ces infos de manière totalement synchrone (récupération, traitement, affichage).
Mais lors de l'appel au service il en ira différemment : le service sera appelé, puis le serveur traitera la demande, et renverra la réponse dans un laps de temps qui n'est pas négligeable. Or le navigateur lui ne bloque pas l'utilisateur le temps que la réponse arrive.
Par conséquent il faut traiter les choses de manière asynchrone et ne préparer les données que lorsque la réponse est arrivée.
Pour ce faire, modifier le service en utilisant Observable au lieu de renvoyer les données directement :
import { Injectable } from '@angular/core';
import { Carte } from './carte';
import { cartes } from './mock.cartes';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class KanbanService {
cartes: Carte[] = cartes;
constructor() { }
getCartes(): Observable<Carte[]> {
return of(this.cartes);
}
}
Modifier la méthode getKanban en conséquence dans app.component.ts :
getKanban() {
this.kanbanService.getCartes().subscribe(cartes => {
this.cartes = cartes;
this.TODO = this.cartes.find(carte => carte.titre === "TODO");
this.DOING = this.cartes.find(carte => carte.titre === "DOING");
this.DONE = this.cartes.find(carte => carte.titre === "DONE");
});
}
Sauvegarde des modifications du kanban
Pour prendre en compte le déplacement d'une tâche nous allons ajouter une opération au service qui va simplement appeler le back avec les nouvelles infos du kanban.
Modifier kanban.service.ts et ajouter l'opération (pour l'instant la méthode ne fait rien d'autre qu'afficher un message) :
saveCartes(): void {
console.log("Sauvegarde des cartes.");
}
Appeler l'opération du service dans la fonction drop :
drop(event: CdkDragDrop<Tache[]>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
// Change juste l'item de place; pas la peine d'appeler le service
} else {
transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);
// Change l'item de liste; appel du service pour màj le kanban
this.kanbanService.saveCartes();
}
}
Voilà, à présent le kanban doit permettre de faire glisser des tâches d'une carte à l'autre.
Tests unitaires
Pour l'instant les seuls TU qui existent sont des coquilles vides. Mieux vaut les enrichir avec de vrais tests. Nous allons ajouter des tests pour le service, mais dans l'idéal il faut en créer pour chaque classe composant, etc.
Pour tester la récupération de la liste des infos du kanban, nous allons nous abonner au service comme dans la "vraie" méthode getKanban avec la fonction subscribe.
Modifier kanban.service.spec.ts pour ajouter la fonction de test :
it('devrait retourner la liste des cartes', () => {
const service: KanbanService = TestBed.get(KanbanService);
service.getCartes().subscribe(cartes => {
expect(cartes).toBeDefined();
expect(cartes.length).toEqual(3);
});
});
Ici on vérifie le résultat du retour du service avec expect pour :
- s'assurer qu'il n'est pas vide (toBeDefined)
- vérifier qu'on a bien 3 cartes (to, doing et done)
Ajouter ensuite la fonction de test pour la sauvegarde :
it('devrait sauvegarder la liste des cartes', () => {
const service: KanbanService = TestBed.get(KanbanService);
service.saveCartes().subscribe((retour) => {
expect(retour).toEqual("OK");
});
});
Comme le service retourne simplement OK pour l'instant, il suffit de tester que le retour dans la fonction de subscribe a bien cette valeur grâce à expect.
Lancer les tests
Nous allons utiliser Chrome pour exécuter les tester.
Note : Comme on ne s'intéresse qu'aux tests du service pour l'instant supprimer simplement les ficheirs .spec.ts des composants.
Sous Linux, il est nécessaire d'ajouter une variable d'environnement pour que Jasmine trouve la commande du navigateur Chrome (ou chromium) à lancer :
export CHROME_BIN=chromium
Ensuite pour exécuter les tests il suffit de taper la commande
yarn test
On doit obtenir le résultat suivant dans chrome :
KanbanService
should be created
devrait retourner la liste des cartes
devrait sauvegarder la liste des cartes
Commentaires
Enregistrer un commentaire