[筆記][Angular 2][Angular 4][分頁] 表格分頁共用元件範例

如何把分頁控制項的部分,抽出來變成共用的元件,讓分頁的部分可以重複使用。

緣起

小喵之前的一篇『[筆記][Angular 2]透過Angular-CLI 撰寫 Angular 2針對單一資料表的CRUD』,裡面有用到了分頁的功能,搭配Service,傳遞分頁代號 (PageNum) 與分頁的筆數 (PageSize) ,以控制資料表的分頁顯示。小喵心想,這樣的功能,相信很多的地方,要顯示資料表,都會用到。如果可以抽出這部分的功能,變成共用的元件,讓其他要顯示資料的時候,可以簡單的套用該元件,這樣應該會很方便,小喵這一篇就來記錄這樣元件的範例。

Pagging Service

藉由 Service 的特性,可以在各元件 Component 之間共用共存資料的特性,我們新增一個分頁專用的 Service,把分頁需要的變數、函數,都寫在這個 Service 中,這樣一來,「顯示資料的元件」、「存取資料的 Service」、「分頁顯示控制的元件」,都可以透過 DI 將這個 Service 注入,共同使用。

Pagging Service 的相關程式碼如下:

import { Injectable } from '@angular/core';

@Injectable()
export class PaggingService {

  //分頁相關屬性開始
  currentPageNum:number=1;
  pageSize:number=5;
  totalPage:number=0;

  pageNumArray:number[]=[];   //用以存放所有分頁的數字(3頁就會是1,2,3)
  pageNumArrayMax:number=0;   //分頁數的陣列最大數
  startPg:number=0;     //分頁陣列,開始的頁數
  //分頁相關屬性結束

  constructor() { }

  //分頁相關開始
  //  設定分頁控制項的陣列
  set_pageNumArray(){
      //初始劃分頁陣列
        this.pageNumArray=[];
        let i:number=0;

        //計算起始頁
        this.startPg = Math.floor((this.currentPageNum-1)/10)*10+1;

        if(this.startPg+10>this.totalPage){
          this.pageNumArrayMax = this.totalPage;
        }
        else{
          this.pageNumArrayMax = this.startPg+10-1;
        }
        for(i=this.startPg;i<=this.pageNumArrayMax;i++){
            this.pageNumArray.push(i);
        }
  }
  //分頁相關結束

}

記得,要到 app.module.ts 裡面,設定providers的部分。

providers: [PaggingService]

Pagging Component

接著,設計分頁的元件 (PaggingComponent)。

首先,是TypeScript,由於要讓分頁的部分,徹底的與資料顯示的部分徹底拖勾,這部分就利用QueryString的方式,傳遞要顯示第幾頁,以及每一頁有幾筆的部分。

另外,透過 DI 將 PaggingService 的服務注入,這樣在html內容,就可以直接存取相關的屬性。

import { Router} from '@angular/router';
import { PaggingService } from './../pagging.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-pagging',
  templateUrl: './pagging.component.html',
  styleUrls: ['./pagging.component.css']
})
export class PaggingComponent implements OnInit {

  constructor(private svcPagging:PaggingService,private router:Router) { }

  ngOnInit() {
  }

  //設定第幾頁,透過queryParams傳遞第幾個分頁
  setPgNum(pgNum:number){
    this.svcPagging.currentPageNum = pgNum;
    this.router.navigate(['prod'],{queryParams:{pgNum:pgNum}});
  }

  //設定分頁的筆數,透過queryParams傳遞第幾個分頁,分頁的筆數
  setPgSize(){
    this.router.navigate(
      ['prod'],
      {
        queryParams:{
          pgNum:this.svcPagging.currentPageNum,
          pgSize:this.svcPagging.pageSize
        }
      }
    );
  }

}

接著,是分頁控制項的部分 (pagging.component.html) 

由於在 TypeScript 中透過 DI 將 PaggingService 注入,頁面中就可以直接存取該 Service的屬性。

    <!--分頁開始-->
    <span><button (click)="this.setPgNum(1)">第一頁</button></span>
    <span *ngIf="(this.svcPagging.startPg-10)>=1">
      <button (click)="this.setPgNum(this.svcPagging.startPg-1)">...{{this.svcPagging.startPg-1}}</button>
    </span>
    <span *ngFor="let pn of this.svcPagging.pageNumArray">
      <button (click)="this.setPgNum(pn)" [ngClass]="{currentPage: pn==this.svcPagging.currentPageNum}">{{pn}}</button>
    </span>
    <span *ngIf="svcPagging.pageNumArrayMax < svcPagging.totalPage">
      <button (click)="this.setPgNum(svcPagging.pageNumArrayMax+1)">{{svcPagging.pageNumArrayMax+1}}...</button>
    </span>
    <span><button (click)="this.setPgNum(svcPagging.totalPage)">最後一頁</button></span>
    每頁筆數:
    <select name="sltPageSize" id="sltPageSize" [(ngModel)]="svcPagging.pageSize" (change)="this.setPgSize()">
      <option value="5">5</option>
      <option value="10">10</option>
      <option value="15">15</option>
      <option value="20">20</option>
    </select>
    <!--分頁結束-->

