This repository stores code for the following DevExpress tutorial: Getting Started with DevExtreme Pagination. The project creates a UI component that allows users to navigate through pages.
## Files to Review
- jQuery
- Angular
- Vue
- React
Documentation
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
JavaScript/* eslint-disable no-console, default-case */
const total = 100;
const hexCodes = [];
const apiEndpoint = 'https://www.thecolorapi.com/id?hex=';
const cache = new Map();
function fetchData(colorId) {
return new Promise((resolve, reject) => {
if (cache.has(colorId)) {
resolve(cache.get(colorId));
} else {
$.getJSON(apiEndpoint + colorId, (data) => {
const colorData = {
image: data.image.bare,
name: data.name.value,
};
cache.set(colorId, colorData);
resolve(colorData);
}).fail(() => {
reject(new Error(`Error loading color for hex: ${colorId}`));
});
}
});
}
const getRandomPastelColor = () => {
const hue = Math.floor(Math.random() * 360);
const saturation = Math.random() * 0.4 + 0.2;
const brightness = Math.random() * 0.3 + 0.7;
return hsvToHex(hue, saturation, brightness);
};
const hsvToHex = (h, s, v) => {
let r = 0;
let g = 0;
let b = 0;
const i = Math.floor(h / 60);
const f = h / 60 - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
const toHex = (x) => {
const hex = Math.round(x * 255).toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
return toHex(r) + toHex(g) + toHex(b);
};
const renderCards = async (pageSize, pageIndex) => {
$('#cards').empty();
const startIndex = (pageIndex - 1) * pageSize;
const endIndex = pageIndex * pageSize;
const hexSubset = hexCodes.slice(startIndex, endIndex);
const promises = hexSubset.map((hex) => fetchData(hex));
try {
const pageColors = await Promise.all(promises);
pageColors.forEach((color) => {
const image = $('<img>').attr({
src: color.image,
alt: color.name,
});
$('#cards').append(image);
});
} catch (error) {
console.error('Error rendering cards:', error);
}
};
$(() => {
for (let i = 0; i < total; i += 1) {
hexCodes.push(getRandomPastelColor());
}
const loadPanel = $('#load-panel')
.dxLoadPanel({
position: {
my: 'top',
at: 'top',
of: '#cards',
},
visible: false,
showIndicator: true,
showPane: true,
hideOnOutsideClick: false,
})
.dxLoadPanel('instance');
const pagination = $('#pagination')
.dxPagination({
showInfo: true,
showNavigationButtons: true,
itemCount: total,
pageIndex: 3,
pageSize: 5,
onOptionChanged: (e) => {
if (e.name === 'pageSize' || e.name === 'pageIndex') {
const pageIndex = pagination.option('pageIndex');
const pageSize = pagination.option('pageSize');
loadPanel.show();
renderCards(pageSize, pageIndex).finally(() => loadPanel.hide());
}
},
})
.dxPagination('instance');
const pageSize = pagination.option('pageSize');
const pageIndex = pagination.option('pageIndex');
loadPanel.show();
renderCards(pageSize, pageIndex).finally(() => loadPanel.hide());
});
HTML<div class="container">
<dx-load-panel
[(visible)]="loadPanelVisible"
[showIndicator]="true"
[showPane]="true"
[hideOnOutsideClick]="false"
><dxo-position my="top" at="top" of="#cards"> </dxo-position>
</dx-load-panel>
<dx-pagination
[showInfo]="true"
[showNavigationButtons]="true"
[itemCount]="total"
[pageIndex]="pageIndex"
[pageSize]="pageSize"
(pageIndexChange)="onPageIndexChange($event)"
(pageSizeChange)="onPageSizeChange($event)"
>
</dx-pagination>
<div id="cards">
<ng-container *ngFor="let color of visibleCards">
<img [src]="color.image" [alt]="color.name" />
</ng-container>
</div>
</div>
TypeScript/* eslint-disable @typescript-eslint/no-non-null-assertion, no-else-return, no-console, no-void */
import { Component } from '@angular/core';
import { firstValueFrom } from 'rxjs';
import { ColorService, Color } from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
providers: [ColorService],
})
export class AppComponent {
loadPanelVisible = false;
total = 100;
colors: Map<string, Color> = new Map();
hexCodes: string[] = [];
visibleCards: Color[] = [];
pageIndex = 3;
pageSize = 5;
constructor(private readonly colorService: ColorService) {}
ngOnInit(): void {
this.generateHexCodes();
void this.fetchColorsForPage();
}
generateHexCodes(): void {
for (let i = 0; i < this.total; i++) {
this.hexCodes.push(this.colorService.getRandomPastelColor());
}
}
async fetchColorsForPage(): Promise<void> {
const startIndex = (this.pageIndex - 1) * this.pageSize;
const endIndex = this.pageIndex * this.pageSize;
const hexSubset = this.hexCodes.slice(startIndex, endIndex);
const promises: Promise<Color>[] = hexSubset.map((hex) => {
if (this.colors.has(hex)) {
return Promise.resolve(this.colors.get(hex)!);
} else {
return firstValueFrom(this.colorService.fetchColorData(hex)).then((data) => {
const colorData: Color = data;
this.colors.set(hex, colorData);
return colorData;
});
}
});
this.loadPanelVisible = true;
try {
const fetchedColors = await Promise.all(promises);
this.visibleCards = fetchedColors;
} catch (error) {
console.error('Error fetching colors:', error);
} finally {
this.loadPanelVisible = false;
}
}
onPageIndexChange(val: number): void {
this.pageIndex = val;
void this.fetchColorsForPage();
}
onPageSizeChange(val: number): void {
this.pageSize = val;
void this.fetchColorsForPage();
}
}
Code<template>
<div class="container">
<DxLoadPanel
v-model:visible="loadPanelVisible"
:show-indicator="true"
:show-pane="true"
:hide-on-outside-click="false"
>
<DxPosition my="top" at="top" of="#cards" />
</DxLoadPanel>
<DxPagination
:show-info="true"
:show-navigation-buttons="true"
v-model:page-index="pageIndex"
v-model:page-size="pageSize"
:item-count="total"
@update:page-index="onPageIndexChange"
@update:page-size="onPageSizeChange"
/>
<div id="cards">
<div v-for="color in visibleCards" :key="color.name">
<img :src="color.image" :alt="color.name" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import 'devextreme/dist/css/dx.light.css';
import { ref, onMounted } from 'vue';
import { fetchColorData, getRandomPastelColor } from '../assets/colorService';
import { DxPagination } from 'devextreme-vue';
import { DxLoadPanel, DxPosition } from 'devextreme-vue/load-panel';
interface Color {
image: string;
name: string;
}
const total = 100;
const pageSize = ref(5);
const pageIndex = ref(3);
const loadPanelVisible = ref(false);
const visibleCards = ref([] as Color[]);
const hexCodes = ref<string[]>([]);
const colorCache = new Map<string, Color>();
const generateHexCodes = () => {
for (let i = 0; i < total; i++) {
hexCodes.value.push(getRandomPastelColor());
}
};
const fetchColorsForPage = async () => {
loadPanelVisible.value = true;
const startIndex = (pageIndex.value - 1) * pageSize.value;
const endIndex = startIndex + pageSize.value;
const hexSubset = hexCodes.value.slice(startIndex, endIndex);
const promises = hexSubset.map((hex) => {
if (colorCache.has(hex)) {
return Promise.resolve(colorCache.get(hex));
}
return fetchColorData(hex).then((color) => {
if (color) {
colorCache.set(hex, color);
}
return color;
});
});
try {
const results = await Promise.all(promises);
visibleCards.value = results.filter((color): color is Color => color !== null);
} catch (error) {
console.error('Error fetching colors:', error);
} finally {
loadPanelVisible.value = false;
}
};
const onPageIndexChange = (value: number) => {
pageIndex.value = value;
fetchColorsForPage();
};
const onPageSizeChange = (value: number) => {
pageSize.value = value;
fetchColorsForPage();
};
onMounted(() => {
generateHexCodes();
fetchColorsForPage();
});
</script>
<style>
#cards {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
</style>