【Nuxt.js × Contentful】タグに関連付いたブログ記事を表示する
  • 2019.10.25に公開
  • ブログ構築
  • 7. タグ機能の構築
  • No.2 / 4
「ブログ構築」では、Nuxt.js v2.14未満が使用されています。 Nuxt.jsはv2.13~14で大きなバージョンアップがありました。それに伴い、書き方も大きく変化しています。 v2.14以上で書かれた「ブログ構築」の続編は、カテゴリー「 ブログ構築TS 」 で公開しています。

今回達成すること

今回は、

  1. タグページの作成
  2. タグの名前を表示
  3. 関連付いた記事の表示
  4. パンくずリストの表示
  5. タグ一覧ページへのリンクの設定

を行います。

タグページの完成イメージ

2019-10-20 14-06-40

まずはブランチの作成

前回の編集をコミットしていない場合は、コミットしておきましょう。

$ 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を編集します。

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

    タグを見つけ次第、ループから抜けます。

トップページにリンクを設定する

続いてトップページに表示されているタグにリンクを設定します。

画像でいうところの赤い四角のところですね。

2019-10-19 20-47-43

pages/index.vueを開いて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」、ページにはタグの名前が表示されていると思います。

2019-10-20 13-48-38

これでタグの名前は表示できました。次へ進みましょう。

(コラム)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にコードを追加していきましょう。

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を取得しています。

さて、編集が完了したらタグページをリロードしてみてください。

下画像のように、関連付く記事のタイトルが表示されます。

2019-10-20 13-53-10

これで関連付く記事の取得は完了です。

パンくずリストのセット

次はパンくずリストをセットしましょう。

パンくずリストは、この記事でグローバルコンポーネントに登録しましたね。

【Nuxt.js × Contentful】カテゴリー記事一覧ページを作成する

pages/tags/_slug.vueにcomputedを追加します。

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>

タグ一覧のナビが表示されましたね。

2019-10-20 13-59-37

あれ、でもクリックできない。。。なんで?

パンくずリストがクリックできない

結論を先に言うと、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>

「タグ一覧」がクリックできるようになりました。

2019-10-20 14-06-40

タグ一覧ページを作る

まだタグ一覧ページを作っていないので、リンク先には何も表示されません。

pages/tags/index.vueを編集しましょう。

pages/tags/index.vue
<template>
  <div>
    <breadcrumbs />
    tags/index.vue
  </div>
</template>

<script>
export default {
}
</script>

どうですか?

「タグ一覧」のリンクから「/tags」のページに飛べるようになりました。

2019-10-20 14-10-59

おわりに

今回はここまで。

今回はタグページを作成し、関連付く記事のタイトルを表示していきました。

タグ一覧ページはリンクを確認するための仮作成なので、次回編集していきます。

さて次回は

タグ一覧ページを作り込んでいきましょう。

タグの一覧、タグに関連付いた投稿の数を表示していきます。

それではまた!

あなたの力になれること
私自身が独学でプログラミングを勉強してきたので、一人で学び続ける苦しみは痛いほど分かります。そこで、当時の私がこんなのあったら良いのにな、と思っていたサービスを立ち上げました。周りに質問できる人がいない、答えの調べ方が分からない、ここを聞きたいだけなのにスクールは高額すぎる。そんな方に向けた単発・短期間メンターサービスを行っています。
独学プログラマのサービス
ブログ構築の投稿
1
  • 更新情報
  • /
  • #01
ブログ構築カテゴリーの記事修正、更新情報【2020/05/19追記: このカテゴリーの更新を一旦終了といたします】
2
  • 更新情報
  • /
  • #02
Nuxt.js v2.13.0新機能メモの公開とv2.12.2にダウングレードする方法
1
  • 今回作るアプリケーション
  • /
  • #01
Nuxt.jsとContentfulで作るマイブログ
1
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #01
Nuxt.jsを動かす環境を構築する
2
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #02
Nuxt.jsのプロジェクトを作成する
3
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #03
Hello Nuxtを表示する
1
  • Nuxt.jsアプリを公開する
  • /
  • #01
Nuxt.jsをデプロイする前の事前準備を行う
2
  • Nuxt.jsアプリを公開する
  • /
  • #02
Netlifyの初期セットアップとNuxt.jsのデプロイを行う
3
  • Nuxt.jsアプリを公開する
  • /
  • #03
NetlifyにデプロイしたNuxt.jsに独自ドメインを設定する
1
  • Contentfulのセットアップ
  • /
  • #01
【Nuxt.js Universal】Vuetify2.0にバージョンアップしよう
2
  • Contentfulのセットアップ
  • /
  • #02
【画像で説明】Contentfulの使い方。初期設定と各メニューについて学ぶ
3
  • Contentfulのセットアップ
  • /
  • #03
Contentfulにブログ記事モデルを作成していこう
4
  • Contentfulのセットアップ
  • /
  • #04
ContentfulからAPIを取得してNuxt.jsで記事一覧を表示する
1
  • ブログ記事周りの構築
  • /
  • #01
Nuxt.jsにContentfulのブログ記事を表示する
2
  • ブログ記事周りの構築
  • /
  • #02
Contentfulから取得した下書き記事を開発環境に表示する
3
  • ブログ記事周りの構築
  • /
  • #03
Nuxt.jsのgenerateプロパティに動的なルーティングを追加する
4
  • ブログ記事周りの構築
  • /
  • #04
【Nuxt.js】middlewareを活用しブログ記事取得のパフォーマンスを改善する
1
  • カテゴリーページの構築
  • /
  • #01
【Contentful】カテゴリーモデルとブログ記事モデルの関連付け
2
  • カテゴリーページの構築
  • /
  • #02
【Nuxt.js × Contentful】ブログ記事に関連付くカテゴリーを表示する
3
  • カテゴリーページの構築
  • /
  • #03
【Nuxt.js × Contentful】カテゴリー記事一覧ページを作成する
1
  • タグ機能の構築
  • /
  • #01
Contentfulにタグモデルを作成し関連付けを行う
2
  • タグ機能の構築
  • /
  • #02
【Nuxt.js × Contentful】タグに関連付いたブログ記事を表示する
3
  • タグ機能の構築
  • /
  • #03
Contentfulのincludesを使って関連モデルを取得しタグ一覧ページを作成する
4
  • タグ機能の構築
  • /
  • #04
Vuetify2のdata-tableの使い方を学んで、タグ一覧ページをレイアウト
1
  • Nuxt.jsブログカスタマイズ
  • /
  • #01
Twitterシェアボタン、フォローボタンの作り方【Nuxt.js Universalモード編】
2
  • Nuxt.jsブログカスタマイズ
  • /
  • #02
Contentfulの全文検索を使ったNuxt.jsブログ内検索の実装
独学プログラマ
独学でも、ここまでできるってよ。
CONTACT
Nuxt.js制作のご依頼は下記メールアドレスまでお送りください。