***Ce TP va permettre de mettre en oeuvre les principales méthodes de sélection et de modification d'éléments de l'arbre DOM.***
## A.1. Installation
1.**Récupérez le contenu du dossier `demarrage`, il contient une solution du TP sur la POO, les modules et le typage qui servira de base pour ce TP sur l'API DOM.** Comme lors du TP précédent n'oubliez pas de lancer un serveur web dans ce dossier
```bash
cd /chemin/vers/votre/dossier/demarrage
python3 -m http.server 8000
```
*Si vous avez des questions sur le lancement de ce serveur, ou sur la procédure à suivre dans le cas où vous n'utiliseriez pas les postes des salles de TP mais votre propre machine, relisez attentivement le [README du premier TP](../01-premiers-pas-en-js/README.md#Préparatifs) !*
2.**Ouvrez le fichier `package.json` et constatez que Babel, webpack et Flow sont listés dans les dépendances du projet.** Le dossier `demarrage` est en effet **déjà configuré pour l'utilisation de tous les outils nécessaires au dev JS moderne**. On va donc pouvoir tout installer facilement grâce à la commande :
```bash
npm install
```
(à lancer à la racine du dossier de votre projet, cad là où se trouve le fichier `package.json`)
*Si vous voulez en savoir plus sur le détail de l'installation et de la configuration des différents outils, vous pouvez consulter les TPs précédents : [modules](../02-poo-modules-typage/C-modules.md#c2-rendre-les-modules-compatibles-avec-les-vieux-navigateurs) et [typage](../02-poo-modules-typage/D-typage.md#d1-installation-et-configuration).*
3.**Une fois tout installé, vous pouvez relancer la compilation à l'aide de la commande `npm run watch`**. Vérifiez ensuite que la page s'affiche correctement dans le navigateur :<br><ahref="images/pizzaland-05-modules-webpack.jpg"><imgsrc="images/pizzaland-05.jpg"width="80%"></a>
Tout au long du TP pensez à vérifier régulièrement que les types que vous utilisez pour les variables, paramètres et valeurs de retour des fonctions/méthodes, sont corrects à l'aide de la commande `./node_modules/.bin/flow`
## A.2. Sélectionner des éléments
### A.2.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()`.
querySelector() est une méthode de la classe Element qui permet de retourner une référence vers un élément de la page (une balise) à partir d'un sélecteur CSS. Par exemple :
assigne dans la constante `appContainerElement` la balise d'id html 'appContainer' :
```html
<mainid="appContainer">
```
Dans le fichier `main.js`, sélectionnez les éléments suivants et affichez les dans la console à l'aide de l'instruction `console.log()`. Par exemple si l'on demande d'afficher la balise d'id *"appContainer"* il faudra taper le code suivant :
En inspectant le code html de la page (à l'aide de l'onglet Elements/Inspecteur des devtools) trouvez les sélecteurs qui permettront d'afficher dans la console :
1. La balise `<img>` qui contient le logo de la page (les 2 parts de pizza)
2. Le lien du menu "Ajouter une pizza"
3. Le lien vers le site [Unsplash](https://unsplash.com/) dans les crédits
4. le titre de la première pizza (*`<h4>Regina</h4>`*)
La propriété `innerHTML` permet à la fois de lire et de modifier le contenu d'un Element HTML (le contenu HTML compris entre les balises ouvrantes et fermantes)
1. Affichez dans la console le titre de la deuxième pizza (*"Napolitaine"*)
2. Remplacez dans la page le titre de la deuxième pizza par *"Savoyarde"*
3. Ajoutez au titre de la page le code HTML suivant :
```html
<smallclass="label label-success">les pizzas c'est la vie</small>
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 : [B. Les événements](./B-evenements.md).
# B. Les événements de l'API DOM <!-- omit in toc -->
## Sommaire <!-- omit in toc -->
-[B.1. Rappels](#b1-rappels)
-[B.2. La gestion du menu](#b2-la-gestion-du-menu)
-[Étape suivante](#Étape-suivante)
## B.1. Rappels
**Le système d'événements en JS permet de réagir à des actions de l'utilisateur (survol d'un élément, click sur un lien, soumission d'un formulaire, etc.) ou à des événements déclenchés par le navigateur (fin du chargement de la page ou d'une image, etc.).**
Comme vu en cours (cf. pdf du cours sur moodle) on peut associer une fonction à un événement grâce à la méthode `addEventListener()` de la classe Element.
Par exemple, pour déclencher la fonction `handleClick` lors du clic sur le premier lien de la page, on peut écrire :
1. le 2e paramètre que l'on passe à addEventListener est une référence vers la fonction `handleClick` et pas l'exécution de la fonction (`handleClick()`)
2. la fonction qui est passée à `addEventListener()` recevra un objet de la classe [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
3. Il faut presque systématiquement (sauf cas particuliers) appeler en premier lieu la méthode `event.preventDefault()` qui permet d'éviter que le navigateur n'exécute le traitement par défaut de l'événement : par exemple rediriger l'utilisateur vers une nouvelle page lorsqu'il clique sur un lien, recharger la page lorsqu'il soumet un formulaire, etc.
## B.2. La gestion du menu
Dans le fichier `main.js`
1.**Commencez par commenter le code de l'exercice [A. Les bases de l'API DOM](./A-les-bases.md)** (certains sélecteurs pourront vous être utiles par la suite)
2. A l'aide de `querySelector()` et `addEventListener()`, **affichez un message dans la console à chaque fois que l'utilisateur clique sur le lien du menu de navigation "Ajouter une pizza"**
3.**Effacez le code précédent et remplacez le par un code qui permette d'écouter le clic sur *tous* les liens du menu de navigation** :
Au clic sur n'importe quel lien de la navigation (actuellement il n'y a dans le menu que les liens "La carte" et "Ajouter une pizza", mais ce code doit fonctionner quelque soit le nombre de liens dans le menu) afficher dans la console le libellé du lien qui a été cliqué grâce à `event.currentTarget` (type `EventTarget`) et `element.innerHTML` : par exemple si l'utilisateur clique sur le lien "La carte" on affiche dans le console la chaîne de caractères `"La carte"`
***NB :** Pour information, le type de `event.currentTarget` est `EventTarget` et celui de la valeur retournée par la méthode `querySelectorAll()` est `NodeList<HTMLElement>`*
4.**Ajoutez la classe CSS "active" sur la balise `<li>` qui contient le lien qui a été cliqué** (utilisez pour cela la propriété [element.parentElement](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement))
5.**Juste avant d'ajouter la classe "active" sur le lien cliqué, effacez les classes CSS du `<li>` du menu qui était précédemment actif** de manière à n'avoir qu'un seul lien actif à la fois<br><imgsrc="./images/pizzaland-nav.gif">
## Étape suivante
Maintenant que l'on est capable de détecter les actions de l'utilisateur nous allons travailler sur la gestion des formulaires dans le prochain exercice : [C. Les formulaires](./C-formulaires.md).
# C. La gestion des formulaires <!-- omit in toc -->
## Sommaire <!-- omit in toc -->
-[C.1. Rappels](#c1-rappels)
-[C.2. Préparatifs](#c2-préparatifs)
-[C.3. Le formulaire d'ajout de pizza](#c3-le-formulaire-dajout-de-pizza)
-[C.4. La validation de la saisie](#c4-la-validation-de-la-saisie)
-[C.5. Le formulaire complet :](#c5-le-formulaire-complet-)
## C.1. Rappels
Comme vu en cours (cf. pdf sur moodle), on peut utiliser l'API DOM avec les formulaires principalement pour 3 utilisations :
- vérifier la saisie de l'utilisateur et afficher des messages d'erreur si besoin
- modifier éventuellement les valeurs des champs (corriger une valeur, ou vider un formulaire)
- Soumettre les données en AJAX (ce qui sera abordé dans le prochain cours et TP)
## C.2. Préparatifs
Avant d'aller plus loin dans le TP faisons un point sur le code qui est fourni dans le dossier `demarrage` : il contient en effet des classes `Page` et `PageRenderer` qui correspondent à la solution de l'exercice [E. pour aller plus loin](../02-poo-modules-typage/E-pour-aller-plus-loin.md) :
- La classe `Page` est une classe de base dont hérite la classe `HomePage` et qui implémente une méthode `renderTitle()`.
- La classe `PageRenderer` est une classe qui dispose d'une méthode statique `PageRenderer.renderPage( page )` qui permet d'afficher une page (méthode `render()`) et son titre (méthode `renderTitle()` ci-dessus).
Pour permettre à chaque page de réagir aux événements de l'utilisateur, on va ajouter une méthode `mount()` dans la classe `Page` et qui sera appelée par la classe `PageRenderer` à chaque fois qu'on affiche une page via `renderPage()` :
1.**Ajouter la méthode `mount()` à la classe `Page`** :
```js
mount():void{
// par défaut, cette méthode ne fait rien
// ce sont les classes filles qui devront surcharger cette méthode
}
```
2.**Appeler la méthode `mount()` à la fin de la méthode statique `PageRenderer.renderPage()`** :
```js
staticrenderPage(page:Page):void{
// ...
page.mount();
}
```
## C.3. Le formulaire d'ajout de pizza
1.**Créez la classe `AddPizzaPage`** (dans un module `js/AddPizzaPage.js`) dont le titre est "Ajouter une pizza" et dont la méthode `render()` est surchargée comme suit :
2.**Dans le fichier `main.js` instanciez la classe `AddPizzaPage` puis, au "click" sur le lien "Ajouter une pizza"** affichez la `AddPizzaPage` à l'aide de `PageRenderer` :
```js
// addPizzaPage est une instance de la classe AddPizzaPage
PageRenderer.renderPage(addPizzaPage);
```
3.**Ajoutez une méthode `submit()` dans la classe `AddPizzaPage`.** Cette méthode sera appelée lorsque l'utilisateur soumettra le formulaire :
```js
submit(event:Event):void{
// ici le code de votre méthode
}
```
Cette méthode va pour le moment juste afficher un message dans la console.
***NB:** Souvenez vous de la méthode `event.preventDefault()`...*
3.**Overridez la méthode `mount()` dans la classe `AddPizzaPage`** en y ajoutant un écouteur d'événement 'submit' sur la balise `<form class="addPizzaPage">` qui va déclencer la méthode `submit()` de l'instance.
***NB :** Pour rappel, la valeur du `this` à l'intérieur d'un écouteur d'événement (fonction appelée par addEventListener) est toujours l'élément HTML qui a déclenché l'événement (ici le formulaire). Pour pouvoir appeler une méthode de l'instance, il faut forcer la valeur du `this` pour qu'elle corresponde toujours à l'instance dans laquelle le code s'exécute. Comme expliqué dans le pdf du cours, il existe plusieurs manières de le faire, mais celle que je vous recommande est l'emploi de la méthode [`bind()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) dans le constructeur de la classe :*
```js
class maClasse {
constructor(){
// $FlowFixMe : pour dire à flow d'ignorer la ligne suivante
this.onClick = this.onClick.bind(this)
}
onClick(event:Event):void {
// this est correct !
}
}
```
## C.4. La validation de la saisie
1.**Au submit afficher dans la console la valeur saisie dans le champ "nom"**. Un sélecteur CSS qui peut être utile ici est le [sélecteur d'attributs](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
3.**Si le champ "nom" est vide, afficher un message d'erreur** à l'aide de la fonction [`alert()`](https://developer.mozilla.org/fr/docs/Web/API/Window/alert)
4.**Si le champ "nom" 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 vider le champ de saisie** pour permettre à l'utilisateur de saisir une nouvelle pizza.
## C.5. Le formulaire complet :
Coder le formulaire complet de création de pizza selon le code HTML suivant (tous les champs sont obligatoires) :
***NB:** Pour récupérer la valeur contenue dans un champ `<select>` ce n'est pas la propriété `value` qu'il faut utiliser mais `selectedOptions` (https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/selectedOptions). Cette propriété retourne un tableau des valeurs sélectionnées, si le tableau est vide, c'est qu'aucune valeur n'a été choisie par l'utilisateur.*
- Savoir ajouter des écouteurs d'événements pour réagir aux actions de l'utilisateur
- Être capable de gérer des formulaires avec JS
- Et faire encore évoluer notre application ***"Pizzaland"*** 🍕
## 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 ! :