Vue 2 + typescript , paginate(ページング 部品) Laravel編

2022/06/04に公開

概要:

Vue 2 で、ページング 部品を作るメモとなります

  • コンポーネント化して、親画面から呼び出す形になります

  • props, $emit使用し、親子間双方向データ送信する方法です。

  • 表示データは固定変数にしていますが。使用時は api等で取得する形を想定しています


構成

  • Vue : 2.6.12
  • typescript : 4.7
  • Larvavel : 9.14
  • node : 14

関連


見た目。ページング部品


参考のコード

https://gist.github.com/kuc-arc-f/7707f7e9ea7c2ee73e79025a119861a6


コンポーネント

  • 子側 ,PaginateBox.vue

data 変数

  • childCurrentPage : 現在ページ位置
  • pages : 最大ページ数
  • this.$emit() の、部分で親側に、データ送信
PaginateBox.vue
<template>
    <div class="">
      <!--
      <h3>PaginateChild</h3>
      -->
      <nav class="my-2">
        <ul class="pagination">
          <li class="page-item">
            <a @click="first" class="page-link" href="#">&laquo;</a>
          </li>
          <li class="page-item">
            <a @click="prev" class="page-link" href="#">&lt;</a>
          </li>
          <li v-for="i in displayPageRange"
            class="page-item"
            :class="{active: i-1 === childCurrentPage}">
            <a @click="pageSelect(i)" class="page-link" href="#">{{ i }}</a>
          </li>
          <li class="page-item">
            <a @click="next" class="page-link" href="#">&gt;</a>
          </li>
          <li class="page-item">
            <a @click="last" class="page-link" href="#">&raquo;</a>
          </li>
        </ul>
      </nav>
    </div>   
</template>

<!-- -->
<style> 
</style>
<!-- -->
<script lang="ts">
import Vue from 'vue';
//
interface DataType {
  child_num: number,
  childCurrentPage: number,
  pages: number,
}
//
export default Vue.extend({
  props: {
    currentPage: {type: Number, default: 0},
    size: {type: Number, default: 0},
    pageRange: {type: Number, default: 0},
    items: {type: Array, default: []},
  },   
  data(): DataType {
    return {
      child_num: 120,
      childCurrentPage: 0,
      pages: 0,
    };
  }, 
  created () {
    console.log( "currentPage=", this.currentPage)
    console.log( "size=", this.size )
    console.log( "pageRange=", this.pageRange )
//    console.log( "items=", this.items)
  },
  computed: {
    /**
     * ページネーションで表示するページ番号の範囲を取得する
     */    
    displayPageRange () : any[]{
      const half = Math.ceil(this.pageRange / 2);
      let start, end;
      this.pages = this.pageCount()
      if (this.pages < this.pageRange) {
        start = 1;
        end = this.pages;
      } else if (this.currentPage < half) {
        start = 1;
        end = start + this.pageRange - 1;
      } else if (this.pages - half < this.currentPage) {
        end = this.pages;
        start = end - this.pageRange + 1;
      } else {
        start = this.currentPage - half + 1;
        end = this.currentPage + half;
      }
      let indexes = [];
      for (let i: number = start; i <= end; i++) {
        //@ts-ignore
        indexes.push(i);
      }
      return indexes;
    },    
  },
  methods: {
    /**
     * ページ数を取得する
     */    
    pageCount () : number {
      return Math.ceil(this.items.length / this.size);
    },    
    first () {
//      console.log("first");
      this.childCurrentPage = 0;
      this.selectHandler(); 
    },
    prev () {
      if (0 < this.currentPage) {
        this.childCurrentPage--;
        this.selectHandler();
      }
    },
    next () {
      this.pages = this.pageCount()
      if (this.currentPage < this.pages - 1) {
        this.childCurrentPage++;
        this.selectHandler();
      }
    },        
    last () {
      this.pages = this.pageCount()
      this.childCurrentPage = this.pages - 1;
      this.selectHandler();
    },
    pageSelect (index: number) {
      this.childCurrentPage = index - 1;
      this.selectHandler();
    },        
    selectHandler () {
      this.$emit("recvCurrentPage", this.childCurrentPage);
    },


  },

})
</script>



  • 親側、PaginateTest.vue

  • 子側PaginateBox に。props で値を渡す ($emitの処理が、わかりにくい )

  • size : 1ページに表示する件数

  • pageRange : paginateボタンの、最大数

  • items: 配列データ

  • reflectNum : 子から呼ばれる関数

  • reflectNum(value: any) の、valueに値が渡される

PaginateTest.vue
<template>
  <div class="container">
    <h3>PaginateParent</h3>
    <!--
    <p>parent_num: {{ currentPage }}</p>
    -->
    <ul class="list-group">
      <li v-for="item in displayItems" class="list-group-item">
        {{ item.text }}
      </li>
    </ul>    
    <hr />
    <PaginateBox
     :currentPage="currentPage"
     :size = "size"
     :pageRange = "pageRange"
     :items="items"
     @recvCurrentPage='reflectNum'>
    </PaginateBox>
  </div>
</template>

<!-- -->
<style> 
</style>
<!-- -->
<script lang="ts">
import Vue from 'vue';
import PaginateBox from './PaginateBox.vue';

type DataType = {
  currentPage: number
  size: number
  pageRange: number
  items: any[],
}

export default Vue.extend({
  components: { PaginateBox },
  data(): DataType {
    return {
      currentPage: 0,   // 現在のページ番号
      size: 10,         // 1ページに表示するアイテムの上限
      pageRange: 8,    // ページネーションに表示するページ数の上限
      items: [],         // 表示するアイテムリスト 
    }
  },  
  created () {
    try{
//console.log("created")
      this.items = [...Array(200)].map((_, i) => {
        return {
          text: `TestItem: ${i}`
        };
      });     
    } catch (e) {
      console.error(e);
    }    
//    console.log( "items=", this.items )
  },
  computed: {
    /**
     * 現在のページで表示するアイテムリストを取得する
     */    
    displayItems () : any[] {
      const head = this.currentPage * this.size;
      return this.items.slice(head, head + this.size);
    },    
  },
  methods: {
    reflectNum(value: any) : void {
      this.currentPage = value;
      console.log("reflectNum.parent_num", this.currentPage);
    }
  }  

})
</script>

....

Discussion