Skip to main content
Version: 5.3

Routing Params

すべての投稿を表示するホームページができたので "deatil" (詳細)ページ -- つまり単一のブログ記事を表示する正規の URL を作成しましょう。まず、ページとルートを生成します:

yarn rw g page Article

では、ホームページのブログ記事のタイトルを詳細ページにリンクしてみましょう( Linkroutesimport します):

web/src/components/ArticlesCell/ArticlesCell.js
import { Link, routes } from '@redwoodjs/router'

// QUERY, Loading, Empty and Failure definitions...

export const Success = ({ articles }) => {
return (
<>
{articles.map((article) => (
<article key={article.id}>
<header>
<h2>
<Link to={routes.article()}>{article.title}</Link>
</h2>
</header>
<p>{article.body}</p>
<div>Posted at: {article.createdAt}</div>
</article>
))}
</>
)
}

ブログ記事のタイトルのリンクをクリックすると、 ArticlePage に定型文が表示されます:

Article page

しかし本当に必要なのは、このページで表示したい どの ブログ記事を指定するかということです。URLの中で /article/1 のようにブログ記事のIDを指定できればいいのですが。 <Route> にURLの別の部分を期待するように伝え、それがきたら、その部分に後で参照できるような名前をつけましょう:

web/src/Routes.js
<Route path="/article/{id}" page={ArticlePage} name="article" />

{id} に注目してください。Redwoodではこれを ルートパラメータ と呼んでいます。これは "パスのこの位置にある値が何であれ、中括弧の中の名前で参照させてください" と言う意味です。そしてルートファイルで、ルートを Set 内の BlogLayout に移動しましょう。

web/src/Routes.js
import { Router, Route, Set } from '@redwoodjs/router'
import ScaffoldLayout from 'src/layouts/ScaffoldLayout'
import BlogLayout from 'src/layouts/BlogLayout'

const Routes = () => {
return (
<Router>
<Set wrap={ScaffoldLayout} title="Posts" titleTo="posts" buttonLabel="New Post" buttonTo="newPost">
<Route path="/posts/new" page={PostNewPostPage} name="newPost" />
<Route path="/posts/{id:Int}/edit" page={PostEditPostPage} name="editPost" />
<Route path="/posts/{id:Int}" page={PostPostPage} name="post" />
<Route path="/posts" page={PostPostsPage} name="posts" />
</Set>
<Set wrap={BlogLayout}>
<Route path="/article/{id}" page={ArticlePage} name="article" />
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={HomePage} name="home" />
</Set>
<Route notfound page={NotFoundPage} />
</Router>
)
}

export default Routes

イイネ、イイネ、イィーネッ。 さて、次はブログ記事のIDを含むリンクを構築しなければなりません:

web/src/components/ArticlesCell/ArticlesCell.js
<h2>
<Link to={routes.article({ id: article.id })}>{article.title}</Link>
</h2>

ルートパラメータを持つルートの場合、名前付きルート関数は、各パラメータの値を指定するオブジェクトをが渡されることを期待します。今リンクをクリックすると、確かに /article/1 (または /article/2 など、ブログ記事のIDによります)に移動します。

新しいブログ記事詳細ページを表示しようとすると、エラーが発生することにお気づきかもしれません。これは、ページが生成されたときの定型的なコードに、ページ自身へのリンクが含まれているためで、このリンクには id が必要です。このリンクを削除すれば、ページは再び動作するようになります:

web/src/pages/ArticlePage.js
- import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'

const ArticlePage = () => {
return (
<>
<MetaTags title="Article" description="Article page" />

<h1>ArticlePage</h1>
<p>
Find me in <code>./web/src/pages/ArticlePage/ArticlePage.js</code>
</p>
<p>
My default route is named <code>article</code>, link to me with `
- <Link to={routes.article()}>Article</Link>`
</p>
</>
)
}

export default ArticlePage

Using the Param

OK、IDはURLの中にあります。特定のブログ記事を表示するために次に必要なものは何でしょうか?データベースからデータを取得するようですが、これはつまりセルが必要だということです。ここでは1つしか表示しないので、単数形の Article となっている点に注意してください:

yarn rw g cell Article

そうしたら、このセルを ArticlePage で使用します:

web/src/pages/ArticlePage/ArticlePage.js
import { MetaTags } from '@redwoodjs/web'
import ArticleCell from 'src/components/ArticleCell'

const ArticlePage = () => {
return (
<>
<MetaTags title="Article" description="Article page" />

<ArticleCell />
</>
)
}

export default ArticlePage

今度はセルの方ですが、データベースにあるブログ記事のIDを調べるために {id} ルートパラメータにアクセスする必要があります。本当のクエリ名 postarticle にエイリアスして、さらにいくつかのフィールドを取得しましょう:

