今回達成すること
カテゴリーコンテンツページに、カテゴリーに関連付く記事一覧を表示します。
関連付くブログ記事を取得する方法
カテゴリーに関連記事を取得するには、2パターン存在します。
- Vuexの
posts
にフィルターをかけ、現在のカテゴリーIDと一致する記事一覧を取得する - Vuexに、カテゴリーIDごとに振り分けた
categoryPosts
オブジェクトを用意する
「2」のcategoryPostsのイメージ
categoryPosts: {
'categoryID 1': [<categoryID 1に紐づいたposts配列>],
'categoryID 2': [<categoryID 2に紐づいたposts配列>],
}
どちらでも動きますが、今回は2の「categoryPosts
オブジェクトを用意する」を採用します。
予め整理したオブジェクトに格納した方が、色々な場所で使い回せる。というのが最大の理由です。
VuexにcategoryPostsを用意する
categoryPosts
を作成します。
categoryPostsの型を用意する
categoryPosts
は、Vuexのstate
で用意するので、
export interface BlogPost {}
の下に追加してください。
store/types/index.ts
// 追加
// カテゴリー関連posts
export type CategoryPosts = {
[key: string]: BlogPost[]
}
stateを用意する
categoryPosts
オブジェクトを用意します。
store/index.ts
// CategoryPosts 追加
import { BlogCategory, BlogPost, BlogTag, CategoryPosts } from './types'
export const state = () => ({
...
// 追加
categoryPosts: {} as CategoryPosts
})
actionメソッドを用意する
categorires
をループして、CategoryID毎にposts
のフィルターを行うgetCategoryPosts()
メソッドを追加します。
store/index.ts
export const actions = actionTree({ state, getters, mutations }, {
...
// 追加
// categoryId別のBlogPost配列を作成する
getCategoryPosts ({ state, commit }) {
const categoryPosts: CategoryPosts = {}
for (const category of state.categories) {
const categoryId: string = category.sys.id
categoryPosts[categoryId] =
state.posts.filter(post =>
post.fields.category.sys.id === categoryId
)
}
commit('setCategoryPosts', categoryPosts)
}
})
categoryPosts
... オブジェクトの中身は、{ 'categoryId': BlogPost[] }
となる。
mutationを用意する
commit
先のミューテーションを用意します。
store/index.ts
export const mutations = mutationTree(state, {
...
// 追加
setCategoryPosts (state: RootState, payload: CategoryPosts) {
state.categoryPosts = payload
}
})
nuxtServerInitからアクションを呼び出す
作成したアクションメソッドは、nuxtServerInit()
内で呼び出しサーバーサイドで処理を行います。
今回のアプリでは、
- サーバー側で必要なデータを全て用意した後に
- クライアントに処理を移行する
設計としています。
store/index.ts
export const actions = actionTree({ state, getters, mutations }, {
async nuxtServerInit ({ dispatch }, _nuxtContext: Context) {
await dispatch('getContentfulApi')
// 追加
dispatch('getCategoryPosts')
},
})
これでCategoryID毎に振り分けられたブログ記事一覧のオブジェクトが作成できました。
カテゴリー関連記事を表示しよう
pages/categories/_slug.vue
// categoryPosts 追加
type AsyncData = void | {
category: BlogCategory
categoryPosts: BlogPost[]
}
@Component
export default class CategoriesSlugPage extends Vue {
validate ({ params }: Context): boolean {
return !!params.slug
}
asyncData ({ app: { $accessor }, params, error }: Context): AsyncData {
const category: BlogCategory | undefined =
$accessor.categories.find((category: BlogCategory) =>
category.fields.slug === params.slug
)
if (!category) {
return error({
statusCode: 404,
message: 'This page could not be found'
})
}
// 追加
const categoryPosts: BlogPost[] = $accessor.categoryPosts[category.sys.id]
return {
category,
// 追加
categoryPosts
}
}
categoryPosts[category.sys.id]
... 整理したデータを用意することで、CategoryIDを指定するだけで関連記事を取得できるようになる。
<template>
内には、categoryPosts
をv-for
でループします。
pages/categories/_slug.vue
<template>
<v-container>
<nuxt-link
to="/"
>
Home
</nuxt-link>
<h2>
カテゴリー
</h2>
<div>
Category slug: {{ category.fields.slug }}
</div>
<div>
Category title: {{ category.fields.title }}
</div>
<h2>
カテゴリー関連記事
</h2>
<ul>
<li
v-for="(post, i) in categoryPosts"
:key="`post-${i}`"
>
[{{ post.fields.category.fields.title }}]
{{ post.fields.title }}
</li>
</ul>
</v-container>
</template>
カテゴリーコンテンツページを確認してみましょう。
同じカテゴリーのブログ記事一覧が表示されました。
今回の作業は以上です。
% git commit -am "Add categoryPosts for categories/_slug.vue"
まとめと次回
今回はVuexにCategoryID毎に整理した記事一覧のオブジェクトを用意し、コンテンツページに関連記事を表示しました。
次回は、NuxtのInject
機能を使って共有メソッドを置くクラスを作成します。
そしてasyncData()
内のエラーの処理を、共通メソッドとして呼び出します。
下記をInjectを使って共通メソッドとする
return error({
statusCode: 404,
message: 'This page could not be found'
})