4. Intégration du formulaire

Actuellement, nous avons bien ajouté un composant contrôlé pour notre formulaire, mais il s’agit d’un fichier isolé dans notre application. Nous devons l’intégrer avec le reste de notre projet. D’abord, nous allons créer un nouveau composant de page nommé pokemon-edit.tsx, dans le dossier pages :

import React, { FunctionComponent, useState, useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import PokemonForm from '../components/pokemon-form';
import Pokemon from '../models/pokemon';
import POKEMONS from '../models/mock-pokemon';

type Params = { id: string };

const PokemonEdit: FunctionComponent<RouteComponentProps<Params>> = ({ match }) => {
  const [pokemon, setPokemon] = useState<Pokemon|null>(null);
 
  useEffect(() => {
    POKEMONS.forEach(pokemon => {
      if (match.params.id === pokemon.id.toString()) {
        setPokemon(pokemon);
      }
    })
  }, [match.params.id]);
   
  return (
    <div>
      { pokemon ? (
        <div className="row">
            <h2 className="header center">Éditer { pokemon.name }</h2>
            <PokemonForm pokemon={pokemon}></PokemonForm>
        </div>
      ) : (
        <h4 className="center">Aucun pokémon à afficher !</h4>
      )}
    </div>
  );
}
 
export default PokemonEdit;

Dans ce composant, on reconnaît du code que nous connaissons déjà. Aux ligne 7 et 9, nous déclarons un type pour une prop nommé id, qui correspond à l’identifiant du pokémon à éditer. Ensuite nous passons cette prop au composant. De la ligne 12 à 18, nous chargeons le bon pokémon dans notre state, c’est-à-dire celui avec l’identifiant passé depuis l’url /pokemon/edit/:id. Mais ce qui nous intéresse se situe dans le DOM vituel de ce composant.

À la ligne 24 et 25, nous intégrons deux éléments : un simple titre, et… notre formulaire d’édition d’un pokémon PokemonForm ! C’est comme cela que nous afficherons notre formulaire sur cette page d’édition. 🔥

Dans le cas où l’utilisateur souhaite accéder à un pokémon dont l’identifiant n’existe pas, on affiche le message « Aucun pokémon à afficher ! » à la ligne 28. Jusque-là, tout va bien.

Mais nous avons encore un problème, c’est que nous avons un composant de page, mais pas d’url associée. Nous n’avons encore rien déclaré au niveau de notre système de gestion des routes. Ajoutons donc la ligne suivante dans le composant App.tsx :

// Les importations...
import PokemonEdit from './pages/pokemon-edit';
 
const App: FunctionComponent = () => {
  return (
    ...
      <Switch>
        <Route exact path="/" component={PokemonsList} />
        <Route exact path="/pokemons" component={PokemonsList} />
        <Route exact path="/pokemons/edit/:id" component={PokemonEdit} />
        <Route path="/pokemons/:id" component={PokemonsDetail} />
        <Route component={PageNotFound} />
      </Switch>
      ...
  );
}
 
export default App;

On associe désormais la route pokemons/edit/:id à notre composant PokemonEdit, qui contient notre formulaire d’édition.

Du point de vue technique, tout doit fonctionne correctement. Seulement, du point de vue de l’utilisateur, il est impossible d’accéder à notre nouvelle page d’édition depuis l’application. On ne vas quand même pas demander à l’utilisateur de taper l’url de notre nouvelle page dans la barre de recherche de son navigateur.

Je vous propose donc d’ajouter un petit bouton d’édition depuis la page de détail d’un pokémon. Ce bouton sera simplement une icône d’un crayon, qui fera la redirection vers le formulaire d’édition :

L’icône de crayon au milieu à gauche permet d’éditer ce pokémon.

Pour ajouter cette icône, nous devons légèrement modifier notre composant PokemonDetail dans le fichier pokemon-detail.tsx :

// Les importations...

 
const PokemonsDetail: FunctionComponent<RouteComponentProps<Params>> = ({ match }) => {
   
  // ...
   
  return (
    <div>
      { pokemon ? (
        <div className="row">
          ...
            <div className="card-image">
              <img src={pokemon.picture} alt={pokemon.name} style={{width: '250px', margin: '0 auto'}}/>
              <Link to={`/pokemons/edit/${pokemon.id}`} className="btn-floating halfway-fab waves-effect waves-light"><i className="material-icons">edit</i></Link>
            </div>
            ...
    </div>
  );
}
 
export default PokemonsDetail;

À la ligne 15, nous ajoutons ce fameux bouton d’édition, avec quelques classes de Materialize permettant de positionner et de designer le bouton correctement.

Mais… le bouton s’affiche n’importe comment, est-ce bien normal ? 🤔

Oui ! Enfin non, il n’est pas normal d’afficher cela à l’utilisateur. En fait, le problème est que nous devons bien importer cette icône de crayon de quelque part. Pour cela, il faut importer une nouvelle librairie contenant tout un tas de petites icônes bien pratique, à afficher dans notre application. Pour importer ces icônes, nous devons ajouter cette ligne dans notre fichier index.html, juste sous la ligne où nous avons importer la librairie Materialize :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Pokédex</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  </head>
  <!-- ... -->
</html>

Avec cette nouvelle importation à la ligne 8, vous pouvez désormais retourner dans votre navigateur. Votre application devrez afficher le crayon correctement, sur la page de détail d’un pokémon.

Ensuite, cliquez sur le petit crayon. Vous serez redirigé vers notre formulaire flambant neuf. Nous sommes sur la bonne voie, ça commence à prendre forme. On continue dans cette direction !