LoginSignup
8
9

More than 5 years have passed since last update.

Angular2で汎用的なPagerComponentを作ってみた

Posted at

まずは作ったファイル

pager.component.ts

@Component()内のselector, templateUrlは適宜書き換えるべし。

pager.component.ts
import { Component, Input, EventEmitter, Output, OnChanges } from '@angular/core';

@Component({
    selector: 'pager',
    templateUrl: '/pager.component.html'
})
export class PagerComponent implements OnChanges
{
    /** 現在のページ番号 */
    @Input()
    public currentPage: number;

    /** 合計アイテム数 */
    @Input()
    public totalItemCount: number;

    /** ページを切り替えた際の通知用 */
    @Output()
    public pageChanged = new EventEmitter<number>();

    /** 1ページに何個のアイテムを表示するか(初期値: 15) */
    @Input()
    public perPage: number = 15;

    /** ページャーの表示サイズ(初期値: 7) */
    @Input()
    public size: number = 7;

    /** 総ページ数 */
    public totalPages: number;

    /** 表示すべきページ番号群 */
    public displayPages: number[];

    public constructor() {}

    /**
     * プロパティバインドした変数の変更を検知し
     * totalPages、displayPagesを更新する
     * @param changes
     * @returns {void}
     */
    public ngOnChanges(changes): void
    {
        if (changes.totalItemCount) {
            this.totalPages = Math.ceil(changes.totalItemCount.currentValue / this.perPage);
            this.updateDisplayPages();
        }
    }

    /**
     * ページ番号を直接指定して移動
     * @param pageNumber
     * @returns {void}
     */
    public jump(pageNumber: number): void
    {
        this.currentPage = pageNumber;
        this.updateDisplayPages();
    }

    /**
     * 「次」ボタンを押して移動
     * @returns {void}
     */
    public next(): void
    {
        if (this.currentPage < this.totalPages) {
            this.currentPage++;
            this.updateDisplayPages();
        }
    }

    /**
     * 「前」ボタンを押して移動
     * @returns {void}
     */
    public prev(): void
    {
        if (this.currentPage > 1) {
            this.currentPage--;
            this.updateDisplayPages();
        }
    }


    /**
     * displayPagesを正しい状態に更新する
     * @returns {void}
     */
    private updateDisplayPages(): void
    {
        this.displayPages = this.calcDisplayPages();

        // EventEmitterを通じて変更を通知
        this.pageChanged.emit(this.currentPage);
    }

    /**
     * sizeを考慮して表示するべきページ配列を返却する
     * @returns {number[]}
     */
    private calcDisplayPages(): number[]
    {
        if (this.size >= this.totalPages) {
            return this.range(1, this.totalPages);
        }

        // 中間を求める(切り上げ)
        let mid = Math.ceil(this.size / 2);

        if (this.currentPage <= mid) {
            return this.range(1, this.size);
        }

        // 現在のページを中心に前後を埋める
        let zero = ((this.size - 1) % 2) === 0;

        let before, after;
        if (zero) {
            before = (this.size - 1) / 2;
            after = before;
        } else {
            before = Math.floor((this.size - 1) / 2);
            after = before + 1;
        }

        if (this.totalPages - after < this.currentPage) {
            return this.range(this.totalPages - this.size + 1, this.size);
        }

        let start = this.currentPage - before;

        return this.range(start, this.size);
    }

    /**
     * 開始値と長さを受け取り配列を生成する
     * @param start
     * @param length
     * @returns {number[]}
     */
    private range(start: number, length: number): number[]
    {
        let arr = [];
        for (let i = start; i < start + length; i++) {
            arr.push(i);
        }

        return arr;
    }

}

pager.component.html

ulliを用いたベーシックなpagerテンプレート。
現在位置のliにはactiveクラスを付与、またaspanに変更。

pager.component.html
<ul class="pagination">
    <li><a href="javascript:;" (click)="prev()">&laquo;</a></li>

    <li *ngFor="let page of displayPages" [ngClass]="{'active': page === currentPage}">
        <a href="javascript:;" *ngIf="page !== currentPage" (click)="jump(page)">{{ page }}</a>
        <span *ngIf="page === currentPage">{{ page }}</span>
    </li>

    <li><a href="javascript:;" (click)="next()">&raquo;</a></li>
</ul>

使い方

使う側のコンポーネントは例えばこんな感じ。

lists.component.ts

lists.component.ts
import { Component, OnInit } from '@angular/core';
import { PagerComponent } from './pager.component';

@Component({
    selector: 'lists',
    templateUrl: '/lists.component.html',
    directives: [
        PagerComponent
    ]
})
export class ListsComponent implements OnInit {

    public page: number;

    public totalItemCount: number;

    public constructor() {}

    public ngOnInit()
    {
        // 例えばapiよりデータを取得してきたとする
        setTimeout(() => {
            // ページ番号をセット
            this.page = 1;
            // 全アイテム件数をセット
            this.totalItemCount = 123;
        }, 1000);
    }

    public update(page: number)
    {
        // ページャーを操作した際の処理を記述
        alert(`現在${page}ページです`);
    }

}

lists.component.html

currentPagetotalItemCountは必須。perPagesizeは任意。
pageChangedにページ変更時に実行したい関数を渡す。$eventでページ番号を受け取れる。

lists.component.html
<pager
    *ngIf="totalItemCount"
    [currentPage]="page"
    [totalItemCount]="totalItemCount"
    [perPage]="10"
    (pageChanged)="update($event)"></pager>
8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9