diff --git a/A-preparatifs.md b/A-preparatifs.md index 46caf8cda4c3e91004987891db1339a82bcb8892..e544cebea932cf68d0e00d2b2216b9082e0353cb 100644 --- a/A-preparatifs.md +++ b/A-preparatifs.md @@ -4,7 +4,7 @@ ## Sommaire <!-- omit in toc --> - [A.1. Récupération du projet](#a1-récupération-du-projet) -- [A.3. Lancement de l'application](#a3-lancement-de-lapplication) +- [A.2. Lancement de l'application](#a2-lancement-de-lapplication) ## A.1. Récupération du projet @@ -15,8 +15,7 @@ Il va vous servir de base pour ce nouveau TP. - soit en cliquant sur le bouton `"Créer une divergence"` (_`"Fork"` sur la version anglaise de gitlab_) - soit en vous rendant directement sur https://gitlab.univ-lille.fr/js/tp3/-/forks/new - Choisissez de placer le fork dans votre profil utilisateur et configurez le repo **en mode "privé"** - (**`Settings`** > **`Visibility, project features, permissions`** > **`Project visibility`**) + Choisissez de placer le fork dans votre profil utilisateur et configurez le repo **en mode "privé"** (**`Settings`** > **`Visibility, project features, permissions`** > **`Project visibility`**) 2. **Ajoutez-votre encadrant de TP en tant que "reporter" pour qu'il ait accès à votre code :** @@ -30,16 +29,16 @@ Il va vous servir de base pour ce nouveau TP. mkdir ~/tps-js git clone https://gitlab.univ-lille.fr/<votre-username>/tp3.git ~/tps-js/tp3 ``` - > _**NB :** Comme pour le TP1, ici je clone dans mon dossier `/home/thomas/tps-js/tp3`. **Si vous êtes sous windows faites attention au sens des slashs et au caractère `"~"`** qui représente le dossier de l'utilisateur sur système unix. Si vous êtes sous windows utilisez **Git bash** (qui comprend cette syntaxe) ou si vous tenez vraiment à utiliser **cmd** pensez à adapter !_ + > _**NB :** Comme pour le TP1, ici je clone dans mon dossier `/home/thomas/tps-js/tp3`. **Si vous êtes sous windows faites attention au sens des slashs et au caractère `"~"`** qui représente le dossier de l'utilisateur sur système unix : utilisez **Git bash** (qui comprend cette syntaxe) ou si vous tenez vraiment à utiliser **cmd** ou **powershell** pensez à adapter la commande !_ - > _**NB2 :** Comme pour le TP1 aussi, si vous préférez **cloner en SSH** pour ne pas avoir à taper votre mot de passe à chaque fois, renseignez votre clé SSH dans votre [compte utilisateur gitlab](https://gitlab.univ-lille.fr/profile/keys) et clonez à partir de cette URL : `git@gitlab-ssh.univ-lille.fr:js/tp3.git`_ + > _**NB2 :** Comme pour le TP1 aussi, si vous préférez **cloner en SSH** pour ne pas avoir à taper votre mot de passe à chaque fois que vous clonerez un TP, renseignez votre clé SSH dans votre [compte utilisateur gitlab](https://gitlab.univ-lille.fr/profile/keys) et clonez à partir de cette URL : `git@gitlab-ssh.univ-lille.fr:js/tp1.git`_ -4. **Ouvrez le projet dans VSCodium** (pour les différentes façon d'ouvrir le projet relisez les [instructions du TP1](https://gitlab.univ-lille.fr/js/tp1/-/blob/master/A-preparatifs.md#a3-ouvrir-le-projet-dans-vscodium) ) +4. **Ouvrez le projet dans VSCodium/VSCode** (pour les différentes façon d'ouvrir le projet relisez les [instructions du TP1](https://gitlab.univ-lille.fr/js/tp1/-/blob/master/A-preparatifs.md#a3-ouvrir-le-projet-dans-vscodium) ) ```bash codium ~/tps-js/tp3 ``` -5. **Installez les paquets npm nécessaires au projet** notamment le compilateur [Babel](https://babeljs.io).<br> +5. **Installez les paquets npm nécessaires au projet** (_notamment [Babel](https://babeljs.io) et [webpack](https://webpack.js.org/)_). \ Ouvrez un terminal intégré à VSCodium (<kbd>CTRL</kbd>+<kbd>J</kbd> *(PC)* / <kbd>CMD</kbd>+<kbd>J</kbd> *(Mac)*) et tapez juste : ```bash npm install @@ -49,7 +48,7 @@ Il va vous servir de base pour ce nouveau TP. > > **Magique !** 🙌 -## A.3. Lancement de l'application +## A.2. Lancement de l'application Comme dans le précédent TP lancez un serveur HTTP et la compilation du projet **dans deux terminaux côte à côte** ([terminaux splittés](https://code.visualstudio.com/docs/editor/integrated-terminal#_terminal-splitting)) : @@ -67,7 +66,7 @@ Comme dans le précédent TP lancez un serveur HTTP et la compilation du projet _que vous avez créé dans le précédent TP ([TP2 / A.5. Créer un script de build personnalisé](https://gitlab.univ-lille.fr/js/tp2/-/blob/master/A-preparatifs.md#a5-cr%C3%A9er-un-script-de-build-personnalis%C3%A9))_ _puis modifié pour [compiler avec webpack+babel](https://gitlab.univ-lille.fr/js/tp2/-/blob/master/C-modules.md#c3-rendre-les-modules-compatibles-avec-les-vieux-navigateurs)_ -3. **Vérifiez dans le navigateur que la page index.html s'affiche correctement** en ouvrant l'url http://localhost:8000. +3. **Vérifiez dans le navigateur que la page `index.html` s'affiche correctement** en ouvrant l'url http://localhost:8000. Le résultat attendu est le suivant : diff --git a/B-les-bases.md b/B-les-bases.md index 2375f0049799e49e111b031b412410d17bb0470c..3e53736aa9e39bc4a6b63b08893111c21ee1b1fb 100644 --- a/B-les-bases.md +++ b/B-les-bases.md @@ -18,7 +18,8 @@ _**Dans cette partie du TP, nous allons prendre en main les méthodes de base de ### B.1.1. querySelector() **Comme vu en cours, la principale méthode pour sélectionner un élément de la page HTML est la méthode [`querySelector()`](https://developer.mozilla.org/fr/docs/Web/API/Document/querySelector).** -`querySelector()` est une méthode de la classe `Element` qui retourne une référence vers le premier élément de la page (_une balise_) qui correspond au sélecteur CSS passé en paramètre. Par exemple : +`querySelector()` est une méthode de la classe `Element` qui retourne une référence vers le premier élément de la page (_une balise_) qui correspond au sélecteur CSS passé en paramètre. \ +Par exemple : ```js document.querySelector('.pageContainer'); ``` @@ -43,20 +44,20 @@ cette instruction vous retourne la balise qui a comme classe `"pizzaList"` <img src="images/readme/queryselector-console.png"> ### B.1.2. querySelectorAll() -**La méthode [`querySelectorAll()`](https://developer.mozilla.org/fr/docs/Web/API/Document/querySelectorAll) permet de récupérer non pas un, mais tous les éléments qui correspondent au sélecteur CSS passé en paramètre.** +**La méthode [`querySelectorAll()`](https://developer.mozilla.org/fr/docs/Web/API/Document/querySelectorAll) permet de récupérer non pas un, mais TOUS les éléments qui correspondent au sélecteur CSS passé en paramètre** (_sous la forme d'un tableau_). Affichez dans la console : -1. **la liste des liens du menu de navigation** ("La carte", "À propos" et "Ajouter une pizza") +1. **la liste des liens (`<a href>`) du menu de navigation** ("La carte", "À propos" et "Ajouter une pizza") cf. capture ci-dessous 2. **la liste des `<li>` contenant le prix de toutes les pizzas** <img src="images/readme/queryselectorall-console.png"> ## B.2. Modifier des éléments ### B.2.1. innerHTML -**La propriété [`innerHTML`](https://developer.mozilla.org/fr/docs/Web/API/Element/innertHTML) permet à la fois de lire et de modifier le contenu d'un Element HTML** (_tout ce qui est compris **entre** les balises ouvrantes et fermantes_) +**La propriété [`innerHTML`](https://developer.mozilla.org/fr/docs/Web/API/Element/innertHTML) permet à la fois de lire ET de modifier le contenu d'un Element HTML** (_tout ce qui est compris **entre** les balises ouvrantes et fermantes_) 1. Affichez dans la console le **titre de la deuxième pizza** (_la chaîne de caractères_ `"Napolitaine"`) -2. **Ajoutez au logo** (_grâce à la propriété `innerHTML`_) le code HTML suivant : +2. **Dans le fichier `main.js`, ajoutez au logo** (_grâce à la propriété `innerHTML`_) le code HTML suivant : ```html <small>les pizzas c'est la vie</small> ``` @@ -71,14 +72,24 @@ Affichez dans la console : <img src="images/readme/pizzaland-innerhtml.png"> + > _**Rappel :** innerHTML est accessible en écriture ET en lecture !_ + ### B.2.2. getAttribute/setAttribute **Les méthodes [`getAttribute()`](https://developer.mozilla.org/fr/docs/Web/API/Element/getAttribute) et [`setAttribute()`](https://developer.mozilla.org/fr/docs/Web/API/Element/setAttribute) de la classe `Element` permettent de lire, d'ajouter ou de modifier des attributs HTML.** +> _**Rappel :** Les **attributs** HTML, ce sont les paires `clé="valeur"` que l'on peut trouver dans les balises ouvrantes (comme `src`, `href`, etc.)._ + 1. Affichez dans la console **l'url du 2e lien contenu dans le footer** (`"https://www.iut-a.univ-lille.fr/"`) -2. **Ajoutez la classe CSS `"active"`** au premier lien du menu ("La carte") +2. Dans le fichier `main.js`, **ajoutez la classe CSS `"active"`** au premier lien du menu ("La carte") <img src="images/readme/pizzaland-setattribute.png"> + > _**NB :** pour associer plusieurs classes CSS à une seule balise, il suffit de les séparer par un espace à l'intérieur de l'attribut `class`. Pour cet exercice on souhaite donc obtenir le code HTML suivant :_ + > ```html + > <a href="/" class="pizzaListLink active"> + > ``` + > _(Notez l'espace entre "pizzaListLink" et "active")_ + ## Étape suivante <!-- omit in toc --> -Maintenant que l'on est capable de sélectionner / modifier des éléments HTML, nous allons voir dans le prochain exercice comment détecter les événements : [C. Les événements](./C-evenements.md). \ No newline at end of file +Maintenant que l'on est capable de sélectionner et modifier des éléments HTML, nous allons voir dans le prochain exercice comment détecter des événements : [C. Les événements](./C-evenements.md). \ No newline at end of file diff --git a/C-evenements.md b/C-evenements.md index 9f44448cfa7ecd503359e4ff36238b8f226476ed..bcffa21556c3ca24903c2a8b681aa787d067bfc7 100644 --- a/C-evenements.md +++ b/C-evenements.md @@ -66,9 +66,11 @@ link.addEventListener('click', handleClick); // écoute l'événement La technique de navigation vue à l'instant (_masquer/afficher des balises_) est pratique pour des applications simples, où tout le contenu HTML est déjà généré côté serveur (_en JAVA, en PHP, en C# ou encore avec Node.JS_). \ **En revanche elle n'est pas très adaptée aux SPA où en général on a du contenu dynamique à injecter dans la page.** -On va donc revenir à la principale technique de navigation employée dans les SPA, celle que l'on utilisait jusque là : **générer dynamiquement, en JS, le code HTML de la page en fonction de ce que demande l'utilisateur** (_comme on le faisait avec la `PizzaList` par exemple_). +On va donc revenir à la principale technique de navigation employée dans les SPA, celle que l'on utilisait jusque là : **générer dynamiquement (en JS) le code HTML de la page en fonction de ce que demande l'utilisateur** (_comme on le faisait avec la `PizzaList` par exemple_). -Pour approfondir cette technique de navigation et **permettre de passer d'une page à une autre**, je vous propose de nous appuyer sur la classe `Router` que vous avez développée lors du précédent TP ([TP2 / D.3. Propriétés et méthodes statiques : La classe Router](https://gitlab.univ-lille.fr/js/tp2/-/blob/master/D-poo-avancee.md#d3-propri%C3%A9t%C3%A9s-et-m%C3%A9thodes-statiques-la-classe-router)) et dont ma version se trouve dans ce repo (`src/Router.js`). +Pour approfondir cette technique de navigation et **permettre de passer d'une page à une autre**, je vous propose de nous appuyer sur la classe `Router` que vous avez développée lors du TP2 (_[D.3. Propriétés et méthodes statiques : La classe Router](https://gitlab.univ-lille.fr/js/tp2/-/blob/master/D-poo-avancee.md#d3-propri%C3%A9t%C3%A9s-et-m%C3%A9thodes-statiques-la-classe-router)_) et dont ma version se trouve dans ce repo (_vous pouvez la consulter dans [`src/Router.js`](./src/Router.js)_). + +Cette classe permet d'afficher un titre et un Component dans la page à l'aide de sa propriété statique `Router.routes` et de sa méthode statique `Router.navigate()`. **L'objectif de l'exercice ici est simple : faire en sorte que lorsque l'utilisateur clique sur un des liens du menu, on affiche un contenu différent dans la page grâce à la méthode `Router.navigate()`.** @@ -77,15 +79,15 @@ Pour approfondir cette technique de navigation et **permettre de passer d'une pa 1. **Dans `src/main.js`, commencez par créez des `Component` pour les différents liens du menu :** ```js - const pizzaList = new PizzaList([]), + const pizzaList = new PizzaList(data), aboutPage = new Component('section', null, 'Ce site est génial'), pizzaForm = new Component('section', null, 'Ici vous pourrez ajouter une pizza'); ``` - > _**NB1 :** `pizzaList` existe déjà, on ajoute ici juste `aboutPage` et `pizzaForm`._ + > _**NB1 :** `pizzaList` était déjà déclarée, effacez son ancienne déclaration et remplacez la par celle-ci. On ajoute par ailleurs `aboutPage` et `pizzaForm`._ > _**NB2 :** pour le moment on utilise pour ces 2 nouvelles pages des `Component` très simples, "en dur", mais on les passera dans des classes spécifiques plus tard._ - **Puis ajoutez les routes correspondantes dans notre Router :** + **Puis, toujours dans le `main.js`, ajoutez les routes correspondantes dans le Router :** ```js Router.routes = [ { path: '/', page: pizzaList, title: 'La carte' }, @@ -103,25 +105,30 @@ Pour approfondir cette technique de navigation et **permettre de passer d'une pa ```js Router.menuElement = document.querySelector('.mainMenu'); ``` - > _**NB :** en faisant cela on envoie au Router une **référence vers la balise `<ul class="mainMenu">`** qui contient le menu de navigation (cela nous évitera de faire référence à `document` dans la classe `Router` et en plus ça reste cohérent avec le fonctionnement des propriétés `titleElement` et `contentElement`)_ + > _**NB :** en faisant cela on stocke dans le Router une **référence vers la balise `<ul class="mainMenu">`** qui contient tout le menu de navigation (cela nous évitera d'avoir à faire référence à `document` dans la classe `Router` et en plus ça reste cohérent avec le fonctionnement des autres propriétés `Router.titleElement` et `Router.contentElement`)_ - `Router.menuElement` est en fait un "setter" dont je vous fourni le code de base à compléter (_à coller **dans** le corps de la classe `Router`_) : + `Router.menuElement` sera en fait **un "setter"** (_comme vu dans le [TP2 / D. POO avancée](https://gitlab.univ-lille.fr/js/tp2/-/blob/master/D-poo-avancee.md#d42-rappels-getterssetters) un setter est une méthode "déguisée" en propriété !_) dont je vous fournis le code à compléter (_à coller **dans** le corps de la classe `Router`_) : ```js - static #menuElement; - static set menuElement(element) { + static #menuElement; // propriété statique privée + /** + * Indique au Router la balise HTML contenant le menu de navigation + * Écoute le clic sur chaque lien et déclenche la méthode navigate + * @param element Élément HTML qui contient le menu principal + */ + static set menuElement(element) { // setter this.#menuElement = element; - // au clic sur n'importe quel lien contenu dans "element" + // au clic sur n'importe quel lien (<a href>) contenu dans "element" // déclenchez un appel à Router.navigate(path) // où "path" est la valeur de l'attribut `href=".."` du lien cliqué } ``` - À l'aide de ce setter, **détectez le clic sur n'importe quel lien du menu** (_actuellement il n'y a en a que 3, mais votre code doit fonctionner quelque soit le nombre de liens_) et **affichez dans la console l'attribut `href` du lien qui a été cliqué**. \ - Par exemple si l'utilisateur clique sur le lien **"À propos"** la console doit afficher la chaîne de caractères **`"/a-propos"`** + À l'aide de ce setter, **détectez le clic sur n'importe quel lien du menu** (_actuellement il n'y a en a que 3, mais votre code doit fonctionner quelque soit le nombre de liens, il faudra donc une boucle_) et lorsqu'un clic est détecté **affichez dans la console l'attribut `href` du lien qui a été cliqué**. \ + Par exemple si l'utilisateur clique sur le lien **"À propos"**, la console doit afficher la chaîne de caractères **`"/a-propos"`** (_l'URL du lien_) - > _**NB1 :** vous aurez besoin pour celà de la propriété [`event.currentTarget` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/Event/currentTarget) et de la méthode [`element.getAttribute()` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/Element/getAttribute)_ + > _**NB1 :** vous aurez besoin pour cela de la propriété [`event.currentTarget` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/Event/currentTarget) et de la méthode [`element.getAttribute()` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/Element/getAttribute)_ - > _**NB2 :** en cas de **problème de scope**, relisez donc la fin du paragraphe [C.1. Rappels](#c1-rappels), juste au cas où..._ + > _**NB2 :** en cas de **problème de scope**, rappelez vous que dans un callback d'événement, la valeur de `this` est parfois modifiée ! Relisez donc la fin du paragraphe [C.1. Rappels](#c1-rappels), juste au cas où..._ 3. **Pour terminer, maintenant que vous avez récupéré le `href` du lien cliqué, il ne vous reste plus qu'à invoquer la méthode `Router.navigate()` en lui passant en paramètre le `href` en question !** diff --git a/D-formulaires.md b/D-formulaires.md index 6f01d0f862b8c6d3a010546e634f19b191c8aad9..29be6f184507fe8a79878c72cae6de9cdbfc01ac 100644 --- a/D-formulaires.md +++ b/D-formulaires.md @@ -9,10 +9,9 @@ _**Dans cette partie du TP, nous allons travailler sur les formulaires et créer - [D.2. Préparatifs : La classe `Page`](#d2-préparatifs-la-classe-page) - [D.3. Le formulaire d'ajout de pizza](#d3-le-formulaire-dajout-de-pizza) - [D.4. La validation de la saisie](#d4-la-validation-de-la-saisie) -- [Étape suivante](#étape-suivante) ## D.1. Un peu de théorie -_**On utilise généralement l'API DOM avec les formulaires pour 2 choses :**_ +_**Comme vu en cours, on utilise généralement l'API DOM avec les formulaires pour 2 choses :**_ 1. **Récupérer les valeurs saisies par l'utilisateur** (_et afficher des messages d'erreur par exemple_) 2. **Détecter la soumission du formulaire** (_pour envoyer des données en AJAX à un webservice par exemple_) @@ -25,11 +24,11 @@ En imaginant le formulaire suivant : ``` On peut lire la valeur tapée par l'utilisateur dans le champ `"message"` avec la propriété [`.value`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input#value) de l'élément `<input type="text">`, et l'événement [`submit`](https://developer.mozilla.org/fr/docs/Web/API/HTMLFormElement/submit_event_) de la balise `<form>` : ```js -const form = document.querySelector('form'), - input = form.querySelector('input[name=message]'); +const form = document.querySelector('form'), // balise form + input = form.querySelector('input[name=message]'); // balise <input name="message"> -form.addEventListener('submit', function(event) { - event.preventDefault(); +form.addEventListener('submit', event => { // détection de la soumission + event.preventDefault(); // on empêche la page de se recharger console.log('Le formulaire a été soumis avec la valeur :'+input.value); }); ``` @@ -40,8 +39,8 @@ form.addEventListener('submit', function(event) { **Vous allez coder votre formulaire d'ajout de pizza dans une classe à part, `PizzaForm` (comme on l'a fait pour la `PizzaList`). Et dans cette nouvelle classe il faudra détecter la soumission du formulaire grâce à l'évènement `submit`** (_comme dans l'exemple ci-dessus_). -Hors pour le moment, les "pages" de notre application (_comme la `PizzaList`, ou notre future `PizzaForm`_) **ne sont pas capables d'ajouter des écouteurs d'événements** sur le code HTML qu'elles génèrent. \ -**Pourquoi ?** Et bien parce que leur méthode `render()` ne fait que retourner une chaîne de caractères et c'est le `Router` qui se charge de l'ajouter dans le DOM. La page n'a donc aucune connaissance ni du DOM, ni du moment où le code HTML qu'elle a généré est ajouté à l'écran. 😢 +Or pour le moment, les "pages" de notre application (_comme la `PizzaList`, ou notre future `PizzaForm`_) **ne sont pas capables d'ajouter des écouteurs d'événements** sur le code HTML qu'elles génèrent. \ +**Pourquoi ?** Et bien parce que leur méthode `render()` ne fait que retourner une chaîne de caractères et c'est le `Router` qui se charge de l'ajouter dans le DOM. La page n'a donc aucune connaissance ni du DOM, ni du moment à partir duquel le code HTML qu'elle a généré est ajouté à l'écran. 😢 Ce que je vous propose c'est d'ajouter une nouvelle méthode (`mount(element)`) qui va permettre au `Router` d'indiquer à chaque page quelle est la balise HTML (_l'élément du DOM_) dans laquelle elle vient de s'afficher : **de cette manière les pages auront d'un seul coup deux infos : le fait qu'elles viennent d'être affichée à l'écran, et aussi dans quelle balise HTML !** @@ -66,12 +65,14 @@ C'est grâce à ces deux infos que nos pages pourront enfin **ajouter des écout > _**NB2 :** Comme vous le voyez la classe `Page` ajoute à la classe `Component` :_ > - _une **propriété `element`**_ > - _une **méthode `mount()`** qui se contente de stocker la valeur de `element`_\ - > _C'est cette méthode qui pourra être surchargée par les pages pour ajouter des écouteurs d'événements_ + > _C'est cette méthode qui pourra être surchargée par les différentes pages de notre application pour y ajouter des écouteurs d'événements par exemple._ 2. **Faites hériter la `PizzaList` de la classe `Page`, et adaptez le constructeur pour respecter le nouveau constructeur parent** (_celui de `Page` désormais_) : ```js import Page from './Page'; + import PizzaThumbnail from '../components/PizzaThumbnail'; + export default class PizzaList extends Page { #pizzas; @@ -124,15 +125,22 @@ C'est grâce à ces deux infos que nos pages pourront enfin **ajouter des écout submit(event) {} } ``` + + > _**NB1 :** Vous remarquez qu'ici **la méthode `render()` retourne directement le code HTML sous forme de template string** sans utiliser d'autres `Component`._ \ + > _C'est en effet beaucoup plus simple que d'imbriquer des Component dans des Component dans des Component, comme on a pu le faire dans le précédent TP avec la classe `PizzaThumbnail` par exemple : le but était à l'époque juste de vous faire travailler les notions de composition et d'héritage, maintenant on privilégie la lisibilité du code._ + + > _**NB2 :** avant la template string, j'ai mis un commentaire `/*html*/`. Le but est de permettre à l'extension vscode [es6-string-html](https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html) de faire de la **coloration syntaxique du code HTML contenu à l'intérieur de votre chaîne JS** !!_ \ + > _Si ça vous intéresse n'hésitez pas à l'installer (_<kbd>CTRL</kbd>+<kbd>SHIFT</kbd>+<kbd>X</kbd>_)_ + 2. **Utilisez cette classe pour créer la constante `pizzaForm` dans le `main.js`.** À la place de : ```js - const pizzaList = new PizzaList([]), + const pizzaList = new PizzaList(data), aboutPage = new Component('p', null, 'ce site est génial'), pizzaForm = new Component('p', null, 'ici vous pourrez ajouter une pizza'); ``` vous aurez maintenant : ```js - const pizzaList = new PizzaList([]), + const pizzaList = new PizzaList(data), aboutPage = new Component('p', null, 'ce site est génial'), pizzaForm = new PizzaForm(); ``` @@ -141,9 +149,9 @@ C'est grâce à ces deux infos que nos pages pourront enfin **ajouter des écout <img src="images/readme/pizzaform.png" > -3. **Dans la méthode `mount()` de la classe `PizzaForm` détectez la soumission du formulaire à l'aide des méthodes `querySelector` et `addEventListener`**. Cet écouteur d'événement devra déclencher l'appel à la méthode `submit()` de l'instance. +3. **Dans la méthode `mount()` de la classe `PizzaForm`, détectez la soumission du formulaire à l'aide des méthodes `this.element.querySelector()` et `addEventListener`**. Cet écouteur d'événement devra déclencher l'appel à la méthode `submit()` de `PizzaForm`. - > _**NB :** Souvenez vous de la méthode `event.preventDefault()` et vérifiez que la soumission du formulaire n'entraîne pas un rechargement de page par exemple avec l'onglet Network/Réseau des devtools_ + > _**NB :** Souvenez vous de la méthode `event.preventDefault()` et vérifiez que la soumission du formulaire n'entraîne pas un rechargement de page en utilisant l'onglet "Network"/"Réseau" des devtools_ 4. Au submit, **affichez dans la console la valeur saisie par l'utilisateur dans l'input "name"**. @@ -154,5 +162,7 @@ C'est grâce à ces deux infos que nos pages pourront enfin **ajouter des écout 3. **Si le champ "name" n'est pas vide, afficher une alerte "La pizza xxxxx a été ajoutée"** (où "xxxxx" correspond au nom qu'a saisi l'utilisateur) **et videz le champ de saisie** pour permettre à l'utilisateur de saisir une nouvelle pizza. -## Étape suivante + > _**NB :** pour modifier la valeur d'un champ, sachez que la propriété [`.value`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input#value) de l'élément input est accessible aussi en écriture !_ + +## Étape suivante <!-- omit in toc --> Pour terminer ce TP, voyons comment améliorer la navigation dans notre application à l'aide de la History API : [E. Navigation avancée](./E-navigation-avancee.md). \ No newline at end of file diff --git a/E-navigation-avancee.md b/E-navigation-avancee.md index 3266baae8a0d14f5f22e090096e1588ba03d77cb..126ed290d384b8fefb5442289a2e940efbbed467 100644 --- a/E-navigation-avancee.md +++ b/E-navigation-avancee.md @@ -8,11 +8,11 @@ _**Pour terminer ce TP, nous allons finaliser le mécanisme de navigation dans l - lorsqu'on recharge la page dans le navigateur (F5) on doit afficher la page sur laquelle l'utilisateur se trouvait ## Sommaire <!-- omit in toc --> -- [E.1. activation du menu](#e1-activation-du-menu) +- [E.1. Activation du menu](#e1-activation-du-menu) - [E.2. History API](#e2-history-api) -- [E.3. Le Deeplinking](#e3-le-deeplinking) +- [E.3. Le deep linking](#e3-le-deep-linking) -## E.1. activation du menu +## E.1. Activation du menu **Actuellement lorsqu'on clique sur un lien du menu, rien n'indique dans le menu sur quelle page on se trouve.** On va donc remédier à ça en ajoutant une **classe CSS** sur le lien qui correspond à la page sur laquelle on se rend (_notez le trait plus grand sur le lien cliqué_) : <img src="./images/readme/nav-active.gif"> @@ -49,14 +49,14 @@ Ce qui permet ensuite d'utiliser les boutons précédent/suivant du navigateur ( - le **callback [`window.onpopstate` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/WindowEventHandlers/onpopstate)** qui permet d'appeler une fonction lors de l'utilisation des boutons précédent/suivant du navigateur - et la **propriété [`document.location` _(mdn)_](https://developer.mozilla.org/fr/docs/Web/API/Document/location)** qui permet d'obtenir des informations sur l'URL de la page courante - Dans `src/main.js`, assignez une fonction (une arrow function par exemple) à `window.onpopstate` et placez-y pour le moment juste un `console.log(document.location)`. + **Dans `src/main.js`, assignez une fonction (_une arrow function par exemple_) à `window.onpopstate`** et placez-y pour le moment juste un `console.log(document.location)`. - Rechargez la page, naviguez un peu via le menu et les boutons précédent/suivant et inspectez dans la console le contenu de `document.location`, vous devriez y trouver quelque chose qui peut vous aider à afficher la bonne page... + Rechargez la page, naviguez un peu via le menu et les boutons précédent/suivant et inspectez dans la console ce que contient `document.location`, **vous devriez y trouver quelque chose qui peut vous aider à afficher la bonne page**... - > _**NB :** faites attention à ne pas invoquer `window.history.pushState()` lors du `onpopstate` sinon cela va quelque peu "casser" la navigation avec les boutons précédent/suivant en créant une "inception"..._ + > _**NB :** faites attention à ne pas invoquer `window.history.pushState()` lors du `onpopstate` sinon cela va "casser" la navigation avec les boutons précédent/suivant en créant une "inception"..._ -## E.3. Le Deeplinking +## E.3. Le deep linking _**Notre application est presque terminée, à un détail près : l'absence de [deep linking](https://fr.wikipedia.org/wiki/Lien_profond).**_ @@ -66,17 +66,17 @@ En effet, si vous vous rendez directement sur http://localhost:8000/a-propos dan En fait, lorsque vous lancez la requête en entrant cette URL dans la barre d'adresse du navigateur, le serveur http lancé avec `npx serve` cherche par défaut à trouver un fichier `a-propos.html` ou bien un fichier `index.html` dans un sous dossier `/a-propos/`. Autant dire que ça n'a aucune chance de fonctionner comme ça. -Heureusement `npx serve` dispose d'une option `-s` qui permet de rediriger toutes les 404 vers le `index.html` de la racine : ainsi notre JS se chargera et il ne restera plus qu'à déterminer (en JS) quelle page afficher grâce à l'URL courante. +Heureusement `npx serve` dispose d'une option `-s` qui permet de rediriger toutes les 404 vers le `index.html` de la racine : ainsi notre page HTML se chargera correctement et il ne restera plus qu'à déterminer (en JS) quelle page afficher grâce à l'URL courante. Facile ! -1. **Stoppez le serveur HTTP `npx serve -l 8000` et relancez le avec cette fois l'option `-s` :** +1. **Stoppez donc le serveur HTTP `npx serve -l 8000` et relancez le avec cette fois l'option `-s` :** ```bash npx serve -s -l 8000 ``` - Rechargez la page http://localhost:8000/a-propos : la 404 a disparu est le site s'affiche ! Malheureusement c'est encore la `PizzaList` qui est affichée et pas la page "À propos"... + Rechargez la page http://localhost:8000/a-propos : la 404 a disparu est le site s'affiche ! Malheureusement c'est la `PizzaList` qui s'affiche et pas la page "À propos"... -2. **Maintenant que notre site s'affiche quelque soit l'URL de la page, reste à faire en sorte que l'on affiche la page qui correspond à l'URL demandée par l'utilisateur.** +2. **Maintenant que notre site s'affiche quelque soit l'URL de la page, il ne reste donc plus qu'à faire en sorte que l'on affiche la bonne page : c'est à dire celle qui correspond à l'URL demandée par l'utilisateur.** - Actuellement, au chargement, notre application affiche toujours la `PizzaList` en premier car dans notre `main.js` nous avons le code : + Actuellement, au chargement, notre application affiche systématiquement la `PizzaList` en premier car dans notre `main.js` nous avons le code : ```js Router.navigate('/'); ``` diff --git a/README.md b/README.md index 8bcda39fa1fa5c25aba6ff21be74928786d4ca22..7ec7d4625c870ad2833a2ae272581a28594eab5b 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,13 @@ - Savoir manipuler la page HTML avec l'API DOM - Savoir détecter les actions de l'utilisateur avec les Event - Être capable de gérer des formulaires avec JS +- Utiliser la History API ## Sommaire -Pour plus de clarté, les instructions du TP se trouvent dans des fichiers distincts (un fichier par sujet), procédez dans l'ordre sinon, ça fonctionnera beaucoup moins bien ! : +Pour plus de clarté, les instructions du TP se trouvent dans des fichiers distincts (un fichier par sujet), procédez dans l'ordre sinon, ça fonctionnera beaucoup moins bien ! 1. [A. Préparatifs](A-preparatifs.md) 2. [B. Les bases de l'API DOM](B-les-bases.md) 3. [C. Les événements](C-evenements.md) 4. [D. Les formulaires](D-formulaires.md) -5. [E. Formulaires et navigation](E-navigation.md) \ No newline at end of file +5. [E. Navigation avancée](./E-navigation-avancee.md) \ No newline at end of file diff --git a/images/readme/boutons-prev-next.gif b/images/readme/boutons-prev-next.gif index 8eff810b044b90553a44edb810d5e08c94d4596b..51a8e709afdf5f8b0ea4f7b19370d80701aeb296 100644 Binary files a/images/readme/boutons-prev-next.gif and b/images/readme/boutons-prev-next.gif differ diff --git a/images/readme/changement-url.gif b/images/readme/changement-url.gif index 946f5cfd819028ebf51274c2dad0cf9f53b239e0..ee4b6ca0efe8ae9349c822aa11a3b9601b36cd16 100644 Binary files a/images/readme/changement-url.gif and b/images/readme/changement-url.gif differ diff --git a/images/readme/deeplinking.gif b/images/readme/deeplinking.gif index ab5325ecb2400f4dfade1ed60c29911c30357e29..a1712e2c8a55d4b8b37f34802c635690fd301ede 100644 Binary files a/images/readme/deeplinking.gif and b/images/readme/deeplinking.gif differ diff --git a/images/readme/nav-active.gif b/images/readme/nav-active.gif index 354ac1408f12fe05eb07a84b96fa4774a096e877..75b3f66892fe5b2081daeff43267cba9d78973e3 100644 Binary files a/images/readme/nav-active.gif and b/images/readme/nav-active.gif differ diff --git a/images/readme/nav-simple.gif b/images/readme/nav-simple.gif index f95e5f418e471e29f3618a31ccf9be2d68814a95..d3aee44750b8b0a00d3b00e8bf65fc2899f99432 100644 Binary files a/images/readme/nav-simple.gif and b/images/readme/nav-simple.gif differ diff --git a/images/readme/newscontainer.png b/images/readme/newscontainer.png index 9b79588d0c2a25257782ef1a3209d6c9432c6dc3..6afca69a60bb1367767e37c8818da69f3e9daa64 100644 Binary files a/images/readme/newscontainer.png and b/images/readme/newscontainer.png differ diff --git a/images/readme/pizzaform.png b/images/readme/pizzaform.png index 0b62518532f043c0124d71e08c145b3abb7d1890..667f262c19ad603d57849d7e0d50fc5ff2716f89 100644 Binary files a/images/readme/pizzaform.png and b/images/readme/pizzaform.png differ diff --git a/images/readme/pizzaland-00.png b/images/readme/pizzaland-00.png index 3d3fef54ea9f1ec0f9704f2c0a3cbea88b31ee62..024a6fe73bdb3e1bfa739bb77c3ff10f9bafa0c0 100644 Binary files a/images/readme/pizzaland-00.png and b/images/readme/pizzaland-00.png differ diff --git a/images/readme/pizzaland-05.png b/images/readme/pizzaland-05.png deleted file mode 100644 index fafec9e3eb62973dec67bf9e4375a24322eae704..0000000000000000000000000000000000000000 Binary files a/images/readme/pizzaland-05.png and /dev/null differ diff --git a/images/readme/pizzaland-innerhtml.png b/images/readme/pizzaland-innerhtml.png index 7b3336142c1d5534fb5bd9788487c466a613acfb..9a5224ecb65eab667bd490b75fa1347a7d5d512f 100644 Binary files a/images/readme/pizzaland-innerhtml.png and b/images/readme/pizzaland-innerhtml.png differ diff --git a/images/readme/pizzaland-nav.gif b/images/readme/pizzaland-nav.gif deleted file mode 100644 index 81da2f846a9342f6fec7743a261023c0a3dcbd92..0000000000000000000000000000000000000000 Binary files a/images/readme/pizzaland-nav.gif and /dev/null differ diff --git a/images/readme/pizzaland-setattribute.png b/images/readme/pizzaland-setattribute.png index c2bee3aebcdabd9c6ee5c2b69aacac4d2346f18a..1bceac86ec66126061c92c15c56e382afd2993d9 100644 Binary files a/images/readme/pizzaland-setattribute.png and b/images/readme/pizzaland-setattribute.png differ diff --git a/images/readme/queryselector-pizzaList.png b/images/readme/queryselector-pizzaList.png index d6bade71e2611d69bf948755b2d7caf99a2b298b..1ea828262bc88b57717f9d2aad1bd18581ec28d1 100644 Binary files a/images/readme/queryselector-pizzaList.png and b/images/readme/queryselector-pizzaList.png differ