npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/components-pagination

v0.2.357-11

Published

> Component phân trang Angular standalone, hỗ trợ two-way binding, nhảy trang nhanh và debounce input.

Downloads

2,563

Readme

@libs-ui/components-pagination

Component phân trang Angular standalone, hỗ trợ two-way binding, nhảy trang nhanh và debounce input.

Giới thiệu

LibsUiComponentsPaginationComponent là standalone Angular component giúp chia nhỏ dữ liệu thành các trang và cung cấp trải nghiệm điều hướng mượt mà. Component hỗ trợ two-way binding qua model(), tự động tính toán cửa sổ trang hiển thị theo trang hiện tại, và tích hợp ô nhập nhảy trang với debounce 1500ms. Phù hợp dùng với mọi bảng dữ liệu hoặc danh sách lớn cần phân trang phía client hoặc server-side.

Tính năng

  • Two-way binding qua [(currentPage)] — đồng bộ trang hiện tại hai chiều
  • Hiển thị tổng trang — tùy chọn [showTotalPage] hiển thị nhãn "X trang"
  • Nhảy trang nhanh — ô input "Go to page" với debounce 1500ms, nhấn Enter nhảy ngay
  • Bố cục linh hoạt — vùng tổng trang & nhảy trang đặt được ở top / bottom / left / right
  • Sliding window — danh sách số trang hiển thị trượt theo trang hiện tại, tự điều chỉnh số lượng
  • Boundary protection — tự kéo currentPage về trang cuối khi dữ liệu thay đổi làm số trang giảm
  • Disable state — vô hiệu hóa toàn bộ điều hướng qua [disable]
  • Custom class — tùy chỉnh class cho container, từng nút trang và vùng thông tin phụ
  • Angular Signals + OnPush — hiệu năng cao, không re-render thừa
  • firstPageStartNumber — hỗ trợ quy ước trang bắt đầu từ 0 hoặc 1

Khi nào sử dụng

  • Khi có danh sách hoặc bảng dữ liệu lớn cần chia thành nhiều trang để tối ưu hiệu suất
  • Khi cần cung cấp điều hướng rõ ràng (Previous / Next / số trang) cho người dùng
  • Khi người dùng cần nhảy nhanh đến một trang cụ thể bằng cách nhập số trang
  • Khi cần tích hợp phân trang server-side (gọi API mới mỗi khi outPageSelect emit)

Cài đặt

npm install @libs-ui/components-pagination

Import

import { LibsUiComponentsPaginationComponent } from '@libs-ui/components-pagination';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPaginationComponent],
  // ...
})
export class YourComponent {}

Types & Interfaces

import {
  IPaginationConfig,
  IPaginationFunctionsControl,
} from '@libs-ui/components-pagination';

Ví dụ sử dụng

1. Phân trang cơ bản

// your.component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsPaginationComponent } from '@libs-ui/components-pagination';

@Component({
  selector: 'app-your',
  standalone: true,
  imports: [LibsUiComponentsPaginationComponent],
  templateUrl: './your.component.html',
})
export class YourComponent {
  protected currentPage = signal<number>(1);

  protected handlerPageSelect(page: number): void {
    event?.stopPropagation?.();
    console.log('Chuyển sang trang:', page);
    // Gọi API để load dữ liệu trang mới
  }
}
<!-- your.component.html -->
<libs_ui-components-pagination
  [totalItems]="100"
  [perPage]="10"
  [(currentPage)]="currentPage"
  (outPageSelect)="handlerPageSelect($event)" />

2. Hiển thị tổng trang và nhảy trang nhanh (bên phải)

// your.component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsPaginationComponent } from '@libs-ui/components-pagination';

@Component({
  selector: 'app-your',
  standalone: true,
  imports: [LibsUiComponentsPaginationComponent],
  templateUrl: './your.component.html',
})
export class YourComponent {
  protected currentPage = signal<number>(1);
  protected totalItems = signal<number>(500);

  protected handlerPageSelect(page: number): void {
    event?.stopPropagation?.();
    // Load dữ liệu trang mới từ API
  }
}
<!-- your.component.html -->
<libs_ui-components-pagination
  [totalItems]="totalItems()"
  [perPage]="20"
  [(currentPage)]="currentPage"
  [showTotalPage]="true"
  [showInputGotoPage]="true"
  [modeDisplayTotalPageAndGotoPage]="'right'"
  (outPageSelect)="handlerPageSelect($event)" />

