@fertyga098
FullStack js developer

Как сделать пагинацию через Vuex?

Как сделать правильно пагинацию во vue с vuex?

Что имею в данный момент:

Vuex
export default {
    state: { 
        results: [],
        totalPhotos: 0,
        perPage: 15,
        page: 1
    },
    getters: {
        allPhoto(state) {
            return state.results
        }
    },
    mutations: {
        updatePhotos( state, results ) {
            state.results = results
        }
    },
    actions: {
        async fetchCurrency( ctx, page ) {   
            const options = {
                params: {
                    page: page,
                    per_page: this.perPage
                }
            }
            
            const url = 'https://jsonplaceholder.typicode.com/photos'
    
            const res = await fetch( url, options )

            let results = await res.json()

            console.log( results )

            ctx.commit( 'updatePhotos', results )
        }
    }
}

Вывод массива
<template>
    <div>
        <div class="flex">
            <div class="container-block">
                <div class="name" v-for="result in allPhoto" :key="result.id" >
                    <img class="img" :src="result.url" />
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import { mapGetters } from "vuex"

    export default {
        computed: mapGetters( [ 'allPhoto' ] ),
        async mounted() {
            this.$store.dispatch( 'fetchCurrency' )
        },
    }
</script>

Ну и самый главный компонент
<template>
    <div>
        <app__loader v-if="loading" />
        
        <div v-else >
            <app__photo/>

            <div class="wrapper">
                <div class="body">
                    <button type="button" class="btn" > {{ previous }} </button>
                    <button type="button" class="btn" > {{ next }} </button>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import app__loader from '../under_components/Loader'
    import app__photo from '../under_components/Photo__post'

    export default {
        components: { app__loader, app__photo },
        data() {
            return {
                loading: true,
                carrency: null,
                previous: '<',
                next: '>'
            }
        },
        async mounted() {
            this.carrency =  await this.$store.dispatch( 'fetchCurrency' )
            this.loading = false
        },
    }
</script>
  • Вопрос задан
  • 565 просмотров
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
Хранилище

Какие свойства должны быть в стейте - номер текущей страницы; количество элементов на странице; общее количество элементов; данные текущей страницы; флаг, сигнализирующий о том, что в данный момент осуществляется загрузка данных (первые четыре у вас уже есть, последнее почему-то находится в компоненте - надо переместить):

state: {
  page: 0,
  perPage: 5,
  total: 0,
  posts: [],
  loading: false,
},

Понадобится геттер, вычисляющий количество страниц:

getters: {
  numPages: state => Math.ceil(state.total / state.perPage),
},

Что и как будет обновляться в стейте:

mutations: {
  updateLoading: (state, loading) => state.loading = loading,
  updatePosts: (state, { posts, total, page }) => Object.assign(state, { posts, total, page }),
},

Загрузка данных - до запроса устанавливаете loading в true, после переключаете обратно в false; запрос, сохранение результатов - тут, думаю, всё понятно:

actions: {
  async fetchPosts({ state, commit }, page) {
    commit('updateLoading', true);

    const start = (page - 1) * state.perPage;
    const end = page * state.perPage;
    const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;

    try {
      const response = await fetch(url);
      const posts = await response.json();
      const total = response.headers.get('x-total-count');
      commit('updatePosts', { posts, total, page });
    } catch (e) {
      console.error(e);
    }

    commit('updateLoading', false);
  },
},

Компонент пагинации

Получает два параметра - номер текущей страницы и количество страниц:

props: [ 'page', 'numPages' ],

Имеет методы для перехода к конкретной странице (отправляет номер новой страницы в родительский компонент - тому виднее, что с ним делать) и к странице относительно текущей:

methods: {
  goTo(page) {
    this.$emit('paginate', page);
  },
  next(step) {
    this.goTo(this.page + step);
  },
},

Чтобы не давать пользователю переходить к несуществующим страницам, сделаем вычисляемые свойства, указывающие, является ли текущая страница первой/последней,...

computed: {
  isFirst() {
    return this.page <= 1;
  },
  isLast() {
    return this.page >= this.numPages;
  },
},

...и используем эти свойства для блокировки кнопок перехода на предыдущую/следующую/первую/последнюю страницы:

<button @click="goTo(1)" :disabled="isFirst">&lt;&lt;</button>
<button @click="next(-1)" :disabled="isFirst">&lt;</button>
{{ page }} / {{ numPages }}
<button @click="next(+1)" :disabled="isLast">&gt;</button>
<button @click="goTo(numPages)" :disabled="isLast">&gt;&gt;</button>

Ну и добавим поддержку директивы v-model:

model: {
  prop: 'page',
  event: 'paginate',
},


Как всем этим пользоваться

В компоненте, из которого планируется осуществление пагинации, делаем вычисляемое свойство, значением которого является номер текущей страницы из хранилища, а при установке нового значения будет вызываться действие, загружающее данные:

computed: {
  page: {
    get() {
      return this.$store.state.page;
    },
    set(page) {
      this.$store.dispatch('fetchPosts', page);
    },
  },

Привязываем через v-model это свойство к экземпляру компонента пагинации (ну и количество страниц не забываем ему передать):

<компонент-пагинации
  v-model="page"
  :num-pages="$store.getters.numPages"
/>

Не забываем указать, какую страницу хотим видеть по умолчанию:

created() {
  this.page = 1;
},

Отображение данных:

<div v-if="$store.state.loading">данные загружаются, ждите</div>
<компонент-для-отображения-данных v-else :данные="$store.state.posts" />


https://jsfiddle.net/08hno57q/
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы