Contentfulで投稿ごとにnuxtのcomponentを切り替える方法(動的コンポーネントの実装)


はじめに

JAMstackにハマってnuxt, contentful, netlifyでブログなどを作っていて
contentfulの投稿ごとにnuxtで使うcomponentを切り替えたいと思うことが何度かあって、その方法を実装したのでそれについて共有できればと思いました。
nuxtの導入や、contentfulとの連携などについては、あとあと書くかもしれませんし、すでに他の方がわかりやすい記事を書かれているのでそちらを参考にしてください。
また、例として載せているコードはTypeScriptで書いています。

Contentful

Model設定

まず、Content modelにフィールドを追加します。
Add new field > Text

Nameはここではtemplate、Short text, exact searchにチェック。
Create and cofigureをクリック

Validations タブに移動し、Accept only specified valuesにチェック。

表示されるテキストボックスに任意のtemplate名を入力してEnter。
これを切り替えたいtemplate名として使用していきます。

Appearanceタブに移動し、Dropdownを選択。
Saveをクリック。

これで、Content Modelの設定は終わりです。

Contentの設定

Modelでtemplateのフィールドを追加したので、記事投稿画面にtemplateというドロップダウンのフォームが追加されています。
ここで好きなtemplate名を選んで保存します。

nuxt

いよいよnuxtでcomponentの切り替えを実装します。
まずpagesは以下のようにしています。

├── pages
│   ├── index.vue
│   └── post
│       └── _slug.vue

Vue.jsの公式で動的なコンポーネント、として説明があります。
実際のコードは以下のようにします。

/pages/post/_slug.vue
<template lang="pug">
div
  template-wrapper
    component(:is="checkTemplate")
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

import TemplateWrapper from '~/components/template/TemplateWrapper.vue'
import TemplateOne from '~/components/template/TemplateOne.vue'
import TemplateTwo from '~/components/template/TemplateTwo.vue'

@Component({
  components: {
    TemplateWrapper,
    TemplateOne,
    TemplateTwo
  }
})
export default class Slug extends Vue {
  get currentPost() {
    return this.$store.state.post.currentPost
  }
  get checkTemplate() {
    const template = this.currentPost.fields.template
    if (template === 'template1') {
      return templateOne
    } else if (template === 'Template2') {
      return TemplateTwo
    }
  }
}
</script>

<style scoped lang="scss"></style>

TemplateWrapper.vue
<template lang="pug">
  div
    slot
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

@Component({})
export default class TemplateWrapper extends Vue {}
</script>

<style scoped lang="scss"></style>
TemplateOne.vue
<template lang="pug">
  #template1
    | テンプレート1      
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

@Component()
export default class TemplateOne extends Vue {}
</script>

<style scoped lang="scss"></style>

TemplateTwo.vue
<template lang="pug">
  #template1
    | テンプレート2 
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

@Component()
export default class TemplateTwo extends Vue {}
</script>

<style scoped lang="scss"></style>

コードの解説

_slug.vue で、TemplateWrapper.vueをよびだし、Contentfulのフィールドから記事に設定したtemplateをcheckTemplate()で判定して返します。
TemplateWrapper.vueは、ほとんどなにもしません、slotにTemplateOne,Twoが挿入されます。
TemplateOne,Twoに切り替えて使いたい内容を実装していきます。

おわり

これを実装すると、WordPressの投稿テンプレート的な使い方ができるようになるので、JAMstackをもっと推せるようになるんじゃないかと思います。
動的コンポーネントについての記事は結構あったのですが、contentfulを記事ごとに切り替える方法が見つからなかったので、今回書くことにしました。
もし、こういう方法よりも、もっと簡単に実現する方法があれば教えていただきたいです。
それでは。

参考サイト

Nuxt.jsで動的コンポーネントを使って簡単に切り替え可能なモーダルを実装する