web/src/components/ArticleCell/ArticleCell.js
export const QUERY = gql`
query ArticleQuery($id: Int!) {
article: post(id: $id) {
id
title
body
createdAt
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ article }) => {
return <div>{JSON.stringify(article)}</div>
}

OK。近づいてきました。それにしても、あの$idはどこからきたのでしょう?Redwoodにはもう一つトリックがあります。ルート内にルートパラメータを置くと、そのパラメータはルートがレンダリングするページで自動的に利用できるようになります。つまり ArticlePage を更新して次のようにできます:

web/src/pages/ArticlePage/ArticlePage.js
import { MetaTags } from '@redwoodjs/web'
import ArticleCell from 'src/components/ArticleCell'

const ArticlePage = ({ id }) => {
return (
<>
<MetaTags title="Article" description="Article page" />

<ArticleCell id={id} />
</>
)
}

export default ArticlePage

ルートパラメータに {id} という名前をつけたので id はすでに存在しています。Redwoodありがとう!しかしこの id はどのようにして $id という GraphQL パラメータになるのでしょうか?もしあなたがRedwoodについて学んだことがあるなら、Redwoodがあなたのためにそれを処理してくれていることを知るべきです。デフォルトでは、セルに与えた props は自動的に変数に変換され、クエリに渡されます。"マジか" と思うでしょう?マジです。

証明してあげます!ブラウザでブログ記事の詳細ページを見てみると......oh......んーむ:

Article error message

tip

いま見えているエラーメッセージは、セルの Failure セクションのおかげです!

Error: Variable "$id" got invalid value "1"; Int cannot represent non-integer value: "1"

ルートパラメータはURLから文字列として抽出されますが、GraphQLは id に整数を要求していることがわかりました。 ArticleCell に渡す前に parseInt() を使って数値に変換することもできますが、それよりもよい方法があります。

Route Param Types

もし、ルートのパスで型変換を要求できるとしたらどうでしょう? route param types を紹介します。既存のルートパラメータに :Int を追加するのと同じくらい簡単です:

web/src/Routes.js
<Route path="/article/{id:Int}" page={ArticlePage} name="article" />

ほらね!これは id パラメータを数値に変換してからページに渡すだけでなく、 id パスセグメントが数字だけで構成されていなければルートにマッチしないようにします。もし数字以外が見つかった場合、ルータは他のルートを試し、最終的にマッチするルートがない場合は NotFoundPage を表示します。

What if I want to pass some other prop to the cell that I don't need in the query, but do need in the Success/Loader/etc. components?

セルに与えたすべての props は、自動的にレンダーコンポーネントの props として利用できるようになります。GraphQLの変数リストにマッチするものだけがクエリに渡されます。あなたは両方の世界のベストを得ることができます!上記のブログ記事表示で、一緒に乱数を表示したい場合(チュートリアル的な意図的な理由で)、その props を渡せばいいのです:

<ArticleCell id={id} rand={Math.random()} />

そして、クエリ結果(と、必要なら元の id も)と一緒にコンポーネントに渡します:

export const Success = ({ article, id, rand }) => {
// ...
}

Redwood またまたありがとう!

Displaying a Blog Post

さて、クエリ結果をダンプする代わりに、実際のブログ記事を表示してみましょう。ホームページの記事からコピーすることもできますが、それはあまり再利用性がありません!これは古き良き時代のコンポーネントのための完璧な場所です - 表示を一度定義して、ホームページとブログ記事詳細ページでそのコンポーネントを再利用します。 ArticlesCellArticleCell の両方で新しいコンポーネントが表示されます。コンポーネントをRedwood-upしてみましょう(このフレーズは今作りました):

yarn rw g component Article

これは web/src/components/Article/Article.tsx (と、対応するテストなど!)を超シンプルなReactコンポーネントとして作成します:

web/src/components/Article/Article.js
const Article = () => {
return (
<div>
<h2>{'Article'}</h2>
<p>{'Find me in ./web/src/components/Article/Article.js'}</p>
</div>
)
}

export default Article
info

React 自体には明示的な import ステートメントがないことにお気づきかもしれません。私たち(Redwood 開発チーム)は、すべてのファイルで何度も何度もインポートするのに疲れてしまったので、あなたのために自動的にインポートするようにしました!

ArticlesCell から <article> セクションをコピーして、代わりに article 自体を props として取り込んでここに配置しましょう:

web/src/components/Article/Article.js
import { Link, routes } from '@redwoodjs/router'

const Article = ({ article }) => {
return (
<article>
<header>
<h2>
<Link to={routes.article({ id: article.id })}>{article.title}</Link>
</h2>
</header>
<div>{article.body}</div>
<div>Posted at: {article.createdAt}</div>
</article>
)
}

export default Article

そうしたら、代わりにこの新しいコンポーネントを使用するよう ArticlesCell を更新しましょう:

web/src/components/ArticlesCell/ArticlesCell.js
import Article from 'src/components/Article'

export const QUERY = gql`
query ArticlesQuery {
articles: posts {
id
title
body
createdAt
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ articles }) => {
return (
<>
{articles.map((article) => (
<Article key={article.id} article={article} />
))}
</>
)
}

最後に、ブログ記事を適切に表示するために ArticleCell を更新できます:

web/src/components/ArticleCell/ArticleCell.js
import Article from 'src/components/Article'

export const QUERY = gql`
query ArticleQuery($id: Int!) {
article: post(id: $id) {
id
title
body
createdAt
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ article }) => {
return <Article article={article} />
}

よし、いいぞ!ホームページと詳細ページを行き来できるようになりました。ブログの記事が1つしかない場合は、トップページと詳細ページが同じになります!ブログ記事管理画面へ移動して、もう2つほど作成してみましょうかね?

Article page showing an article

info

ルータが気になるなら Redwood Router ガイドにdeep diveできますよ。

Summary

おさらい:

  1. 一つのブログ記事を表示するページ(詳細ページ)を新規作成した
  2. ブログ記事の id を処理するルートを追加し、それをルートパラメータに変換し、さらにそれを整数に固定した
  3. ブログ記事を取得して表示するセルを作成した
  4. Redwoodは、コードのいくつかの重要な分岐点で id を利用できるようにし、さらにそれを自動的に数値に変換することで、世界をより良い場所にした
  5. 実際のブログ記事表示は標準的なReactコンポーネントとし、ホームページと新しい詳細ページの両方で使用した