Assembler l’application et ses interfaces
Objectif : Relier les modules de données, de rendu et de démarrage au moyen d’interfaces explicites.
Une application peut être répartie en fichiers et rester difficile à faire évoluer si chacun attend des autres des fonctions ou des données implicites. Dans cette leçon, vous assemblez le gestionnaire de tâches autour d’un point d’entrée qui coordonne les modules sans absorber leurs responsabilités.
Vous avez déjà défini les responsabilités des fichiers et la direction de leurs dépendances. Vous allez maintenant rendre leurs échanges concrets : le démarrage restaure l’état, transmet les actions au réducteur, puis demande au rendu, à la persistance et aux adaptateurs de traiter le nouvel état.
Un point d’entrée qui orchestre
Le contrat retenu ici est simple : etat.js fournit l’état initial ; reducteur.js expose reduireEtat ; persistance.js restaure et enregistre les tâches ; dom.js traduit les événements en actions ; rendu.js affiche un état. Les modules de progression, de copie et de statistiques restent des services appelés par le démarrage ou par les adaptateurs concernés. Les noms exportés ci-dessous rendent ces interfaces visibles ; si vos fichiers possèdent déjà ces fonctions sous d’autres noms, conservez leurs noms établis et adaptez uniquement les imports correspondants.
Le point d’entrée de l’application
Voici un main.js complet pour cette composition. Il restaure les tâches, crée une fonction de traitement des actions, puis raccorde les adaptateurs ; chaque mise à jour suit le même chemin, ce qui rend l’orchestration facile à examiner.
import { creerEtatInitial } from "./etat.js";
import { reduireEtat } from "./reducteur.js";
import { chargerTaches, enregistrerTaches } from "./persistance.js";
import { connecterDOM } from "./dom.js";
import { afficherEtat } from "./rendu.js";
import { observerProgression } from "./progression.js";
import { copierTexte } from "./clipboard.js";
import { calculerStatistiques } from "./statistiques.js";
let etat = creerEtatInitial();
const tachesChargees = chargerTaches();
if (Array.isArray(tachesChargees)) {
etat = { ...etat, taches: tachesChargees };
}
function actualiserInterface() {
afficherEtat(etat);
observerProgression(etat.taches, calculerStatistiques);
}
function traiterAction(action) {
etat = reduireEtat(etat, action);
enregistrerTaches(etat.taches);
actualiserInterface();
}
function traiterCopie() {
import("./rapport.js")
.then(({ creerRapport }) => copierTexte(creerRapport(etat.taches)))
.catch(() => {
const message = document.querySelector("[data-message]");
if (message) message.textContent = "Le résumé n’a pas pu être préparé.";
});
}
actualiserInterface();
connecterDOM(traiterAction, traiterCopie);Le gestionnaire ne modifie l’état qu’en appelant reduireEtat. Après cette transition, il persiste la nouvelle liste et déclenche le rendu ; il ne construit pas lui-même les éléments HTML et ne lit pas directement les événements de formulaire. Le calcul des statistiques et l’observation de la progression sont transmis au module prévu à cet effet.
Vérifier les points d’ancrage du HTML
Les adaptateurs s’appuient sur les attributs data-* du HTML, et non sur les classes visuelles. Voici la page complète correspondant à l’assemblage ; elle conserve le squelette de référence et charge le point d’entrée comme module.
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#f4f6f8">
<title>Gestionnaire de tâches</title>
<link rel="stylesheet" href="style.css">
<script type="module" src="js/main.js"></script>
</head>
<body>
<main class="taches" aria-labelledby="titre-taches">
<header class="taches__entete">
<h1 id="titre-taches">Gestionnaire de tâches</h1>
<p>Organisez vos tâches et suivez votre progression.</p>
</header>
<section class="taches__progression" aria-labelledby="titre-progression" data-progression>
<h2 id="titre-progression">Progression</h2>
<p><span data-terminees>0</span> tâche(s) terminée(s) sur <span data-total>0</span></p>
<progress class="taches__barre" data-barre value="0" max="0" aria-label="Progression des tâches">0%</progress>
</section>
<form class="taches__formulaire" data-formulaire>
<label for="nouvelle-tache">Nouvelle tâche</label>
<input class="taches__champ" id="nouvelle-tache" name="tache" type="text" required maxlength="200" autocomplete="off">
<button type="submit">Ajouter</button>
</form>
<section aria-labelledby="titre-liste">
<h2 id="titre-liste">Mes tâches</h2>
<ul class="taches__liste" data-liste aria-live="polite"></ul>
</section>
<footer class="taches__commandes">
<button class="taches__copier" type="button" data-copier>Copier le résumé</button>
</footer>
<p class="taches__message" data-message role="status" aria-live="polite"></p>
</main>
</body>
</html>Les interfaces d’import constituent des contrats : chargerTaches renvoie une liste validée ou une valeur absente, enregistrerTaches reçoit la liste courante, et connecterDOM reçoit deux fonctions de rappel. Vérifiez ces noms et ces formes de données dans vos fichiers avant de lancer la page ; un import incorrect est détecté au chargement du module, avant même une interaction utilisateur.
Faire circuler le rapport jusqu’à Clipboard
Le rapport reste chargé uniquement lors d’une demande de copie, comme dans la leçon précédente. Dans cette composition, creerRapport transforme la liste en texte, puis copierTexte délègue la copie au module Clipboard ; l’échec de préparation affiche un message sans bloquer le reste de l’interface.
À retenir
- Le point d’entrée restaure l’état et coordonne les modules au moyen d’interfaces explicites.
- Toute action passe par le réducteur, puis déclenche persistance et actualisation de l’interface.
- Les attributs `data-*` relient les adaptateurs au HTML ; les classes restent le contrat visuel.
- Le rapport est importé à la demande et confié au module de copie.
La prochaine leçon vérifiera la restauration et les interactions dans un scénario complet, notamment après rechargement et face aux erreurs des API du navigateur.