3. Trang bắt đầu từ 0 (zero-based) với vùng thông tin phụ ở trên

// your.component.ts
import { Component, signal } from '@angular/core';
import { LibsUiComponentsPaginationComponent } from '@libs-ui/components-pagination';

@Component({
  selector: 'app-your',
  standalone: true,
  imports: [LibsUiComponentsPaginationComponent],
  templateUrl: './your.component.html',
})
export class YourComponent {
  protected currentPage = signal<number>(0);

  protected handlerPageSelect(page: number): void {
    event?.stopPropagation?.();
    // page = 0, 1, 2, ... (zero-based)
  }
}
<!-- your.component.html -->
<libs_ui-components-pagination
  [totalItems]="200"
  [perPage]="10"
  [(currentPage)]="currentPage"
  [firstPageStartNumber]="0"
  [showTotalPage]="true"
  [showInputGotoPage]="true"
  [modeDisplayTotalPageAndGotoPage]="'top'"
  (outPageSelect)="handlerPageSelect($event)" />

4. Disabled state

<libs_ui-components-pagination
  [totalItems]="100"
  [perPage]="10"
  [(currentPage)]="currentPage"
  [disable]="isLoading()" />

5. Tùy chỉnh class container và nút trang

<libs_ui-components-pagination
  [totalItems]="100"
  [perPage]="10"
  [(currentPage)]="currentPage"
  [numberPageDisplay]="7"
  classIncludeContainer="mt-4"
  classIncludeItem="w-[36px] h-[36px]"
  [showTotalPage]="true"
  classDisplayTotalPageAndGotoPage="gap-2"
  (outPageSelect)="handlerPageSelect($event)" />

6. Dùng FunctionsControl qua ViewChild

// parent.component.ts
import { Component, viewChild } from '@angular/core';
import {
  LibsUiComponentsPaginationComponent,
  IPaginationFunctionsControl,
} from '@libs-ui/components-pagination';

@Component({
  selector: 'app-parent',
  standalone: true,
  imports: [LibsUiComponentsPaginationComponent],
  template: `
    <libs_ui-components-pagination
      #paginationRef
      [totalItems]="totalItems()"
      [perPage]="20"
      [(currentPage)]="currentPage" />
    <button (click)="handlerResetPage()">Reset trang</button>
  `,
})
export class ParentComponent {
  private readonly paginationRef = viewChild<LibsUiComponentsPaginationComponent>('paginationRef');
  protected currentPage = signal<number>(1);
  protected totalItems = signal<number>(200);

  protected handlerResetPage(): void {
    event?.stopPropagation?.();
    this.paginationRef()?.FunctionsControl.changePage(1);
  }
}

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [totalItems] | number | Bắt buộc | Tổng số bản ghi cần phân trang | [totalItems]="500" | | [perPage] | number | Bắt buộc | Số bản ghi hiển thị trên mỗi trang | [perPage]="20" | | [(currentPage)] | number | Bắt buộc | Trang hiện tại — two-way binding qua model() | [(currentPage)]="currentPage" | | [numberPageDisplay] | number | 5 | Số lượng nút số trang tối đa hiển thị trên thanh điều hướng | [numberPageDisplay]="7" | | [showTotalPage] | boolean | undefined | Hiển thị nhãn tổng số trang (vd: "10 trang") | [showTotalPage]="true" | | [showInputGotoPage] | boolean | undefined | Hiển thị ô input để nhảy nhanh đến trang bất kỳ | [showInputGotoPage]="true" | | [modeDisplayTotalPageAndGotoPage] | 'top' \| 'bottom' \| 'left' \| 'right' | 'bottom' | Vị trí hiển thị vùng tổng trang và nhảy trang so với thanh số trang | [modeDisplayTotalPageAndGotoPage]="'right'" | | [classDisplayTotalPageAndGotoPage] | string | undefined | Class CSS tùy chỉnh cho vùng chứa thông tin tổng trang và nhảy trang | classDisplayTotalPageAndGotoPage="gap-4" | | [classIncludeContainer] | string | undefined | Class CSS tùy chỉnh cho wrapper ngoài cùng của component | classIncludeContainer="mt-4 justify-end" | | [classIncludeItem] | string | undefined | Class CSS tùy chỉnh cho từng nút số trang | classIncludeItem="w-[40px] h-[40px]" | | [disable] | boolean | undefined | Vô hiệu hóa toàn bộ điều hướng (click, nhập số trang) | [disable]="isLoading()" | | [firstPageStartNumber] | 0 \| 1 | 1 | Quy ước số trang bắt đầu: 1 = one-based (mặc định), 0 = zero-based | [firstPageStartNumber]="0" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outPageSelect) | number | Emit số trang mới mỗi khi người dùng chuyển trang (click số trang, Previous, Next, hoặc Go to page) | handlerPageSelect(page: number): void { event?.stopPropagation?.(); /* gọi API */ } | (outPageSelect)="handlerPageSelect($event)" |

FunctionsControl (ViewChild API)

Truy cập qua viewChild để điều khiển component từ bên ngoài:

private readonly paginationRef = viewChild<LibsUiComponentsPaginationComponent>('paginationRef');

// Gọi changePage với số trang cụ thể
this.paginationRef()?.FunctionsControl.changePage(3);

// Gọi changePage không có tham số → re-render trang hiện tại
this.paginationRef()?.FunctionsControl.changePage();

| Method | Signature | Mô tả | |---|---|---| | changePage | (page?: number) => Promise<void> | Chuyển đến trang page. Nếu không truyền page, re-render trang hiện tại |

Types & Interfaces

import {
  IPaginationConfig,
  IPaginationFunctionsControl,
} from '@libs-ui/components-pagination';

// IPaginationConfig — dùng để định nghĩa config tập trung
type IPaginationConfig = {
  position?: 'top' | 'bottom';
  classIncludeContainer?: string;
  classIncludeItem?: string;
  disable?: boolean;
  numberPageDisplay?: number;
  showTotalPage?: boolean;
  firstPageStartNumber?: 0 | 1;
  showInputGotoPage?: boolean;
  modeDisplayTotalPageAndGotoPage?: 'top' | 'bottom' | 'left' | 'right';
  classDisplayTotalPageAndGotoPage?: string;
  perPage?: number;
  totalItems?: number;
  currentPage?: number;
};

// IPaginationFunctionsControl — interface của FunctionsControl getter
interface IPaginationFunctionsControl {
  changePage: (page?: number) => Promise<void>;
}

Logic ẩn quan trọng

Debounced Navigation (1500ms)

Khi người dùng nhập số vào ô "Go to page", component đợi 1500ms sau lần gõ cuối mới thực hiện chuyển trang, tránh spam API. Nhấn Enter sẽ bỏ qua thời gian chờ và chuyển trang ngay lập tức.

Sliding Window (cửa sổ trang trượt)

Danh sách các nút số trang hiển thị được tính động, luôn bao quanh currentPage trong phạm vi numberPageDisplay. Khi currentPage thay đổi, cửa sổ trang trượt theo để trang hiện tại luôn nằm ở giữa (hoặc gần giữa).

Boundary Protection (giới hạn biên)

Nếu totalItems thay đổi làm giảm tổng số trang xuống thấp hơn currentPage hiện tại, component tự động kéo currentPage về trang cuối hợp lệ và emit outPageSelect.

firstPageStartNumber

  • firstPageStartNumber="1" (mặc định): currentPage nhận giá trị 1, 2, 3, ..., emit 1, 2, 3, ...
  • firstPageStartNumber="0": currentPage nhận giá trị 0, 1, 2, ..., emit 0, 1, 2, ...

Dùng firstPageStartNumber="0" khi API backend dùng quy ước zero-based pagination.

Lưu ý quan trọng

⚠️ Two-way binding bắt buộc: Luôn dùng [(currentPage)] thay vì [currentPage] một chiều. Nếu chỉ dùng một chiều, component không thể cập nhật lại số trang hiển thị khi người dùng điều hướng.

⚠️ Required inputs: totalItems, perPagecurrentPage là bắt buộc. Thiếu bất kỳ input nào sẽ khiến component không thể tính toán số trang và không render được gì.

⚠️ Debounce 1500ms: Ô "Go to page" có delay 1500ms. Nếu UX yêu cầu chuyển trang ngay lập tức khi nhập, hướng dẫn người dùng nhấn Enter sau khi nhập.

⚠️ Disable khi loading: Nên bind [disable]="isLoading()" để ngăn người dùng chuyển trang trong lúc đang gọi API, tránh race condition.