Vue.js

Vue Pagination 구현하기.

Hoon1994 2021. 6. 15. 20:05

Vue.js

 

Vuetify를 사용하다 보니 Pagination도 v-pagination을 사용하다가, 원하던 기능이나 디자인이 커스텀되지 않는 부분이 있어서 새롭게 제작하게 됐다. 

 

v-pagination이나 구글링하면 나오는 소스들과는 조금 다른 점이 있어서 직접 개발을 하게 됐는데, 

조금 특이한 점이 있다면 이전과 다음 버튼을 누르면 보통 한 페이지씩 이동하고, 

다음 페이지 그룹으로 넘어갈 때 (1,2,3,4,5에서 6,7,8,910) 조건이 5에서 다음 버튼을 누르면 6으로 가거나 그러는데 

 

구현해야 하는 기능은 이전, 다음 버튼을 누르면 5개씩 페이지가 넘어가고, 

1페이지에서 다음을 누르든, 2든, 3,4,5 페이지에서 다음을 누르면 무조건 6으로 가고, 

마찬가지로 6,7,8,9,10 페이지에서 다음을 누르면 11페이지로 가야 하는 기능이 필요했다. 

 

<template>
  <v-row no-gutters align="center" justify="center">
    <v-btn
      v-if="buttonDisplay"
      depressed
      text
      :class="['pagination__previous-btn', { 'is-disabled': previousButtonDisabled }]"
      :disabled="previousButtonDisabled"
      @click="previous"
    >
      <img src="@/assets/images/black-arrow-right.svg" alt="arrow" class="pagination__previous-image" />이전</v-btn
    >
    <span class="pagination__divider ml-2 mr-4" v-if="buttonDisplay"></span>
    <ul class="pagination__inner">
      <li class="pagination__btn-con" v-for="number in pageList" :key="number">
        <button type="button" :class="['pagination__btn', { 'is-active': value === number }]" @click="change(number)">
          {{ number }}
        </button>
      </li>
    </ul>
    <span class="pagination__divider ml-4 mr-2" v-if="buttonDisplay"></span>
    <v-btn
      v-if="buttonDisplay"
      depressed
      text
      :class="['pagination__next-btn', { 'is-disabled': nextButtonDisabled }]"
      :disabled="nextButtonDisabled"
      @click="next"
    >
      다음 <img src="@/assets/images/black-arrow-right.svg" alt="arrow" class="pagination__next-image"
    /></v-btn>
  </v-row>
</template>

<script>
import { computed } from '@vue/composition-api';
export default {
  props: {
  // 현재 페이지 
    value: {
      type: Number,
      default: 1,
    },
  // 총 페이지 
    totalPageCount: {
      type: Number,
      default: 5,
    },
  // 보여줄 페이지 수 
    pageDisplayCount: {
      type: Number,
      default: 5,
    },
  },
  setup(props, context) {
  // 총 페이지가 5개 이하면 이전/다음 버튼을 보여주지 않음 
    const buttonDisplay = computed(() => props.totalPageCount > 5);
    
  // 현재 페이지의 그룹 번호 (현재 페이지 / 보여줄 페이지의 수)
    const currentPageGroup = computed(() => Math.ceil(props.value / props.pageDisplayCount));
    
  // 마지막 페이지 번호
    const lastPageNumber = computed(() => {
      const lastNumber = currentPageGroup.value * props.pageDisplayCount;
      if (lastNumber > props.totalPageCount) return props.totalPageCount;
      return lastNumber;
    });
    
  // 첫번째 페이지 번호
    const firstPageNumber = computed(() => {
     // 끝 번호가 26,27 이렇게 끝날 경우 페이지를 [26,27] 이렇게 보여줘야 하기에 존재하는 로직
      if (lastPageNumber.value == props.totalPageCount) {
        const multipleOfPageDisplayCount = lastPageNumber.value % props.pageDisplayCount === 0;
        return multipleOfPageDisplayCount
          ? lastPageNumber.value - props.pageDisplayCount + 1
          : lastPageNumber.value - (lastPageNumber.value % props.pageDisplayCount) + 1;
      }
      return lastPageNumber.value - (props.pageDisplayCount - 1);
    });
    
   // 페이지 리스트 (pageDisplayCount가 5일 경우 [1~5], [6~10]...)
    const pageList = computed(() => {
      const list = [];
      for (let i = firstPageNumber.value; i <= lastPageNumber.value; i++) {
        list.push(i);
      }
      return list;
    });
    
   // 다음 버튼 비활성화 조건 
    const nextButtonDisabled = computed(() => lastPageNumber.value >= props.totalPageCount);
    
  // 이전 버튼 비활성화 조건 
    const previousButtonDisabled = computed(() => firstPageNumber.value <= 1);
    
  // 사용자가 번호를 변경하는 경우 상위 컴포넌트로 값 전달
    const change = clickNumber => {
      if (clickNumber === props.value) return false;
      context.emit('change', clickNumber);
    };
  // 이전 버튼 클릭 시 이전 페이지의 첫번째 값으로 설정
    const previous = () => {
      context.emit('change', firstPageNumber.value - props.pageDisplayCount);
    };
  // 다음 버튼 클릭 시 이후 페이지의 첫번째 값으로 설정    
    const next = () => {
      context.emit('change', lastPageNumber.value + 1);
    };
    return {
      next,
      previous,
      change,
      pageList,
      buttonDisplay,
      nextButtonDisabled,
      previousButtonDisabled,
      firstPageNumber,
      lastPageNumber,
      currentPageGroup,
    };
  },
};
</script>

 

HTML, CSS 부분은 따로 설명하지 않고, 스크립트 영역만 기능 단위로 적었다.
(CSS는 디자이너분의 저작권(?)이 혹시나 문제가 될까봐 삭제!! 어차피 이미지보면 다 만들 수 있긴 한데.. ㅎㅎ)

 

아, 우선 해당 코드는 Vue2를 사용하나, Vue composition api 를 사용하고 있다. 

 

또한 Props로 받게되는 value 값과 totalPageCount는 백엔드 API에서 내려주는 값을 사용하고 있다. 

 

결론: 혹시나 비슷한 기능을 구현해야 하는 필요한 분이 계실까봐 올려봐요~ :) 

 

완성 이미지! 스타일은 직접 구현해야 합니다. :)