Nuxt.js + TypeScriptでQueryを用いたページング処理の実装を行う

Nuxt.js + TypeScriptでQueryを用いたページング処理の実装を行う

Nuxt.js + TypeScript でページネーションが必要な一覧ページでのページング処理の実装方法について解説します。

Nuxt.jsでTypeScriptを利用する方法は以下のページを参考にしてください。
https://ja.nuxtjs.org/guide/typescript/

前提

今回の記事も参考ページにあるnuxt-property-decoratorの使用をしています。

インストール

//npmを利用している人は以下のコマンドで利用できます。
npm i -S nuxt-property-decorator 

//yarnを利用している人は以下のコマンドを利用してください。
yarn add nuxt-property-decorator

decoratorでの書き方

<script lang="ts">
import {Component, Prop, Vue} from 'nuxt-property-decorator'
import BarComponent from '~/components/BarComponent.vue'

@Component({
  components:{
    BarComponent
  }
})
export default class FooComponent extends Vue {
  @Prop({type: String,  required: true}) id!: string;
  @Prop({type: Number, default: 0}) num?: number;
  
 //dataオプション
  flag = false 
  nameArray:string[] = ['hatanaka', 'kamiya', 'yamada', 'ito']

  mounted(){
    console.log('mounted !')
  }
  
 alertError(){
   alert('エラーが発生しました。')
 }
}

</script>

nuxt-property-decoratorの使用をしている場合、
依存コンポーネントを@Componentで読み込むことができます。
サンプルではBarComponent.vueを依存コンポーネントとして読み込んでいます。

propsは@Propで定義でき、サンプルではidnumというpropsを定義しています。
また、@PropのオプションはVueのpropで指定可能なオプションは全て網羅されています。
prop(Vue公式ドキュメント)
(2019年8月現在、Nuxt.js + TypeScriptは素の状態での型チェックが甘いため、オプションを指定することをお勧めします。)

stateはclassの直下にプロパティとして定義します。
(この文法はES2015のClassではなくTypeScriptのClassでのみ定義されています。)
サンプルではflagというデフォルト値falseのものとnameArrayという文字列の配列をstateとして定義しています。

MethodやLifeCycle MethodはClassのメソッドとして定義します。
サンプルではLifeCycle Methodとしてmountedを定義し、mounted時にconsole上にmounted !と出力するように定義しています。
また、MethodとしてalertErrorを定義し、こちらは実行された際にエラーが発生しました。とアラートされるように定義しています。

nuxt-property-decoratorの基本的な使い方が確認できたら本題のページネーションの実装を見ていきましょう。 

実際のコード例

<template>
  <article class="foo-page">
    <div>
      {{pageText}}
    </div>
    <nuxt-link :to="prevPagePath">前のページへ</nuxt-link>
    <nuxt-link :to="nextPagePath">次のページへ</nuxt-link>
  </article>
</template>
​
​
<script lang="ts">
import { Component, Vue} from 'nuxt-property-decorator'
​
@Component({
  components: {},
  watchQuery: ['page']
})
export default class Foo extends Vue {
  //data
  pageNum = 0
  pageText = '初期値'
  
  //returnの内容でdataが上書きされる
  asyncData({ query }) {
     //クエリーがあればその数値を、なければ1を代入
    const pageNum = !query.page ? 1 : query.page
    //ページ番号を渡すと文字列を返してくる単純なAPI
    const data = testApi
      .fetch(pageNum)
      .then(res => {
        return {
          pageText: res.pageText,
          pageNum: 0
        }
      })
      .catch(e => {
        console.log(e)
      })
    data.pageNum = pageNum
    return data 
  }
  
  //クエリ付きのパスを返す
  get prevPagePath(){
    return '/foo?page=' + (pageNum - 1)
  }
  get nextPagePath(){
   return '/foo?page=' + (pageNum + 1)
  }
}
</script>
<template>
  <div>
    <nuxt :nuxt-child-key="$route.fullPath" />
  </div>
</template>

<script>
</script>

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

次のページへの遷移

サンプルでは、<nuxt-link >というコンポーネントを用いて他ページへのリンクを作成しています。
こちらは、toで指定したpathへリンクするaタグ要素となるのでCSSなどで見た目のカスタマイズが行えます。

nuxt-link(公式ドキュメント)

APIからのデータ取得

このページではasyncDataメソッドでapiからデータの取得を行っています。
asyncDataはSSRが行われるタイミングで実行されるようになっています。

しかし、Queryのみ更新された場合はasyncDataの再度呼び出しはされないため、
呼び出されるように設定を行う必要があります。

asyncData(公式ドキュメント)

asyncDataが再度呼び出されるようにwatchQueryを設定する。

watchQueryで指定したqueryが更新された時に全てのコンポーネントメソッドを再実行するようになります。
(asyncData、fetch、validate、layoutなど)
また、default.vueで<nuxt :nuxt-child-key="$route.fullPath" />を指定しないと再レンダリングされない場合があるようなので忘れずに指定します。

watchQuery(公式ドキュメント)

ちなみに

nuxt-property-decoratorを使用しない場合は、以下のようになります。

export default {
  watchQuery: ['page'],

  data () { 
    return { 
      pageNum : 0,
      pageText : '初期値'
    }
  }, 

  asyncData ({query}) { 
    const pageNum = !query.page ? 1 : query.page
    const data = testApi
      .fetch(pageNum)
      .then(res => {
        return {
          pageText: res.pageText,
          pageNum: 0
        }
      })
      .catch(e => {
        console.log(e)
      })
    data.pageNum = pageNum
    return data 
  }
}

まとめ

  • asyncDataやfetchメソッドを使用している時、そのままではQueryの更新で再度メソッドの呼び出しは行われない。
  • Queryの監視を行いたい場合は、watchQueryを設定する。