序章

Pagination は、大きな結果を別々のページに分割するためのソリューションであり、ユーザーにはこれらのページを表示するためのナビゲーションツールが提供されます。

Webアプリケーションのリソースのページ付けは、パフォーマンスの観点からだけでなく、ユーザーエクスペリエンスの観点からも非常に役立ちます。

この記事では、動的で再利用可能なVue.jsページネーションコンポーネントを作成する方法を学習します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、Node v16.4.0、npm v7.19.0、およびVuev2.6.11で検証されました。

ステップ1—プロジェクトの設定

ページ付けコンポーネントを使用すると、ユーザーは最初と最後のページに移動し、前後に移動して、至近距離のページに直接変更できるようにする必要があります。

最初のページ、前のページ、範囲のページ数、次のページ、最後のページに移動するボタンをレンダリングします。

[first] [next] [1] [2] [3] [previous] [last]

ほとんどのアプリケーションは、ユーザーがページを変更するたびにAPIリクエストを行います。 コンポーネントで許可されていることを確認する必要がありますが、コンポーネント内でリクエストを行う必要はありません。 このようにして、コンポーネントがアプリケーション全体で再利用可能であり、リクエストがすべてアクション/サービスレイヤーで行われることを確認します。 これは、ユーザーがクリックしたページの番号でイベントをトリガーすることで実現できます。

APIエンドポイントにページ付けを実装する方法はいくつかあります。

APIがレコードの総数のみを通知する場合、結果の数をページあたりの結果の数で割ることにより、ページの総数を計算できます:totalResults / perPage

この例では、APIがページあたりの結果数(perPage)、総ページ数(totalPages)、および現在のページ(currentPage)について通知するとします。 ])。 これらは私たちのダイナミックな小道具になります。

ある範囲のページをレンダリングしたいのですが、利用可能なすべてのページをレンダリングしたくはありません。 また、コンポーネント(maxVisibleButtons)で、表示されるボタンの最大数を小道具として構成できるようにします。

コンポーネントに何を実行させ、どのデータが必要になるかがわかったので、HTML構造と必要な小道具を設定できます。

@vue/cliを使用して、新しいVueプロジェクトを作成できます。

  1. npx @vue/cli create --default vue-pagination-example

次に、新しく作成されたプロジェクトディレクトリに移動します。

  1. cd vue-pagination-example

Pagination.vueファイルを作成し、コードエディタで開きます。

src / components / Pagination.vue
<template>
  <ul>
    <li>
      <button
        type="button"
      >
        First
      </button>
    </li>

    <li>
      <button
        type="button"
      >
        Previous
      </button>
    </li>

    <!-- Visible Buttons Start -->

    <!-- ... -->

    <!-- Visible Buttons End -->

    <li>
      <button
        type="button"
      >
        Next
      </button>
    </li>

    <li>
      <button
        type="button"
      >
        Last
      </button>
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    maxVisibleButtons: {
      type: Number,
      required: false,
      default: 3
    },    
    totalPages: {
      type: Number,
      required: true
    },
    perPage: {
      type: Number,
      required: true
    },
    currentPage: {
      type: Number,
      required: true
    }
  }
};
</script>

この時点で、コンポーネントはリスト内の4つのボタンをレンダリングします。 表示されるボタンの範囲を取得するために、forループを使用します。

表示されるボタンの最大数を設定する小道具よりも数が大きくならないようにする必要があります。また、使用可能なページの数よりも大きくならないようにする必要があります。

サイクルの開始番号は、現在のページによって異なります。

  1. 現在のページが最初のページになったら、現在のページと次のページをユーザーに表示しましょう。
  2. 現在のページが最後のページになったら、最後のページと前のページを表示しましょう。
  3. その間の何かについては、前のページと次のページを表示しましょう。

サイクルの終了番号にもいくつかの計算が必要です。 総ページ数と停止したい位置の間で最小の数を取得する必要があります。 停止したい位置を計算するには、開始したい位置と表示されているボタンの最大数の合計が必要です。 現在のページの左側に常に1つのボタンを表示したいので、この数値から1を引く必要があります。

表示されているページの範囲を持つオブジェクトの配列を返す計算プロパティを使用してみましょう。 各オブジェクトには、ページ番号の小道具と、ボタンを無効にするかどうかを示す別の小道具があります。 結局のところ、ユーザーが既に表示しているページをクリックすることは望ましくありません。

