今回達成すること
前回セットアップしたモジュールcontentful
を使って、Contentfulのコンテンツを取得し、Vueファイルに表示します。
プラグイン contentful.tsをインポートする
ContentfulへAPIリクエストを投げるには、前回作成したプラグインファイルimport
します。
<script>
タグ下にimport
を追加しましょう。
pages/index.vue
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
// 追加
import client from '~/plugins/contentful'
</script>
asyncDataからAPI リクエストを行う
開発環境では、asyncData()
を使用すればサーバーサイドでAPIリクエストを行うことができます。
Contentfulの複数のコンテンツを取得する場合は、getEntries()
メソッドを使用します。
<script lang="ts">
@Component
export default class IndexPage extends Vue {
// 追加
async asyncData (): Promise<{ entries: object }> {
let entries: object = {}
if (process.server) {
await client.getEntries({
content_type: 'blogPost',
limit: 200,
order: '-fields.publishDate'
})
.then((entry: object) => {
entries = entry
})
.catch((error: object) =>
console.log(error)
)
}
return {
entries
}
}
}
</script>
-
content_type
... 取得したいモデル名を指定する。ここの値は、Contentful上のモデル名ではなく、Content type idを指定する。
-
Content type id
... Contentful管理画面 > 「Content model」 > 「該当のモデルを選択」 > 右サイドバーの「CONTENT TYPE ID」より確認できる。 -
limit
... 取得するコンテンツ数を指定する。デフォルトは100。100以上のコンテンツを作成した場合、デフォルト値のままだと全てのコンテンツが取得できない。ここはよくハマるので覚えておく。
-
order
... API取得時のコンテンツの並び順を文字列で指定する。-
をつければ降順となり、つけなければ昇順となる。 -
-fields.publishDate
... 公開日の新しい順にコンテンツを取得する。(降順)
コンテンツはブラウザをリロードした時のみに表示される
今回の設計はAPI Keyの漏洩を防ぐため、Keyをクライアントに登録していません。
開発環境のasyncData()
メソッドは、クライアントでも実行されるため、Contentfulにリクエストが行われるとAPI Keyが不正なエラーを吐きます。
そのエラーを回避するためにprocess.server
を使用し、サーバーサイドでのみリクエストを行うように分岐処理をしています。
if (process.server) {
// server only
}
コンテンツは
- ホットリロード時や
nuxt-link
のページ遷移(クライアント リクエスト)には表示されず、 - ブラウザをリロードしたタイミング(サーバー リクエスト)のみ表示されます。
目的のコンテンツはitemsに格納されている
Contentfulから取得したentry
オブジェクトには、items
という配列を返すプロパティが存在します。
その配列内にcontent_type
で指定したモデルのコンテンツリストが格納されています。
contentful レスポンス
entry: {
items: [/* Content model items */]
}
このitems
をHTMLに表示します。
pages/index.vue
<template>
<!-- 追加 -->
<h2>
Contentful API test
</h2>
<template
v-if="Object.keys(entries).length"
>
<b>
Post
</b>
<ul>
<li
v-for="(post, i) in entries.items"
:key="`post-${i}`"
>
id: {{ post.sys.id }}
<ul>
<li>
title: {{ post.fields.title }}
</li>
<li>
publishDate: {{ post.fields.publishDate }}
</li>
<li>
updatedAt: {{ post.sys.updatedAt }}
</li>
</ul>
</li>
</ul>
</template>
</v-container>
</div>
</template>
sysプロパティはContentfulが用意しているもの
sys
プロパティには
- コンテンツID
- コンテンツタイプ
- コンテンツ作成時間、更新時間
など、Contentfulが用意している値が格納されています。
fieldsプロパティは自分で用意した値が入っている
fields
プロパティは、モデルに追加したフィールドの値が格納されています。
関連付けられたモデルのプロパティ
関連付けられたモデル(blogCategoryやblogTag)は、<blogPostフィールド名>.fields.<関連モデルのフィールド名>
で取得します。
sys
プロパティは全モデル共通のプロパティです。
pages/index.vue
<!-- 追加 -->
<li>
categoryId: {{ post.fields.category.sys.id }}<br>
categoryTitle: {{ post.fields.category.fields.title }}
</li>
1対多の関連付けモデルのプロパティ
tags
などの1対多の関連付がある場合は配列が返されます。
pages/index.vue
<!-- 追加 -->
<ul
v-if="post.fields.tags"
>
<li
v-for="(tag, tagI) in post.fields.tags"
:key="`tag-${tagI}`"
>
tagId: {{ tag.sys.id }}<br>
tagTitle: {{ tag.fields.title }}
</li>
</ul>
画像やファイルなどのメディアの格納場所
メディア(画像やファイル)は、Contentfulが用意しているプロパティで取得します。
該当のデータは、<フィールド名>.fields
に格納されています。
例えば、アイキャッチ画像はimage
フィールドで登録しているので、取得するには以下のようになります。
image.fields.title
... メディアタイトル。image.fields.file
... メディアデータ。imageファイルのURLはfields.file.url
に保存されている。image.fields.file.details
... メディアの詳細データ。imageファイルのサイズなどが格納されている。
pages/index.vue
<!-- 追加 -->
<li>
<img
:src="`https://${post.fields.image.fields.file.url}`"
:alt="post.fields.image.fields.title"
:width="post.fields.image.fields.file.details.image.width / 10"
:height="post.fields.image.fields.file.details.image.height / 10"
>
</li>
コンテンツが表示されたindex.vue
最終的に
公開日が新しい順にリストされていることも確認してください。
関連モデルコンテンツはincludes.Entryに格納されている
blogPostに関連づくblogCategoryとblogTagモデルのコンテンツは、includes.Entry
の配列内に格納されています。
contentful レスポンス
entry: {
includes: {
Entry: [/* Reference model items */]
}
}
includes.Entryの使い所
includes.Entry
には1次元配列で全ての関連モデルデータが格納されているので、自分でコンテンツを整理する必要があります。
APIリクエスト回数を減らせますが、同時にJavaScriptのコードが複雑になります。
- モデル単位で
getEntries()
メソッドを実行するか includes.Entry
を使用してAPIリクエストの回数を減らすか
これはコードの複雑さにより判断してください。
今回は、2つの関連モデルを整理するだけなので「2」のincludes.Entry
を採用します。
includes.Entryの注意点
includes.Entry
を使用する注意点として、blogPostモデルに関連付けていないコンテンツは取得できません。
作成だけして、関連付けしていないカテゴリーやタグは取得できないということです。
モデルタイプを取得するプロパティ
sys
プロパティ内のcontentType
にモデルIDが格納されています。
entry.sys.contentType.sys.id
ここの値が
blogCategory
の場合 => カテゴリー配列にpushblogTag
の場合 => タグ配列にpushすることで
1次元配列の関連モデルを整理します。
関連コンテンツを表示するindex.vue
pages/index.vue
<template>
<!-- 追加 -->
<b>
Post References
</b>
<ul>
<li
v-for="(entry, i) in entries.includes.Entry"
:key="`entry-${i}`"
>
{{ entry.sys.contentType.sys.id }}<br>
{{ entry.sys.id }}<br>
{{ entry.fields.title }}
</li>
</ul>
</template>
</v-container>
</div>
</template>
関連づけしたカテゴリーとタグが取得できました。
今回の作業は以上です。
GitHubへのPushは次回に行います。
% git commit -am "Add contentful api reduest to index.vue"