Gestion des leçons et du contenu dynamique

Par dbeulze, 21 février, 2025

Dans cette partie je vous présente comment gérer dynamiquement des leçons à partir de fichiers YAML dans une application Angular. Vous apprendrez à récupérer, convertir et stocker des questions, ainsi qu'à intégrer ces fonctionnalités dans votre application.


Récupération et Conversion des fichiers YAML en JSON

Définir l'entité Question

Nous commençons par créer une entité qui définit la structure d'une question :

export class Question {
  type: string = "";
  titre: string = "";
  niveau: string = "";
  objectif: string = "";
  énoncé: string = "";
  ébauches: { [lang: string]: string } = {};
  rétroactions: { positive: string, négative: string } = { positive: "", négative: "" };
  tests: string = "";
  auteur: string = "";
  licence: string = "";
}

Création du Service YAML

Générer le service

Exécutez la commande suivante pour créer un service nommé yaml.service :

ng g service services/yaml

Une fois généré, le fichier yaml.service.ts ressemble à ceci :

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class YamlService {
  constructor() { }
}

Ajouter les fonctions de lecture et de conversion

Installez la bibliothèque js-yaml et ajoutez le code suivant dans votre service :

constructor(private http: HttpClient) { }

readYAML(url: string): Observable<string> {
  return this.http.get(url, { responseType: 'text' });
}

convertYAMLToQuestion(yaml: string, url: string): Question {
  const includeType = new jsyaml.Type('!include', {
    kind: 'scalar',
    resolve: (data) => typeof data === 'string',
    construct: (data) => {
      return url.replace("info.yml", "") + data;
    }
  });

  const CUSTOM_SCHEMA = jsyaml.DEFAULT_SCHEMA.extend([includeType]);
  const parsedData = jsyaml.load(yaml, { schema: CUSTOM_SCHEMA });
  return parsedData as Question;
}

Ces fonctions permettent de lire un fichier YAML et de le convertir en JSON, en gérant notamment les inclusions pour ajouter le chemin d'accès aux fichiers associés.


Service de Stockage

Générer le service de stockage

Exécutez la commande suivante :

ng g service services/stockage

Nous utiliserons la bibliothèque angular-async-local-storage.

Ajouter les méthodes de stockage

Sauvegarder une question

sauvegarderQuestion(question: Question) {
  let dejaPresent = false;
  this.storage.get('questions').subscribe((value: unknown) => {
    let questions: Question[] = [];
    if (value && Array.isArray(value)) {
      questions = value as Question[];
    }

    questions.forEach(val => {
      if (val.titre === question.titre) {
        dejaPresent = true;
      }
    });
    if (!dejaPresent) {
      questions.push(question);
      this.storage.set('questions', questions).subscribe(() => {
        console.log("Question ajoutée !");
      });
    }
  });
}

Supprimer toutes les questions

toutSupprimer() {
  this.storage.clear().subscribe(() => {});
}

Observer les questions sauvegardées

observerQuestions(): Observable<Question[]> {
  return this.storage.watch('questions').pipe(
    map((value: unknown) => {
      return (Array.isArray(value) ? value : []) as Question[];
    })
  );
}

Ce service permet de sauvegarder, supprimer et observer dynamiquement les questions.


Intégration dans l'Application Angular

Modification de app.component.html

Mettez à jour votre fichier app.component.html afin d'afficher les leçons et de permettre l'ajout d'un exercice :

<main class="main">
  <div class="h-100 w-100 d-flex flex-column gap-3 p-2 overflow-auto">
    <app-carte-lecon 
      *ngFor="let exercice of exercices" 
      [title]="exercice.titre" 
      [description]="exercice.objectif" 
      [niveau]="exercice.niveau">
    </app-carte-lecon>
    <app-carte-lecon 
      (ajoutExercise)="chargerExercise()" 
      [add]="true">
    </app-carte-lecon>
  </div>
</main>

Modification de app.component.ts

Injectez le StockageService dans le constructeur et ajoutez la méthode pour charger les questions :

constructor(private gestionStockage: StockageService) { }

ngOnInit(): void {
  this.chargerExercise();
}

chargerExercise() {
  this.gestionStockage.observerQuestions().subscribe((questions: Question[]) => {
    this.exercices = questions;
  });
}

Cette configuration permet de charger dynamiquement toutes les questions stockées dans votre application.


Mise à jour du Composant carte-lecon

Injection des Services

Dans le composant carte-lecon, injectez les services de stockage et YAML dans le constructeur :

constructor(
  private gestionStockage: StockageService, 
  private yamlService: YamlService
) { }

Sauvegarde d'une Question

Ajoutez la méthode suivante pour lire le YAML depuis une URL et sauvegarder la question convertie :

sauvegarderQuestion() {
  this.yamlService.readYAML(this.url).subscribe(value => {
    this.gestionStockage.sauvegarderQuestion(
      this.yamlService.convertYAMLToQuestion(value, this.url)
    );
  });
}

Template HTML du Composant

Modifiez le template HTML pour gérer l'affichage en mode ajout :

<div [style.background]="color" class="card-custom container d-flex justify-content-between w-100 gap-3">
  <div *ngIf="add" class="d-flex flex-column align-items-center justify-content-center w-100 gap-3">
    <p>Ajouter un exercice</p>
    <input [(ngModel)]="url" placeholder="URL de l'exercice" />
    <app-bouton 
      [colorText]="color" 
      (onClick)="sauvegarderQuestion()" 
      title="Ajouter">
    </app-bouton>
  </div>
  <div *ngIf="!add" class="row d-flex flex-column h-100 justify-content-between gap-4">
    <div class="d-flex flex-column gap-2">
      <h4 *ngIf="title.length > 0">
        {{ title }}
      </h4>
      <p *ngIf="description.length > 0" class="description">
        {{ description }}
      </p>
    </div>
    <app-bouton 
      [colorText]="color" 
      title="Lancer">
    </app-bouton>
  </div>
  <div class="row d-flex flex-column h-100 justify-content-between">
    <app-badge *ngIf="niveau.length > 0" [title]="niveau"></app-badge>
  </div>
</div>

Ce template permet à l'utilisateur de saisir une URL contenant une question de progression et de l'ajouter à l'application.


Vous disposez désormais d'une application Angular capable d'ajouter dynamiquement des exercices.

On se retrouve semaine prochaine pour un nouveau blog !


Références

  1. Repo NODECA js-yaml (Page consulté le 19 février 2025)

  2. Repo CYRILLETUZI, angular-async-local-storage (Page consulté le 19 février 2025)

  3. Documentation Angular, Angular (Page consulté le 19 février 2025)

Étiquettes

Commentaires