このページの配列をレンダリングするために、v-forディレクティブを使用します。 より複雑なデータ構造の場合は、各v-forkeyを提供することをお勧めします。 Vueはkey値を使用して、更新する必要のある要素を見つけます。この値が指定されていない場合、Vueは「インプレースパッチ」戦略を使用します。 使用しているデータは十分に単純ですが、keyの値を指定しましょう。eslint-vue-pluginvue3-essential ルールとともに使用すると、次のようになります。常にkey値を提供する必要があります。

Pagination.vueファイルに再度アクセスし、startPage()pages()を追加します。

src / components / Pagination.vue
<template>
  <ul>
    ...
    <!-- Visible Buttons Start -->

    <li
      v-for="page in pages"
      :key="page.name"
    >
      <button
        type="button"
        :disabled="page.isDisabled"
      >
        {{ page.name }}
      </button>
    </li>

    <!-- Visible Buttons End -->
    ...
  </ul>
</template>

<script>
export default {
  ...
  computed: {
    startPage() {
      // When on the first page
      if (this.currentPage === 1) {
        return 1;
      }

      // When on the last page
      if (this.currentPage === this.totalPages) {
        return this.totalPages - this.maxVisibleButtons;
      }

      // When inbetween
      return this.currentPage - 1;
    },
    pages() {
      const range = [];

      for (
        let i = this.startPage;
        i <= Math.min(this.startPage + this.maxVisibleButtons - 1, this.totalPages);
        i++
      ) {
        range.push({
          name: i,
          isDisabled: i === this.currentPage
        });
      }

      return range;
    },
  }
};
</script>

これで、表示可能な最大ボタンのロジックが終了しました。

ステップ2—イベントリスナーを追加する

次に、ユーザーがボタンをクリックしたときと、ユーザーがクリックしたボタンを親コンポーネントに通知する必要があります。

各ボタンにイベントリスナーを追加する必要があります。 v-onディレクティブを使用すると、DOMイベントをリッスンできます。 この例では、v-on省略形を使用してクリックイベントをリッスンします。

親に通知するために、 $ Emit メソッドを使用して、ページがクリックされたときにイベントを発行します。

また、ページが利用可能な場合にのみページネーションボタンがアクティブになるようにしましょう。 そのために、 v-bind を使用して、disabled属性の値を現在のページにバインドします。 v-bind.の省略形である:も使用します

テンプレートをよりクリーンに保つために、計算されたプロパティを使用して、ボタンを無効にする必要があるかどうかを確認します。 計算されたプロパティを使用すると、値もキャッシュされます。つまり、currentPageが変更されない限り、同じ計算されたプロパティに対する他のリクエストは、関数を再度実行しなくても、以前に計算された結果を返します。

Pagination.vueファイルに再度アクセスし、現在のページのチェックを追加して、イベントメソッドをクリックします。

src / components / Pagination.vue
<template>
  <ul>
    <li>
      <button
        type="button"
        @click="onClickFirstPage"
        :disabled="isInFirstPage"
      >
        First
      </button>
    </li>

    <li>
      <button
        type="button"
        @click="onClickPreviousPage"
        :disabled="isInFirstPage"
      >
        Previous
      </button>
    </li>

    <!-- Visible Buttons Start -->

    <li
      v-for="page in pages"
      :key="page.name"
    >
      <button
        type="button"
        @click="onClickPage(page.name)"
        :disabled="page.isDisabled"
      >
        {{ page.name }}
      </button>
    </li>

    <!-- Visible Buttons End -->

    <li>
      <button
        type="button"
        @click="onClickNextPage"
        :disabled="isInLastPage"
      >
        Next
      </button>
    </li>

    <li>
      <button
        type="button"
        @click="onClickLastPage"
        :disabled="isInLastPage"
      >
        Last
      </button>
    </li>
  </ul>
</template>

