Skip to content

Étape 4 — Page de détail avec route dynamique

Résultat final (toutes les séquences terminées)

Page de détail — version finale

Votre version n'aura pas encore les types colorés ni les statistiques — ils seront ajoutés dans les séquences suivantes.

Objectifs

  • Créer une route dynamique (/pokemon/:id)
  • Récupérer le paramètre d'URL avec useRoute()
  • Rendre la carte Pokémon cliquable pour naviguer vers le détail

Contexte

On veut cliquer sur un Pokémon dans la grille pour voir sa fiche complète :

L'URL sera /pokemon/5566710e-... (l'ID du Pokémon).

Encart oral — Routes dynamiques

Une route dynamique contient un segment variable, noté [id] dans le nom de fichier.

useRoute() = lire les informations de la page actuelle (lecture seule) :

js
const route = useRoute()

// Sur l'URL /pokemon/abc123?lang=fr
route.params.id   // → "abc123"  (paramètre dynamique)
route.path         // → "/pokemon/abc123"  (chemin complet)
route.query.lang   // → "fr"  (paramètre de requête ?lang=fr)

useRouter() = changer de page par code (navigation) :

js
const router = useRouter()

router.push('/')              // → aller à la page d'accueil
router.push('/favoris')       // → aller à la page favoris
router.back()                 // → retour à la page précédente (comme le bouton ← du navigateur)

Analogie : useRoute() c'est le GPS (il vous dit où vous êtes). useRouter() c'est le volant (il vous permet d'aller ailleurs).

Tâches

1. Créer la page de détail

Créez d'abord le dossier src/pages/pokemon/, puis le fichier [id].vue à l'intérieur :

src/pages/
├── pokemon/          ← nouveau dossier
│   └── [id].vue      ← nouveau fichier
├── index.vue
├── favoris.vue
├── a-propos.vue
└── [...path].vue

Cette page doit :

  1. Récupérer l'id depuis useRoute().params.id
  2. Appeler fetch('http://localhost:3535/pokemons') pour charger tous les Pokémon
  3. Trouver le Pokémon correspondant à l'id avec .find()
  4. Afficher : image (grande), nom, niveau, description
  5. Afficher un message d'erreur si le Pokémon n'est pas trouvé
  6. Ajouter un bouton "Retour" qui utilise $router.back()

2. Rendre les cartes cliquables

Dans PokemonCard.vue, ajoutez la prop :to sur la <v-card> existante pour créer un lien vers la page de détail :

vue
<v-card :to="`/pokemon/${pokemon.id}`" hover>
  • :to transforme la carte en lien (navigation au clic)
  • hover est déjà présent depuis l'étape 2 (effet d'élévation au survol)

3. Tester

  • Cliquez sur une carte → la page de détail s'affiche avec les bonnes données
  • Le bouton "Retour" ramène à la page précédente
  • Testez un ID invalide (ex: /pokemon/xxx) → le message d'erreur s'affiche

Projet perso — À faire en parallèle

Dans votre projet individuel :

  1. Créez votre page de détail avec une route dynamique (/item/[id].vue ou similaire)
  2. Rendez vos cartes cliquables vers la page de détail
  3. Affichez les données détaillées de l'élément sélectionné
  4. Ajoutez un bouton retour

Références utiles

Tests

  • Cliquer sur une carte navigue vers /pokemon/:id
  • La page de détail affiche l'image, le nom, le niveau et la description
  • Le bouton "Retour" fonctionne
  • Un ID invalide affiche le message d'erreur
  • 0 erreurs dans la console

Solutions

src/pages/pokemon/[id].vue
vue
<template>
  <v-container>
    <v-btn
      variant="text"
      prepend-icon="mdi-arrow-left"
      class="mb-4"
      @click="$router.back()"
    >
      Retour
    </v-btn>

    <v-alert
      v-if="!pokemon"
      type="error"
      variant="tonal"
    >
      Pokémon non trouvé.
    </v-alert>

    <v-card
      v-else
      max-width="800"
      class="mx-auto"
    >
      <v-img
        :src="getImageUrl(pokemon.img)"
        :alt="pokemon.name"
        height="300"
        cover
      />

      <v-card-title class="text-h4">
        {{ pokemon.name }}
      </v-card-title>

      <v-card-subtitle>
        Niveau {{ pokemon.level }}
      </v-card-subtitle>

      <v-card-text>
        <p v-if="pokemon.description" class="text-body-1 mb-4">
          {{ pokemon.description }}
        </p>
      </v-card-text>
    </v-card>
  </v-container>
</template>

<script setup>
import { getImageUrl } from '@/utils/imageUrl'

const route = useRoute()

const pokemons = ref([])
const pokemon = computed(() => {
  return pokemons.value.find(p => p.id === route.params.id)
})

onMounted(async () => {
  const response = await fetch('http://localhost:3535/pokemons')
  pokemons.value = await response.json()
})
</script>
src/components/PokemonCard.vue (modification)
vue
<v-card :to="`/pokemon/${pokemon.id}`" hover>

Ajoutez :to et hover sur la balise <v-card> existante.

Commit

bash
git add -A
git commit -m "feat: page détail Pokémon avec route dynamique"

Problème à résoudre plus tard

Vous avez remarqué ? On appelle fetch() deux fois : une fois dans index.vue et une fois dans [id].vue. C'est du gaspillage. À la prochaine séquence (Pinia), on résoudra ce problème en centralisant les données dans un store.

Documentation pour les cours de développement web