今回達成すること
今回は、
- タグページの作成
- タグの名前を表示
- 関連付いた記事の表示
- パンくずリストの表示
- タグ一覧ページへのリンクの設定
を行います。
タグページの完成イメージ
まずはブランチの作成
前回の編集をコミットしていない場合は、コミットしておきましょう。
$ git commit -am "top_page_add_tags"
新規ブランチを作成します。ブランチ名は「tag」とします。
$ git checkout -b tag
$ git branch
...
* tag
これでブランチの作成と移動が完了しました。
OK、次に進みましょう!
tagファイルの作成
タグ一覧ページと、タグの個別ページを作成します。
Nuxt.jsプロジェクトのルートディレクトリで、下のコマンドを実行してください。
$ mkdir pages/tags && touch pages/tags/_slug.vue && touch pages/tags/index.vue
このように、tagsディレクトリとindex.vue、_slug.vueファイルが作成されます。
app
L pages
L tags
L _slug.vue
L index.vue
タグの名前を表示する
まずはタグの名前を表示してみましょう。
pages/tags/_slug.vue
<template>
<div>
<h1>{{ tag.fields.name }}</h1>
</div>
</template>
<script>
export default {
asyncData({ payload, params, error, store, env }) {
let tag = payload
if (!tag) {
for (let i = 0; i < store.state.posts.length; i++) {
const tags = store.state.posts[i].fields.tags
if (tags) tag = tags.find(tag => tag.fields.slug === params.slug)
if (tag) break
}
}
if (tag) {
return { tag }
} else {
error({ statusCode: 400 })
}
}
}
</script>
-
let tag = payload
本番環境では、payloadにタグが格納されています。
もし、tagが存在しない場合のみ、for文でtagを検索しています。
-
for (let i = 0; i < store.state.posts.length; i++) { ...
Vuexに保存されたpostsをループで回し、さらにpostオブジェクトに関連付いたtagsを、findメソッドで検索しています。
-
if (tag) break
タグを見つけ次第、ループから抜けます。
トップページにリンクを設定する
続いてトップページに表示されているタグにリンクを設定します。
画像でいうところの赤い四角のところですね。
to
の部分を書き換えます。
pages/index.vue
<template>
...
<v-card-text
style="height: 64px;"
>
<template v-if="post.fields.tags">
<v-chip
v-for="(tag) in post.fields.tags"
:key="tag.sys.id"
:to="linkTo('tags', tag)" <!-- toの部分を書き換える -->
small
label
outlined
class="ma-1"
>
...
</template>
編集が完了したら、トップページのタグをクリックしてみてください。
URLは「tags/設定したslug」、ページにはタグの名前が表示されていると思います。
これでタグの名前は表示できました。次へ進みましょう。
(コラム)for検索 VS API通信 タグ取得はどちらが速いか?
ちなみに、下記のようにContentfulのclient.getEntries
を使って、タグを取得することもできます。
これは上のコードと全く同じ結果を返します。
pages/tags/_slug.vue (Contentfulから取得する場合)
<template>
<div>
<h1>{{ tag.fields.name }}</h1>
</div>
</template>
<script>
import client from '~/plugins/contentful'
export default {
async asyncData({ payload, params, error, store, env }) {
const tag = payload || await client.getEntries({
content_type: 'tag',
'fields.slug': params.slug
}).then(res => res.items[0]).catch(console.error)
if (tag) {
return { tag }
} else {
error({ statusCode: 400 })
}
}
}
</script>
ではfor文とclient、どちらが速いのでしょうか?
筆者は、for文の方が画面遷移の速度が速く感じました。
なので、今回はfor文でタグを検索する方法を採用しています。
ただこれは、今時点の記事の数、タグ、カテゴリーの数での速度です。
記事やタグの数が増えてきた場合は、また別の結果になる可能性もあります。
今後コンテンツが増えてきたら検討の予知があると言うことを、頭の隅っこに置いておくといいですよ。
(コラム終わり)
タグに関連する記事の取得
次は、タグに関連付く記事を取得していきます。
pages/tags/_slug.vue
<template>
<div>
<h1>{{ tag.fields.name }}</h1>
<!-- 追記 -->
<div
v-for="(post, i) in relatedPosts"
:key="i"
>
{{ post.fields.title }}
</div>
<!-- 追記終了 -->
</div>
</template>
<script>
import client from '~/plugins/contentful' // 追記
export default {
async asyncData({ payload, params, error, store, env }) { // asyncの追記
...
if (tag) {
// 追記
const relatedPosts = await client.getEntries({
content_type: env.CTF_BLOG_POST_TYPE_ID,
'fields.tags.sys.id': tag.sys.id
}).then(res => res.items).catch(console.error)
return { tag, relatedPosts } // relatedPostsの追記
} else {
error({ statusCode: 400 })
}
}
}
</script>
-
'fields.tags.sys.id': tag.sys.id
Contentfulでは、関連付くフィールド(ここで言うtags)のIDで、取得するコンテンツを絞り込むことができます。blogPostモデルのうち、tag.sys.idが一致するpostを取得しています。
さて、編集が完了したらタグページをリロードしてみてください。
下画像のように、関連付く記事のタイトルが表示されます。
これで関連付く記事の取得は完了です。
パンくずリストのセット
次はパンくずリストをセットしましょう。
パンくずリストは、この記事でグローバルコンポーネントに登録しましたね。
pages/tags/_slug.vue
<template>
<div>
<breadcrumbs :add-items="addBreads" /> <!-- 追記 -->
...
</div>
</template>
<script>
export default {
// 追記
computed: {
addBreads() {
return [{ icon: 'mdi-tag-outline', text: 'タグ一覧', to: '/tags' }]
}
},
...
}
</script>
タグ一覧のナビが表示されましたね。
あれ、でもクリックできない。。。なんで?
パンくずリストがクリックできない
結論を先に言うと、Vuetifyのv-breadcrumbs-itemコンポーネントのactive-class
プロパティに「v-breadcrumbs__item--disabled」クラスがセットされているためです。
このクラスがついていると、リンクは使用禁止状態になります。
なぜ勝手にクラスがセットされるのか?
このactive-classプロパティは、「/tags」も「/tags/ruby」もアクティブと見なすためです。
つまりアクティブリンクの判定を、URLの完全一致で行なっていないのです。
解決方法
URLの完全一致でアクティブリンクを判定するにはexact-active-class
プロパティを使います。
active-classのクラスを空にして、exact-active-classプロパティに「v-breadcrumbs__item--disabled」をセットすることで解決します。
components/ui/breadcrumbs.vue
<template>
<v-breadcrumbs :items="breadcrumbs">
<template #item="props">
<v-breadcrumbs-item
:to="props.item.to"
active-class="" <!-- active-classを空にして -->
exact-active-class="v-breadcrumbs__item--disabled" <!-- exact-active-classにセットする -->
>
...
</template>
「タグ一覧」がクリックできるようになりました。
タグ一覧ページを作る
まだタグ一覧ページを作っていないので、リンク先には何も表示されません。
pages/tags/index.vue
<template>
<div>
<breadcrumbs />
tags/index.vue
</div>
</template>
<script>
export default {
}
</script>
どうですか?
「タグ一覧」のリンクから「/tags」のページに飛べるようになりました。
おわりに
今回はここまで。
今回はタグページを作成し、関連付く記事のタイトルを表示していきました。
タグ一覧ページはリンクを確認するための仮作成なので、次回編集していきます。
さて次回は
タグ一覧ページを作り込んでいきましょう。
タグの一覧、タグに関連付いた投稿の数を表示していきます。
それではまた!