其中,按鈕與目前的頁數一樣,用不同顏色顯示,這部分的css設定,就設定在 (pagging.component.css) ,內容如下:

.currentPage{background-color:yellow;}

 

使用範例

用北風資料庫的Product為例,小喵新增一個Service,一個Component來作為範例

  • ProdService
  • ProdComponent

ProdService

如果要比對之前的作法,可以參考之前的「[筆記][Angular 2]透過Angular-CLI 撰寫 Angular 2針對單一資料表的CRUD

import { PaggingService } from './pagging.service';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class ProdService {

  //API來源名稱
  serverName:string='http://localhost:54514/'

  //取回的內容
  datas:any=[];
  error:any;
  currentData:any={};   //被選取作用中的單筆資料
  editMode:string='';   //記錄編輯模式為何?Edit:編輯中, AddNew:新增中,空字串為非作用中,初始值為''非作用中。


  constructor(private http:Http, private svcPagging:PaggingService) { }

  GetDatasByPage(){
    let url:string=this.serverName + 'api/prod/' + this.svcPagging.currentPageNum.toString() + '/' + this.svcPagging.pageSize.toString();
    let headers = new Headers({'Content-Type':'application/json'});
    let options = new RequestOptions({headers:headers});

    this.http.get(url, options)
    .subscribe(
      (value:Response)=>{
        let jData:any;
        jData = value.json();
        this.datas = jData.oPages;
        this.svcPagging.totalPage = jData.TotalPage;
        this.svcPagging.set_pageNumArray();
      },
      (error)=>{
        this.error = error;
      }
    );
  }



}

ProdComponent

首先,是TypeScript的部分,這裡面透過 DI 注入分頁的服務 PaggingService。另外,透過ActiveRoute,在OnInit的事件中,藉由訂閱的方式,取得由 PaggingComponent透過QueryString傳遞過來的分頁代號(pgNum)與分頁筆數(pgSize),並觸發 ProdService 中的函數取得資料。

prod.component.ts

import { PaggingService } from './../pagging.service';
import { ProdService } from './../prod.service';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from "@angular/router";

@Component({
  selector: 'app-prod',
  templateUrl: './prod.component.html',
  styleUrls: ['./prod.component.css']
})
export class ProdComponent implements OnInit {

  constructor(private svcData:ProdService
    ,private svcPagging:PaggingService
    ,private route:ActivatedRoute
    ) { }

  ngOnInit() {
   this.route.queryParams.subscribe((value)=>{
      if(value['pgNum']==null){
        this.svcPagging.currentPageNum=1;
      }else{
        this.svcPagging.currentPageNum=parseInt(value['pgNum']);
      }
      //console.log(this.svcPagging.currentPageNum);
      if(value['pgSize']!=null){
        this.svcPagging.pageSize = parseInt(value['pgSize']);
      }
      this.svcData.GetDatasByPage();
    });
  }

}

顯示資料內容的html,並套用分頁控制項  <app-pagging></app-pagging>

prod.component.html

<div *ngIf="svcData.editMode==''">
  <button (click)="svcData.editMode='AddNew'">新增</button>
  <table class="table table-hover table-striped table-bordered">
    <thead>
      <tr class="info">
        <th>功能</th>
        <th>ProductID</th>
        <th>ProductName</th>
        <th>SupplierID</th>
        <th>CategoryID</th>
        <th>QuantityPerUnit</th>
        <th>UnitPrice</th>
        <th>UnitsInStock</th>
        <th>UnitsOnOrder</th>
        <th>ReorderLevel</th>
        <th>Discontinued</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let oItem of svcData.datas">
        <td>
          <button type="button" (click)="svcData.doSetcurrentData(oItem)" class="btn">編</button>
        </td>
        <td>{{oItem.ProductID}}</td>
        <td>{{oItem.ProductName}}</td>
        <td>{{oItem.SupplierID}}</td>
        <td>{{oItem.CategoryID}}</td>
        <td>{{oItem.QuantityPerUnit}}</td>
        <td>{{oItem.UnitPrice}}</td>
        <td>{{oItem.UnitsInStock}}</td>
        <td>{{oItem.UnitsOnOrder}}</td>
        <td>{{oItem.ReorderLevel}}</td>
        <td>
            <input type="checkbox" [(ngModel)]="oItem.Discontinued">
        </td>
      </tr>
    </tbody>
  </table>
    <app-pagging></app-pagging>
</div>


執行結果

切換分頁Size:

殘留課題

目前的分頁 Service與Component ,可以讓資料顯示簡單的套用。不過,目前還有個狀況是,如果單一頁面,有兩個地方需要用到這個,由於 Service 是大家共用的,所以目前的方式,只適合一個頁面中,單一資料表顯示使用。後續小喵解決這個問題,會再發表新的文章。

 

 

 


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6)