# Paramètres de Routes
Maintenant que notre page d'accueil liste l'ensemble des articles de notre blog, il est temps de créer une page présentant le détail d'un article. Commençons par générer une page et sa route associée:
yarn rw g page BlogPost
Remarquez que nous ne pouvons pas nommer cette page
Post
car une autre page homonyme a déjà été crée lors de notre précédente démonstration du scaffolding.
Pour chaque article listé sur la page d'accueil, ajoutons un lien qui pointe vers notre nouvelle page (sans oublier au passage les imports pour Link
et routes
):
// web/src/components/BlogPostsCell/BlogPostsCell.js
import { Link, routes } from '@redwoodjs/router'
// QUERY, Loading, Empty and Failure definitions...
export const Success = ({ posts }) => {
return posts.map((post) => (
<article key={post.id}>
<header>
<h2>
<Link to={routes.blogPost()}>{post.title}</Link> </h2>
</header>
<p>{post.body}</p>
<div>Créé le: {post.createdAt}</div>
</article>
))
}
Si vous cliquez sur le lien, vous deviez voir s'afficher un peu de texte issu de BlogPostPage
. Mais ce dont nous avons vraiment besoin, c'est de pouvoir préciser quel article nous souhaitons afficher. Ce que nous cherchons a obtenir en définitive, c'est une URL du type /blog-post/1
. Pour cela, nous allons dire au routeur que notre url comporte une partie variable supplémentaire:
// web/src/Routes.js
<Route path="/blog-post/{id}" page={BlogPostPage} name="blogPost" />
Notez l'ajout de {id}
dans notre route. Redwood nomme ceci un paramètre de route. Ces paramètres de route signifie la chose suivante: "quelque soit la valeur à cette position, elle sera référencée par le nom utilisé entre les accolades".
Cool, cool, cool. Maintenant, nous devons donc construire un lien qui possède cet identifiant:
// web/src/components/BlogPostsCell/BlogPostsCell.js
<Link to={routes.blogPost({ id: post.id })}>{post.title}</Link>
Pour les routes avec paramètres, un objet est attendu pour chaque paramètre. Si vous cliquez sur le lien d'un article, vous constaterez qu'en effet il pointe désormais vers /blog-post/1
(ou /blog-post/2
, etc... selon l'article).
# Utilisation des Paramètres
OK, donc l'identifiant se trouve bien dans l'URL. Et maintenant que fait-t-on pour afficher le bon article? On dirait bien que nous allons devoir récupérer les données depuis la base. Vous l'aurez compris, c'est le bon moment pour utiliser une Cell:
yarn rw g cell BlogPost
Nous allons ensuite utiliser cette Cell dans notre page BlogPostPage
(et pendant que nous y sommes, nous insèrerons notre page dans notre Layout BlogLayout
):
// web/src/pages/BlogPostPage/BlogPostPage.js
import BlogLayout from 'src/layouts/BlogLayout'
import BlogPostCell from 'src/components/BlogPostCell'
const BlogPostPage = () => {
return (
<BlogLayout>
<BlogPostCell />
</BlogLayout>
)
}
export default BlogPostPage
Maintenant, à l'intérieur de notre Cell, nous avons besoin d'accéder à ce paramètre de route {id}
qui contient l'identifiant de notre article en base de données. Pour ce faire, mettons à jour la requête de façon à ce qu'elle accepte une variable en entrée. Modifions également le nom de la requête blogPost
en post
.
// web/src/components/BlogPostCell/BlogPostCell.js
export const QUERY = gql`
query BlogPostQuery($id: Int!) { post(id: $id) { id
title body createdAt }
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ post }) => { return JSON.stringify(post)}
Okay, on approche du but! Ceci étant, d'où vient donc ce $id
? Redwood a plus d'un tour dans son sac. Chaque fois que vous ajoutez un paramètre de route, ce paramètre est automatiquement accessible dans la page qui correspond. Ce qui signifie que vous pouvez modifier la page BlogPostPage
de la façon suivante:
// web/src/pages/BlogPostPage/BlogPostPage.js
const BlogPostPage = ({ id }) => { return (
<BlogLayout>
<BlogPostCell id={id} /> </BlogLayout>
)
}
id
existe déjà sans effort supplémentaire puisque nous avons nommé notre paramètre de route {id}
. Merci qui? Merci Redwood! Mais comment se fait-il que cet id
finisse par devenir un paramètre GraphQL $id
? Redwood s'en charge également pour vous! Par défaut, chaque propriété que vous donnez à une Cell devient automatiquement un variable disponible pour une requête GraphQL. Incroyablement simple, et pourtant vrai :)
D'ailleurs on peut le prouver! Essayez maintenant d'aller voir un article and — ... uh oh. Hmm:
Au passage le code d'erreur que vous voyez s'afficher provient de la section
Failure
de votre Cell!
Si vous examinez la console de votre navigateur, vous constaterez la présence d'une erreur GraphQL:
[GraphQL error]: Message: Variable "$id" got invalid value "1";
Expected type Int. Int cannot represent non-integer value: "1",
Location: [object Object], Path: undefined
Il s'avère que les paramètres de route sont extraits des URL sous la forme de chaînes de caractères, et dans le cas présent GraphQL s'attend à recevoir un identifiant sous la forme d'un entier. Nous pourrions simplement utiliser la fonction javascript parseInt()
afin de convertir notre paramètre de route vers un entier avant de le passer à BlogPostCell
. Mais honnêtement, on peut faire bien mieux que ça!
# Paramètres de Route Typés
Et si vous aviez la possibilité de demander cette conversion directement dans le chemin de la route? Et bien devinez-quoi, vous pouvez! Redwood appelle ça les paramètres de route typés ("route param types" en anglais). Et c'est aussi simple que d'ajouter :Int
à notre paramètre de route:
What if you could request the conversion right in the route's path? Well, guess what: you can! Introducing route param types. It's as easy as adding :Int
to our existing route param:
// web/src/Routes.js
<Route path="/blog-post/{id:Int}" page={BlogPostPage} name="blogPost" />
Voilà! Non seulement vous allez convertir sans effort le paramètre id
en un entier avant de la passer à votre Page, mais en bonus vous faîtes en sorte que la route n'applique que si id
représente effectivement un entier, c'est à dire une suite de chiffres. Dans le cas contraire, le routeur essaiera d'autres routes. S'il ne s'en trouve aucune à s'appliquer, le routeur affichera la page NotFoundPage
.
Que se passe-t-il si je veux passer d'autres propriétés à ma Cell dont je n'ai pas besoin dans la requête, mais qui me sont utile dans les composants Success/Loader/etc... ?
Toutes les propriétés que vous donnez à votre Cell seront automatiquement disponibles pour ses composants internes. Seuls ceux qui se se trouvent dans la liste des variables GraphQL seront transmises à la requête. Vous avez ainsi le meilleur des deux mondes! Dans l'affichage de notre article ci-dessus, si vous désirez montrer par exemple un nombre au hasard (pour des raisons evidentes liées à ce didacticiel :D), il vous suffit de passer cette propriété à votre Cell:
<BlogPostCell id={id} rand={Math.random()} />
Et ensuite vous la récupérez avec le résulat de la requête ans le composant (et même avec l'identifiant de l'article si vous le souhaitez): And get it, along with the query result (and even the original
id
if you want) in the component:
export const Success = ({ post, id, rand }) => { //... }
Merci Redwood!
# Afficher un Article
Maintenant, affichons un véritable article au lieu d'un simple dump du résultat de la requête. Il semble que ce soit l'endroit parfait pour utiliser un bon vieux composant puisque nous affichons les articles de façon identique (pour l'instant) à la fois sur la page d'accueil et sur la page de détail.
yarn rw g component BlogPost
L'exécution de cette commande créé le composant BlogPost
dans le fichier web/src/components/BlogPost/BlogPost.js
, accompagné de son fichier de test:
// web/src/components/BlogPost/BlogPost.js
const BlogPost = () => {
return (
<div>
<h2>{'BlogPost'}</h2>
<p>{'Find me in ./web/src/components/BlogPost/BlogPost.js'}</p>
</div>
)
}
export default BlogPost
Vous remarquerez peut-être que nous n'avons ici aucun
import
relatif à la librairieReact
. Il s'agit pourtant bien d'un classique composant React. En réalité, nous (la "Redwood dev team") sommes un peu fatigués d'avoir à importer constamment les mêmes fichiers de la même manière... alors nous avons fait en sorte que Redwood le fasse pour nous, et donc pour vous!
Supprimons la partie de code qui affiche l'article dans BlogPostCell
, et mettons la plutôt ici. Ce faisant, passons à notre nouveau composant la propriété post
:
// web/src/components/BlogPost/BlogPost.js
import { Link, routes } from '@redwoodjs/router'
const BlogPost = ({ post }) => { return (
<article> <header> <h2> <Link to={routes.blogPost({ id: post.id })}>{post.title}</Link> </h2> </header> <div>{post.body}</div> </article> )
}
export default BlogPost
Mettons à jour BlogPostsCell
et BlogPostCell
pour utiliser notre composant d'affichage commun:
// web/src/components/BlogPostsCell/BlogPostsCell.js
import BlogPost from 'src/components/BlogPost'
// Loading, Empty, Failure...
export const Success = ({ posts }) => {
return posts.map((post) => <BlogPost key={post.id} post={post} />)}
// web/src/components/BlogPostCell/BlogPostCell.js
import BlogPost from 'src/components/BlogPost'
// Loading, Empty, Failure...
export const Success = ({ post }) => {
return <BlogPost post={post} />}
Et nous y sommes! Nous devrions maintenant pouvoir aller et venir à notre guise entre la page d'accueil et les articles.
Si vous appréciez ce que vous venez de voir sur le routeur, vous pouvez en apprendre plus dans le guide qui lui est consacré.
# Résumé
Un petit état des lieux de ce que nous avons réalisé:
- Création d'une nouvelle page pour afficher un article
- Ajout d'une route prenant en char l'identifiant
id
d'un article sous la forme d'un paramètre de route - Création d'une Cell permettant de récupérer et afficher un article
- Constat de la capacité de Redwood à vous mettre de bonne humeur en vous donnant accès à
id
là où vous en avez besoin tout en le convertissant au format numérique à la volée - Transformation de l'affichage d'un article en un composant React classique pouvant être partagé à plusieurs endroits dans l'interface (en l'espèce dans la page d'accueil et la page de détail)