<script>
export default {
  ...
  computed: {
    ...
    isInFirstPage() {
      return this.currentPage === 1;
    },
    isInLastPage() {
      return this.currentPage === this.totalPages;
    },
  },
  methods: {
    onClickFirstPage() {
      this.$emit('pagechanged', 1);
    },
    onClickPreviousPage() {
      this.$emit('pagechanged', this.currentPage - 1);
    },
    onClickPage(page) {
      this.$emit('pagechanged', page);
    },
    onClickNextPage() {
      this.$emit('pagechanged', this.currentPage + 1);
    },
    onClickLastPage() {
      this.$emit('pagechanged', this.totalPages);
    }
  }
}
</script>

これで、ボタンをクリックするためのロジックが終了しました。

ステップ3—スタイルを追加する

コンポーネントが最初に必要だったすべての機能をチェックするようになったので、CSSを追加して、ページ付けコンポーネントのように見せ、リストのように見せないようにする必要があります。

また、ユーザーがどのページを表示しているかを明確に識別できるようにする必要があります。 現在のページを表すボタンの色を変更してみましょう。

そのために、オブジェクト構文を使用して、HTMLclassをアクティブなページボタンにバインドできます。 オブジェクト構文を使用してクラス名をバインドする場合、値が変更されると、Vueはクラスを自動的に切り替えます。

v-for内の各ブロックは親スコープのプロパティにアクセスできますが、テンプレートをクリーンに保つために、methodを使用してページがアクティブかどうかを確認します。

pagination.vueファイルに再度アクセスし、アクティブなページとCSSスタイルおよびクラスのチェックを追加します。

src / components / Pagination.vue
<template>
  <ul class="pagination">
    <li class="pagination-item">
      <button
        type="button"
        @click="onClickFirstPage"
        :disabled="isInFirstPage"
      >
        First
      </button>
    </li>

    <li class="pagination-item">
      <button
        type="button"
        @click="onClickPreviousPage"
        :disabled="isInFirstPage"
      >
        Previous
      </button>
    </li>

    <!-- Visible Buttons Start -->

    <li
      v-for="page in pages"
      class="pagination-item"
    >
      <button
        type="button"
        @click="onClickPage(page.name)"
        :disabled="page.isDisabled"
        :class="{ active: isPageActive(page.name) }"
      >
        {{ page.name }}
      </button>
    </li>

    <!-- Visible Buttons End -->

    <li class="pagination-item">
      <button
        type="button"
        @click="onClickNextPage"
        :disabled="isInLastPage"
      >
        Next
      </button>
    </li>

    <li class="pagination-item">
      <button
        type="button"
        @click="onClickLastPage"
        :disabled="isInLastPage"
      >
        Last
      </button>
    </li>
  </ul>
</template>

<script>
export default {
  ...
  methods: {
    ...
    isPageActive(page) {
      return this.currentPage === page;
    }
  }
}
</script>

<style>
.pagination {
  list-style-type: none;
}

.pagination-item {
  display: inline-block;
}

.active {
  background-color: #4AAE9B;
  color: #ffffff;
}
</style>

これで、アクティブなボタンのスタイリングのロジックが終了しました。

ステップ4—コンポーネントの使用

この時点で、アプリでコンポーネントを使用できます。 totalPagesperPageの値を指定して、API呼び出しをシミュレートする必要があります。

この例では、currentPage1に設定する必要があり、onPageChangeはアクティブなページからログアウトします。

src / App.vue
<template>
  <div id="app">
    <pagination
      :totalPages="10"
      :perPage="10"
      :currentPage="currentPage"
      @pagechanged="onPageChange"
    />
  </div>
</template>

<script>
import Pagination from './components/Pagination.vue'

export default {
  name: 'App',
  components: {
    Pagination
  },
  data () {
    return {
      currentPage: 1,
    };
  },
  methods: {
    onPageChange(page) {
      console.log(page)
      this.currentPage = page;
    }
  }
}
</script>

この時点で、アプリケーションを実行できます。

  1. npm run serve

ブラウザでアプリケーションを開き、ページネーションコンポーネントを確認します。 最初最後ボタンがあります。 合計10ページのうち3ページのボタンも表示されます。 最初のページが現在のページであるため、1ボタンはアクティブなクラスで示されます。 また、最初のページが現在のページであるため、最初のボタンと前のボタンは無効になっています。

ライブコードの例はCodePenで入手できます。

結論

この記事では、動的で再利用可能なVue.jsページネーションコンポーネントを作成する方法を学びました。

Vue.jsの詳細については、Vue.jsトピックページで演習とプログラミングプロジェクトを確認してください。