Пагинация
При работе с большими наборами данных важно использовать пагинацию. Kodzero SDK предоставляет удобные инструменты для постраничной навигации.
Базовая пагинация
Метод findMany
Для простой пагинации используйте параметры page и perPage:
typescript
// Первая страница, 10 записей
const page1 = await User.findMany({
page: 1,
perPage: 10
})
// Вторая страница
const page2 = await User.findMany({
page: 2,
perPage: 10
})Продвинутая пагинация
Метод findManyPaginated
Для полноценной работы с пагинацией используйте findManyPaginated, который возвращает объект PaginatedResult:
typescript
const result = await User.findManyPaginated({}, 1, 25)
// Данные
console.log(result.data) // массив пользователей
// Состояние пагинации
console.log(result.state.page) // текущая страница
console.log(result.state.total) // всего документов
console.log(result.state.totalPages) // всего страниц
console.log(result.state.limit) // документов на странице
console.log(result.state.left) // осталось документовОбъект PaginatedResult
| Свойство | Тип | Описание |
|---|---|---|
data | T[] | Массив документов текущей страницы |
state.page | number | Номер текущей страницы |
state.total | number | Общее количество документов |
state.totalPages | number | Общее количество страниц |
state.limit | number | Количество документов на страницу |
state.skip | number | Количество пропущенных документов |
state.left | number | Количество оставшихся документов |
Навигация по страницам
PaginatedResult предоставляет методы для навигации:
typescript
const result = await User.findManyPaginated({}, 1, 25)
// Переход к следующей странице
await result.next()
console.log(result.data) // данные новой страницы
console.log(result.state.page) // 2
// Переход к предыдущей странице
await result.previous()
console.log(result.state.page) // 1Примеры использования
Простой список с пагинацией
typescript
async function loadUsers(page: number = 1) {
const result = await User.findManyPaginated({}, page, 10)
return {
users: result.data,
pagination: {
currentPage: result.state.page,
totalPages: result.state.totalPages,
totalItems: result.state.total,
hasNextPage: result.state.page < result.state.totalPages,
hasPrevPage: result.state.page > 1
}
}
}React компонент с пагинацией
tsx
import { useState, useEffect } from 'react'
function UserList() {
const [paginatedResult, setPaginatedResult] = useState(null)
const [loading, setLoading] = useState(false)
const loadPage = async (page: number) => {
setLoading(true)
const result = await User.findManyPaginated({}, page, 10)
setPaginatedResult(result)
setLoading(false)
}
useEffect(() => {
loadPage(1)
}, [])
if (loading) return <div>Загрузка...</div>
if (!paginatedResult) return null
const { data, state } = paginatedResult
return (
<div>
{/* Список пользователей */}
<ul>
{data.map(user => (
<li key={user._id}>{user.name}</li>
))}
</ul>
{/* Пагинация */}
<div className="pagination">
<button
disabled={state.page === 1}
onClick={() => loadPage(state.page - 1)}
>
Назад
</button>
<span>
Страница {state.page} из {state.totalPages}
</span>
<button
disabled={state.page >= state.totalPages}
onClick={() => loadPage(state.page + 1)}
>
Вперёд
</button>
</div>
<p>Всего записей: {state.total}</p>
</div>
)
}Бесконечная прокрутка
typescript
class InfiniteList {
private result: PaginatedResult<User> | null = null
private allItems: User[] = []
async loadInitial() {
this.result = await User.findManyPaginated({}, 1, 20)
this.allItems = [...this.result.data]
return this.allItems
}
async loadMore() {
if (!this.result || !this.hasMore()) {
return this.allItems
}
await this.result.next()
this.allItems = [...this.allItems, ...this.result.data]
return this.allItems
}
hasMore(): boolean {
if (!this.result) return false
return this.result.state.page < this.result.state.totalPages
}
get items() {
return this.allItems
}
}
// Использование
const list = new InfiniteList()
await list.loadInitial()
// При скролле вниз
if (list.hasMore()) {
await list.loadMore()
}Vue 3 composable
typescript
import { ref, computed } from 'vue'
export function usePagination<T>(
model: any,
options = {},
perPage = 10
) {
const result = ref<PaginatedResult<T> | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const items = computed(() => result.value?.data || [])
const currentPage = computed(() => result.value?.state.page || 1)
const totalPages = computed(() => result.value?.state.totalPages || 0)
const total = computed(() => result.value?.state.total || 0)
const hasNext = computed(() => currentPage.value < totalPages.value)
const hasPrev = computed(() => currentPage.value > 1)
async function loadPage(page: number) {
loading.value = true
error.value = null
try {
result.value = await model.findManyPaginated(options, page, perPage)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
async function next() {
if (hasNext.value) {
await loadPage(currentPage.value + 1)
}
}
async function prev() {
if (hasPrev.value) {
await loadPage(currentPage.value - 1)
}
}
return {
items,
currentPage,
totalPages,
total,
hasNext,
hasPrev,
loading,
error,
loadPage,
next,
prev
}
}
// Использование в компоненте
const {
items,
currentPage,
totalPages,
loading,
next,
prev,
loadPage
} = usePagination(User, { sort: '-createdAt' }, 10)
onMounted(() => loadPage(1))Комбинирование с фильтрами
Пагинацию можно комбинировать с другими параметрами запроса:
typescript
const result = await Product.findManyPaginated(
{
search: 'ноутбук',
sort: '-price',
fields: ['name', 'price', 'category']
},
1, // страница
20 // на страницу
)