Skip to content

Formulaires

Les champs de formulaire sont un autre élément fondamental dans l’interactivité d’une application. Ils peuvent servir à ajouter ou modifier des données stockées sur un serveur, ou plus simplement permettre de rechercher, filtrer ou réordonner des données présentes dans l’application.

Formulaires natifs

En HTML, la balise <form> offre un mécanisme permettant, lors du clic sur un bouton, d’envoyer facilement les données du formulaire à un serveur sans nécessiter l’usage de code JavaScript :

html
<form id="my-form" action="https://example.com/submit" method="POST">
  <label for="name">Name :</label>
  <input type="text" id="name" name="name" required />
  <button type="submit">Send</button>
</form>

Lors du clic sur le bouton Submit, la donnée name est envoyée sous la forme d’une requête POST à l’URL indiquée au niveau de la balise <form>.

La page web est ensuite recalculée avec le code HTML, CSS et JavaScript renvoyé par le serveur en réponse de la requête.

Si l’on souhaite gérer davantage le formulaire côté frontend, il est possible de modifier cette logique en gérant la soumission du formulaire avec du code JavaScript :

js
document.getElementById("my-form").addEventListener("submit", function (event) {
  event.preventDefault(); // Block page reload

  const name = document.getElementById("name").value;

  // Do some validations
  if (name === "") {
    alert("Name can't be empty");
    return;
  }

  fetch("https://example.com/api/submit", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ name: name }),
  });
});

On bloque ici le rechargement de la page et on récupère les informations du formulaire pour les envoyer à l'aide de la fonction fetch. Cette stratégie offre plus de flexibilité côté frontend, notamment pour effectuer des validations avant l’envoi, comme ici, ou pour gérer l'état de l'application à la suite de la validation du formulaire.

NOTE

💡 Si vous n'êtes pas très familier avec la fonction fetch, consultez cette fiche annexe.

Champs de formulaire réactifs

Avec les frameworks JavaScript, l’application est dite "Single Page" (SPA, pour Single Page Application). C’est-à-dire que l’ensemble du code frontend est chargé lors de la première requête de l'utilisateur sur le site. Cela offre une expérience plus fluide dans l’application, car il n’est pas nécessaire d’attendre le renvoi de code HTML et CSS lorsque l’on change de page ou lorsque l’on valide un formulaire. Cela permet également de conserver plus facilement l'état de l'application frontend en fonction des interactions de l'utilisateur.

C’est pourquoi, dans une SPA, on cherchera généralement à gérer le traitement des champs de formulaire côté frontend, avant d’envoyer éventuellement des données sur un serveur via du code JavaScript.

Pour gérer les champs en JavaScript, nous pouvons à nouveau nous appuyer sur la réactivité offerte par le framework. Pour lier une variable JavaScript à un champ, il suffit de conjuguer l’utilisation d’une donnée réactive et d’un événement :

jsx
import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");
  const handleChange = (event) => {
    setName(event.target.value);
  };

  return (
    <div>
      <h1>Form with React.js</h1>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" value={name} onInput={handleChange} />
      <p>Current value is : {name}</p>
    </div>
  );
}

L’attribut de balise value permet d'associer la valeur actuelle du champ à notre donnée reactive, et l’événement input répercute les modifications du champ sur la donnée. La donnée réactive peut être utilisé ailleurs dans le composant, comme ici : chaque modification au niveau du champ de formulaire se répercutera instantanément sur la valeur affichée dans la balise <p>.

L’usage d’une variable réactive simplifie l’interaction d’une même donnée avec différents éléments du DOM :

jsx
import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");
  const handleChange = (event) => {
    setName(event.target.value);
  };
  const reset = () => setName("");

  return (
    <div>
      <h1>Form with React.js</h1>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" value={name} onChange={handleChange} />
      {name.length ? <button onClick={reset}>Cancel</button> : null}
      {name.length > 15 ? <p>Name is too long</p> : null}
      <p>Current value is : {name}</p>
      <button disabled={!name.length}>Submit</button>
    </div>
  );
}

TIP

💡 Comme pour class, qui devient className, l'attribut JavaScript for devient htmlFor en JSX, afin de ne pas entrer en conflit avec le mot-clé JavaScript.

Ici, l’état de name est à la fois lié à un champ de formulaire, conditionne l’affichage d’un bouton et d’un message d’erreur, est affiché dans un paragraphe et conditionne la soumission du champ.

Si chaque modification du nom avait dû être répercutée manuellement sur le DOM en JavaScript, cela aurait nécessité beaucoup de manipulations lors de l'édition du champ. L’usage de la réactivité permet ici de représenter plus simplement l’effet de la donnée sur l’état du DOM, directement au niveau de la déclaration HTML, de façon déclarative. Cela permet également de plus facilement découpler les différentes interactions.

Une implémentation similaire peut s’appliquer à d’autres types de champs, comme les <textarea /> ou les <select> (menus déroulants) :

jsx
import { useState } from "react";

export default function App() {
  const [selectedOption, setSelectedOption] = useState("");

  const handleChange = (event) => {
    setSelectedOption(event.target.value);
  };

  return (
    <div>
      <h1>Form with React.js</h1>
      <label htmlFor="options">Choose an option:</label>
      <select id="options" value={selectedOption} onChange={handleChange}>
        <option value="option1">Option 1</option>
        <option value="option2">Option 2</option>
        <option value="option3">Option 3</option>
      </select>
      <p>Current selection is: {selectedOption}</p>
    </div>
  );
}

NOTE

📌 Si, comme ici, la valeur initiale de la donnée ne correspond à aucune des value des balises <option>, alors aucune option ne sera initialement sélectionnée. L'utilisateur devra effectuer une première sélection au niveau du menu déroulant pour que le champ soit sur une valeur valide. Il est bien sûr possible d'initialiser la donnée réactive avec la valeur d'une des options directement, qui fera alors office d'option par défaut.

🐶 Dog Center

Le centre comprend désormais un nombre conséquent de chiens. Nous aimerions ajouter, en en-tête de la galerie, des champs de formulaire permettant de rechercher un chien par son nom et de trier les chiens soit par âge, soit par ordre alphabétique.

Pour cela, ajoutons les éléments de formulaire à notre composant App principal:

jsx
import './App.css';
import React, { useState } from "react";
import dogsData from "./dogsData";
import NewsSlider from "./components/NewsSlider";
import DogCard from "./components/DogCard";

export default function App() {
  const [search, setSearch] = useState("");
  const [dogsSortBy, setDogsSortBy] = useState("age");

  return (
    <div>
      <h1>Dog Center</h1>
      <NewsSlider />
      <div id="gallery-options">
        <input
          type="text"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder="Search dog"
        />
        <label htmlFor="dog-sort">Sort by : </label>
        <select
          id="dog-sort"
          value={dogsSortBy}
          onChange={(e) => setDogsSortBy(e.target.value)}
        >
          <option value="age">Age</option>
          <option value="name">Name</option>
        </select>
      </div>
      <div id="dog-gallery">
        ...
      </div>
    </div>
  );
}

Nous définissons deux données réactives search et dogsSortBy dans le composant. Les données sont ensuite liées à deux champs de formulaire : une balise <input> pour la recherche, et un menu déroulant (<select>) pour l'ordonnancement des chiens.

Il reste maintenant à faire en sorte que la valeur des champs de formulaire influence la liste de DogCard présente dans notre galerie. Ce sera l'objet du prochain chapitre.

Conclusion

Grâce aux données réactives, il est plus simple de lier la valeur d’un champ de formulaire à celle d’une variable JavaScript. Cela permet d’exploiter facilement cette donnée directement côté frontend, pour effectuer des validations ou modifier le contenu de l’application en conséquence.

Dans le prochain chapitre, nous verrons comment déterminer dynamiquement la valeur d'une donnée en fonction de celles d'autres, avec le concept de donnée calculée.