From 64ad4efcdc0364383e76f6b3b65de7db1f23980c Mon Sep 17 00:00:00 2001 From: Vlada Skorohodova <94827090+vladaskorohodova@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:41:36 +0400 Subject: [PATCH] Pagination: add Getting Started tutorial (#6938) --- .../GettingStartedWith/Pagination/index.css | 5 + .../GettingStartedWith/Pagination/index.html | 3 + .../GettingStartedWith/Pagination/index.js | 118 ++++++++ .../00 Getting Started with Pagination.md | 13 + .../05 Create Pagination.md | 95 +++++++ .../10 Configure Pagination.md | 100 +++++++ .../00 Implement Remote Pagination.md | 243 ++++++++++++++++ .../05 Fetch Data.md | 103 +++++++ .../10 Render Items.md | 260 ++++++++++++++++++ .../15 Handle Page Size and Index Changes.md | 101 +++++++ .../20 Integrate LoadPanel.md | 215 +++++++++++++++ 11 files changed, 1256 insertions(+) create mode 100644 applications/GettingStartedWith/Pagination/index.css create mode 100644 applications/GettingStartedWith/Pagination/index.html create mode 100644 applications/GettingStartedWith/Pagination/index.js create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/00 Getting Started with Pagination.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/05 Create Pagination.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/10 Configure Pagination.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/00 Implement Remote Pagination.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/05 Fetch Data.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/10 Render Items.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/15 Handle Page Size and Index Changes.md create mode 100644 concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/20 Integrate LoadPanel.md diff --git a/applications/GettingStartedWith/Pagination/index.css b/applications/GettingStartedWith/Pagination/index.css new file mode 100644 index 0000000000..65633211db --- /dev/null +++ b/applications/GettingStartedWith/Pagination/index.css @@ -0,0 +1,5 @@ +#cards { + display: flex; + justify-content: center; + flex-wrap: wrap; +} \ No newline at end of file diff --git a/applications/GettingStartedWith/Pagination/index.html b/applications/GettingStartedWith/Pagination/index.html new file mode 100644 index 0000000000..9fed08e96d --- /dev/null +++ b/applications/GettingStartedWith/Pagination/index.html @@ -0,0 +1,3 @@ + +
+
\ No newline at end of file diff --git a/applications/GettingStartedWith/Pagination/index.js b/applications/GettingStartedWith/Pagination/index.js new file mode 100644 index 0000000000..4c323fc2d7 --- /dev/null +++ b/applications/GettingStartedWith/Pagination/index.js @@ -0,0 +1,118 @@ +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 = $('').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: 1, + 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()); +}); diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/00 Getting Started with Pagination.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/00 Getting Started with Pagination.md new file mode 100644 index 0000000000..cc7fea697b --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/00 Getting Started with Pagination.md @@ -0,0 +1,13 @@ +#include tutorials-intro-installationnote + +Pagination is a UI component that allows users to navigate through pages and change page size at runtime. + +This tutorial explains how to add Pagination to a page and configure the component's core settings. It also covers implementing remote pagination. Colored cards are loaded each time a user switches pages or changes page size. The final result is displayed below: + +
+ +Each section in this tutorial describes a single configuration step. You can also find the full source code in the following GitHub repository: + +#include btn-open-github with { + href: "https://github.com/DevExpress-Examples/devextreme-getting-started-with-pagination" +} diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/05 Create Pagination.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/05 Create Pagination.md new file mode 100644 index 0000000000..04f1d2aff2 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/05 Create Pagination.md @@ -0,0 +1,95 @@ +--- +##### jQuery +[Add DevExtreme to your jQuery application](/concepts/58%20jQuery%20Components/05%20Add%20DevExtreme%20to%20a%20jQuery%20Application/00%20Add%20DevExtreme%20to%20a%20jQuery%20Application.md '/Documentation/Guide/jQuery_Components/Add_DevExtreme_to_a_jQuery_Application/') and use the following code to create a Pagination component: + + + $(function() { + $("#pagination").dxPagination({ }); + }); + + + + + + + + + + + + + + + + +##### Angular + +[Add DevExtreme to your Angular application](/concepts/40%20Angular%20Components/10%20Getting%20Started/03%20Add%20DevExtreme%20to%20an%20Angular%20CLI%20Application '/Documentation/Guide/Angular_Components/Getting_Started/Add_DevExtreme_to_an_Angular_CLI_Application/') and use the following code to create a Pagination component: + + + + + + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] + }) + export class AppComponent { + + } + + + import { BrowserModule } from '@angular/platform-browser'; + import { NgModule } from '@angular/core'; + import { AppComponent } from './app.component'; + + import { DxPaginationModule } from 'devextreme-angular'; + + @NgModule({ + declarations: [ + AppComponent + ], + imports: [ + BrowserModule, + DxPaginationModule + ], + providers: [ ], + bootstrap: [AppComponent] + }) + export class AppModule { } + +##### Vue + +[Add DevExtreme to your Vue application](/concepts/55%20Vue%20Components/05%20Add%20DevExtreme%20to%20a%20Vue%20Application/00%20Add%20DevExtreme%20to%20a%20Vue%20Application.md '/Documentation/Guide/Vue_Components/Add_DevExtreme_to_a_Vue_Application/') and use the following code to create a Pagination component: + + + + + + +##### React + +[Add DevExtreme to your React application](/concepts/50%20React%20Components/05%20Add%20DevExtreme%20to%20a%20React%20Application/00%20Add%20DevExtreme%20to%20a%20React%20Application.md '/Documentation/Guide/React_Components/Add_DevExtreme_to_a_React_Application/') and use the following code to create a Pagination component: + + + import React from 'react'; + import 'devextreme/dist/css/dx.light.css'; + import { Pagination } from 'devextreme-react/pagination'; + + function App(): JSX.Element { + return ( + + ); + } + + export default App; + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/10 Configure Pagination.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/10 Configure Pagination.md new file mode 100644 index 0000000000..e14f961ee9 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/10 Configure Pagination.md @@ -0,0 +1,100 @@ +This tutorial step guides you through the basic Pagination setup. + +Specify the following settings: + +* [itemCount](/Documentation/ApiReference/UI_Components/dxPagination/Configuration/#itemCount) sets the total number of items. Pagination does not function properly without this setting. +* [pageIndex](/Documentation/ApiReference/UI_Components/dxPagination/Configuration/#pageIndex) sets the initial page to display. This tutorial sets **pageIndex** to 3 (the default value is 1). +* [allowedPageSizes](/Documentation/ApiReference/UI_Components/dxPagination/Configuration/#allowedPageSizes) specifies page sizes available to users. Modify this list as needed. Include `'all'` to allow users to display all items on one page. This tutorial uses the default value: `[5, 10]`. +* [pageSize](/Documentation/ApiReference/UI_Components/dxPagination/Configuration/#pageSize) specifies the initial page size. + +The following code snippet demonstrates how to apply the aforementioned settings: + +--- +##### jQuery + + + const total = 100; + $(() => { + const pagination = $('#pagination') + .dxPagination({ + showInfo: true, + showNavigationButtons: true, + itemCount: total, + pageIndex: 3, + pageSize: 5, + }) + .dxPagination('instance'); + }); + +##### Angular + + + + + + + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] + }) + export class AppComponent { + total = 100; + pageIndex = 3; + pageSize = 5; + } + +##### Vue + + + + + + +##### React + + + import React, { useState } from 'react'; + // ... + const total = 100; + + function App(): JSX.Element { + const [pageSize, setPageSize] = useState(5); + const [pageIndex, setPageIndex] = useState(3); + return ( + + ); + } + + export default App; + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/00 Implement Remote Pagination.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/00 Implement Remote Pagination.md new file mode 100644 index 0000000000..ffca7ffb24 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/00 Implement Remote Pagination.md @@ -0,0 +1,243 @@ +This section explains how to implement remote pagination. Client code generates a color list and requests a remote service for cards representing color entries on the screen. The pagination component helps users browse the resulting color cards. + +Implementation can be broken down into three steps: + +1. Generate 100 hex codes. +2. Fetch color cards from [The Color API](https://www.thecolorapi.com/) service when necessary: + - On page load + - On page size changes + - On page index changes +3. Display color cards obtained from the service. + +Implement the first step. Generate 100 random pastel hex codes and add them to an array: + +--- +##### jQuery + + + const hexCodes = []; + + 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); + }; + $(() => { + for (let i = 0; i < total; i += 1) { + hexCodes.push(getRandomPastelColor()); + } + + const pagination = $('#pagination') + .dxPagination({ + // ... + }) + .dxPagination('instance'); + }); + +##### Angular + + + import { Component } from '@angular/core'; + import { ColorService } from './app.service'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'], + providers: [ColorService], + }) + export class AppComponent { + // ... + hexCodes: string[] = []; + + constructor(private readonly colorService: ColorService) {} + + ngOnInit(): void { + this.generateHexCodes(); + } + + generateHexCodes(): void { + for (let i = 0; i < this.total; i++) { + this.hexCodes.push(this.colorService.getRandomPastelColor()); + } + } + } + + + import { Injectable } from '@angular/core'; + + @Injectable({ + providedIn: 'root', + }) + export class ColorService { + private hsvToHex(h: number, s: number, v: number): string { + 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, g, b] = [v, t, p]; break; + case 1: [r, g, b] = [q, v, p]; break; + case 2: [r, g, b] = [p, v, t]; break; + case 3: [r, g, b] = [p, q, v]; break; + case 4: [r, g, b] = [t, p, v]; break; + case 5: [r, g, b] = [v, p, q]; break; + } + + const toHex = (x: number): string => Math.round(x * 255).toString(16).padStart(2, '0'); + return `${toHex(r)}${toHex(g)}${toHex(b)}`; + } + + getRandomPastelColor(): string { + const hue = Math.floor(Math.random() * 360); + const saturation = Math.random() * 0.4 + 0.2; + const brightness = Math.random() * 0.3 + 0.7; + return this.hsvToHex(hue, saturation, brightness); + } + } + +##### Vue + + + + + + + + export const getRandomPastelColor = (): string => { + 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: number, s: number, v: number): string => { + let r: number, g: number, b: number; + const i = Math.floor(h / 60) % 6; + 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) { + case 0: [r, g, b] = [v, t, p]; break; + case 1: [r, g, b] = [q, v, p]; break; + case 2: [r, g, b] = [p, v, t]; break; + case 3: [r, g, b] = [p, q, v]; break; + case 4: [r, g, b] = [t, p, v]; break; + case 5: [r, g, b] = [v, p, q]; break; + default: throw new Error("Unexpected case in HSV to RGB conversion"); + } + + const toHex = (x: number): string => Math.round(x * 255).toString(16).padStart(2, '0'); + return `${toHex(r)}${toHex(g)}${toHex(b)}`; + }; + +##### React + + + import React, { useState, useEffect } from 'react'; + import { getRandomPastelColor } from './colorService'; + // ... + + function App(): JSX.Element { + // ... + const hexCodes = useRef([]); + + useEffect(() => { + for (let i = 0; i < total; i++) { + hexCodes.current.push(getRandomPastelColor()); + } + }, []); + + return ( + + ); + } + + export default App; + + + export const getRandomPastelColor = (): string => { + 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: number, s: number, v: number): string => { + let r: number, g: number, b: number; + const i = Math.floor(h / 60) % 6; + 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) { + case 0: [r, g, b] = [v, t, p]; break; + case 1: [r, g, b] = [q, v, p]; break; + case 2: [r, g, b] = [p, v, t]; break; + case 3: [r, g, b] = [p, q, v]; break; + case 4: [r, g, b] = [t, p, v]; break; + case 5: [r, g, b] = [v, p, q]; break; + default: throw new Error("Unexpected case in HSV to RGB conversion"); + } + + function toHex(x: number): string { + if (isNaN(x) || x < 0 || x > 1) { + return '00'; + } + return Math.round(x * 255).toString(16).padStart(2, '0'); + } + return `${toHex(r)}${toHex(g)}${toHex(b)}`; + }; + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/05 Fetch Data.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/05 Fetch Data.md new file mode 100644 index 0000000000..102e8d68a3 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/05 Fetch Data.md @@ -0,0 +1,103 @@ +The following code snippet demonstrates how to fetch data from the Color API: + +--- +##### jQuery + + + // ... + 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 pagination = $('#pagination') + .dxPagination({ + // ... + }) + .dxPagination('instance'); + }); + +##### Angular + + + import { Injectable } from '@angular/core'; + + @Injectable({ + providedIn: 'root', + }) + export class ColorService { + // ... + fetchColorData(hex: string): Observable { + return this.http.get(`${this.apiEndpoint}${hex}`) + .pipe( + catchError((error: any) => throwError(() => new Error(`Error fetching color: ${error.message || error}`))), + ) + .pipe(map((data: any) => ({ + name: data.name.value, + image: data.image.bare, + }))); + } + } + +##### Vue + + + import axios from 'axios'; + + const apiEndpoint = 'https://www.thecolorapi.com/id?hex='; + + export const fetchColorData = async ( + hex: string, + ): Promise<{ name: string; image: string } | null> => { + try { + const response = await axios.get(`${apiEndpoint}${hex}`); + return { + name: response.data.name.value, + image: response.data.image.bare, + }; + } catch (error) { + console.error(`Error fetching color for hex ${hex}:`, error); + return null; + } + }; + +##### React + + + import axios from 'axios'; + + const apiEndpoint = 'https://www.thecolorapi.com/id?hex='; + + export async function fetchColorData(hex: string): Promise<{ name: string; image: string } | null> { + try { + const response = await axios.get(`${apiEndpoint}${hex}`); + return { + name: response.data.name.value, + image: response.data.image.bare, + }; + } catch (error) { + console.error(`Error fetching color for hex ${hex}:`, error); + return null; + } + } + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/10 Render Items.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/10 Render Items.md new file mode 100644 index 0000000000..5144b41e22 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/10 Render Items.md @@ -0,0 +1,260 @@ +The render function determines the subset of cards to be displayed and populates the array with images and alt strings. + +--- +##### jQuery + + + + + + + + +
+ + + + + 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 = $('').attr({ + src: color.image, + alt: color.name, + }); + $('#cards').append(image); + }); + } catch (error) { + console.error('Error rendering cards:', error); + } + }; + + $(() => { + const pagination = $('#pagination') + .dxPagination({ + // ... + }) + .dxPagination('instance'); + + const pageSize = pagination.option('pageSize'); + const pageIndex = pagination.option('pageIndex'); + renderCards(pageSize, pageIndex); + }); + + + #cards { + display: flex; + justify-content: center; + flex-wrap: wrap; + } + +##### Angular + + + +
+ + + +
+ + + 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.css'], + providers: [ColorService], + }) + export class AppComponent { + // ... + colors: Map = new Map(); + visibleCards: Color[] = []; + + constructor(private readonly colorService: ColorService) {} + + ngOnInit(): void { + // ... + void this.fetchColorsForPage(); + } + + async fetchColorsForPage(): Promise { + const startIndex = (this.pageIndex - 1) * this.pageSize; + const endIndex = this.pageIndex * this.pageSize; + const hexSubset = this.hexCodes.slice(startIndex, endIndex); + + const promises: Promise[] = 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; + }); + } + }); + + try { + const fetchedColors = await Promise.all(promises); + this.visibleCards = fetchedColors; + } catch (error) { + console.error('Error fetching colors:', error); + } + } + } + + + // ... + export interface Color { + image: string; + name: string; + } + + + #cards { + display: flex; + justify-content: center; + flex-wrap: wrap; + } + +##### Vue + + + + + + + + +##### React + + + import React, { useState, useRef, useEffect } from 'react'; + // ... + interface Color { + image: string; + name: string; + } + + function App(): JSX.Element { + // ... + const [visibleCards, setVisibleCards] = useState([]); + const colorsCache = useRef>(new Map()); + + const fetchColorsForPage = useCallback(async (): Promise => { + const startIndex = (pageIndex - 1) * pageSize; + const endIndex = startIndex + pageSize; + const hexSubset = hexCodes.current.slice(startIndex, endIndex); + + const promises = hexSubset.map((hex) => { + if (colorsCache.current.has(hex)) { + return Promise.resolve(colorsCache.current.get(hex)); + } + return fetchColorData(hex).then((color) => { + if (color) { + colorsCache.current.set(hex, color); + } + return color; + }); + }); + + try { + const results = await Promise.all(promises); + const filteredColors = results.filter((color): color is Color => color !== null); + setVisibleCards(filteredColors); + } catch (error) { + console.error('Error fetching colors:', error); + } + }, [pageIndex, pageSize]); + + useEffect(() => { + fetchColorsForPage().catch((error) => { + console.error('Error updating visible cards:', error); + }); + }, [fetchColorsForPage]); + + return ( + +
+ {visibleCards.map((color, index) => ( +
+ {color.name} +
+ ))} +
+ ); + } + + export default App; + + + #cards { + display: flex; + justify-content: center; + flex-wrap: wrap; + } + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/15 Handle Page Size and Index Changes.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/15 Handle Page Size and Index Changes.md new file mode 100644 index 0000000000..77bc80e260 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/15 Handle Page Size and Index Changes.md @@ -0,0 +1,101 @@ +The render function is called on every page index/size [change](/Documentation/ApiReference/UI_Components/dxPagination/Configuration/#onOptionChanged): + +--- +##### jQuery + + + // ... + + $(() => { + const pagination = $('#pagination') + .dxPagination({ + // ... + onOptionChanged: (e) => { + if (e.name === 'pageSize' || e.name === 'pageIndex') { + const pageIndex = pagination.option('pageIndex'); + const pageSize = pagination.option('pageSize'); + renderCards(pageSize, pageIndex); + } + }, + }) + .dxPagination('instance'); + // ... + }); + +##### Angular + + + + + + + + // ... + export class AppComponent { + // ... + onPageIndexChange(val: number): void { + this.pageIndex = val; + void this.fetchColorsForPage(); + } + + onPageSizeChange(val: number): void { + this.pageSize = val; + void this.fetchColorsForPage(); + } + } + +##### Vue + + + + + + +##### React + + + import React, { useState, useRef, useEffect, useCallback } from 'react'; + // ... + + function App(): JSX.Element { + // ... + const onPageIndexChange = useCallback((value: number) => { + setPageIndex(value); + }, []); + + const onPageSizeChange = useCallback((value: number) => { + setPageSize(value); + }, []); + + return ( + + + ); + } + + export default App; + +--- \ No newline at end of file diff --git a/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/20 Integrate LoadPanel.md b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/20 Integrate LoadPanel.md new file mode 100644 index 0000000000..522b88d001 --- /dev/null +++ b/concepts/05 UI Components/Pagination/05 Getting Started with Pagination/15 Implement Remote Pagination/20 Integrate LoadPanel.md @@ -0,0 +1,215 @@ +The following code integrate a [load panel](/Documentation/Guide/UI_Components/LoadPanel/Overview/) into the application. The panel appears when the app requests card data from the remote service. This step is optional. + +--- +##### jQuery + +To integrate the DevExtreme LoadPanel component: + +1. Add a LoadPanel to the code. +2. [Display](/Documentation/ApiReference/UI_Components/dxLoadPanel/Methods/#show) it before calling the render function. +3. [Hide](/Documentation/ApiReference/UI_Components/dxLoadPanel/Methods/#hide) it after render. + + + + + + + + + +
+
+ + + + + $(() => { + 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({ + // ... + 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'); + // ... + loadPanel.show(); + renderCards(pageSize, pageIndex).finally(() => loadPanel.hide()); + }); + +##### Angular + +To integrate the DevExtreme LoadPanel component: + +1. Add a LoadPanel to the code. +2. [Display](/Documentation/ApiReference/UI_Components/dxLoadPanel/Configuration/#visible) it before calling the render function. +3. Hide it after render. + + + + + + + + + + + // ... + export class AppComponent { + // ... + loadPanelVisible = false; + + async fetchColorsForPage(): Promise { + const startIndex = (this.pageIndex - 1) * this.pageSize; + const endIndex = this.pageIndex * this.pageSize; + const hexSubset = this.hexCodes.slice(startIndex, endIndex); + + const promises: Promise[] = hexSubset.map((hex) => { + // ... + }); + + 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; + } + } + } + +##### Vue + +To integrate the DevExtreme LoadPanel component: + +1. Add a LoadPanel to the code. +2. [Display](/Documentation/ApiReference/UI_Components/dxLoadPanel/Configuration/#visible) it before calling the render function. +3. Hide it after render. + + + + + + + +##### React + +To integrate the DevExtreme LoadPanel component: + +1. Add a LoadPanel to the code. +2. [Display](/Documentation/ApiReference/UI_Components/dxLoadPanel/Configuration/#visible) it before calling the render function. +3. Hide it after render. + + + + // ... + import LoadPanel, { Position } from 'devextreme-react/load-panel'; + + function App(): JSX.Element { + // ... + const [loadPanelVisible, setLoadPanelVisible] = useState(false); + + const fetchColorsForPage = useCallback(async (): Promise => { + setLoadPanelVisible(true); + const startIndex = (pageIndex - 1) * pageSize; + const endIndex = startIndex + pageSize; + const hexSubset = hexCodes.current.slice(startIndex, endIndex); + + const promises = hexSubset.map((hex) => { + // ... + }); + + try { + const results = await Promise.all(promises); + const filteredColors = results.filter((color): color is Color => color !== null); + setVisibleCards(filteredColors); + } catch (error) { + console.error('Error fetching colors:', error); + } finally { + setLoadPanelVisible(false); + } + }, [pageIndex, pageSize]); + + // ... + + return ( + + + + + + ); + } + + export default App; + +--- \ No newline at end of file