基于 Angular 2+ 的分页指令

Angular 2+ 中分页指令是有的,比如 ngx-bootstrap pagination,这里我参考了 angular2-datatable, 加入我们自己的逻辑,自己写分页指令。

HTML 页代码

<div class="row mb-3">
  <div class="col-md-4 offset-md-8">
    <div class="input-group">
      <input #searchValue type="text" (keydown.enter)="search(searchValue.value)" class="form-control" placeholder="Search by scopes you defined"/>
      <button type="button" class="btn btn-primary" (click)="search(searchValue.value)">
        <i class="fa fa-search"></i>&nbsp; Search
      </button>
      <button id="refreshBtn" type="button" class="btn btn-success" style="margin-left: 2px">
        <i class="fa fa-refresh"></i>&nbsp; refresh
      </button>
    </div>
  </div>
</div>
<table class="table table-striped" #rd="rdDataTable" [rdDataSrc]="dataSrc" [rdIsInc]="isInc" [rdFilterQuery]="filterQuery"
  [rdPagesPrefetch]="pagesPrefetch" [rdRowsOnPage]="rowsOnPage" [rdSearchScopes]="searchScopes" [rdIDCol]="idCol" [rdRefreshBtn]="'refreshBtn'"
  (selectedIds)="setSelectIds($event)" (selectedRows)="setSelectedRows($event)">
  <thead>
    <tr>
      <th style="width: 5%"></th>
      <th style="width: 10%">✔</th>
      <th style="width: 10%">
        <a class="text-nowrap">User ID</a>
      </th>
      <th style="width: 10%">
        <a class="text-nowrap">User Name</a>
      </th>
      <th style="width: 20%">
        <a class="text-nowrap">User Phone</a>
      </th>
      <th style="width: 20%">
        <a class="text-nowrap">Modi Time</a>
      </th>
      <th style="width: 25%">
        <a class="text-nowrap">User Address</a>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let item of rd.data; let i = index">
      <td>
        <label style="display: none">{{i}}</label>
      </td>
      <td>
        <input type="checkbox">
      </td>
      <td>{{item['ghu_uid']}}</td>
      <td class="text-middle">{{item['ghu_usrname']}}</td>
      <td>{{item['ghu_usrphone']}}</td>
      <td>{{item['ghu_birthdate']}}</td>
      <td>{{item['ghu_usraddr']}}</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="999">
        <app-randy-paginator [rowsOnPageSet]="rowsOnPageSet"></app-randy-paginator>
      </td>
    </tr>
  </tfoot>
</table>

解释一下,表格上方有一个搜索框,一个搜索按钮,一个刷新按钮,下来就是表格,再下来就是分页。

table 的输入以下:

变量 含义
rdDataSrc 数据来源,可以是一个URL
rdIsInc 是否是增量查询
rdFilterQuery 搜索的VALUE
rdPagesPrefetch 预取几页数据
rdRowsOnPage 每页几行数据
rdSearchScopes 搜索KEY,是个数组
rdIDCol 数据ID所在的列
rdRefreshBtn 刷新按钮的ID

table 的输出如下:

变量 含义
selectedIds 根据输入的ID,输出checkbox选中的行的ID
selectedRows 输出checkbox选中的行的数据

以下直接贴代码了

import {
  Directive, Input, EventEmitter, SimpleChange, OnChanges, DoCheck, IterableDiffers,
  IterableDiffer, Output, OnInit, HostListener, ElementRef
} from '@angular/core';
import * as _ from 'lodash';
import { RequsetStructure } from '../../../models/submit.entities';
import { SubmitService } from '../../../providers/submit.service';
import { ElectronService } from '../../../providers/electron.service';

export interface PageEvent {
  activePage: number;
  rowsOnPage: number;
  dataLength: number;
}

export interface DataEvent {
  length: number;
}

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: 'table[rdDataSrc]',
  exportAs: 'rdDataTable'
})
export class DataTableDirective implements OnChanges, DoCheck, OnInit {

  private diff: IterableDiffer<string>;
  private inputData: Array<any> = [];
  /* tslint:disable */
  @Input('rdDataSrc') public dataSrc: string;
  @Input('rdIsInc') public isInc = false;
  @Input('rdFilterQuery') public filterQuery: string;
  @Input('rdSearchScopes') public searchScopes: Array<string> = [];
  @Input('rdIDCol') public idCol: number;
  @Input('rdPagesPrefetch') public pagesPrefetch: number;
  @Input('rdRefreshBtn') public reFreshBtn: string;

  @Input('rdRowsOnPage') public rowsOnPage = 1000;
  @Input('rdActivePage') public activePage = 1;
  /* tslint:enable */
  @Output() selectedIds: EventEmitter<Set<number>> = new EventEmitter;
  @Output() selectedRows: EventEmitter<Set<object>> = new EventEmitter;

  private mustRecalculateData = false;
  public data: any[];
  public onPageChange = new EventEmitter<PageEvent>();
  public inputDataLength = 0;
  private getPageData = null;
  private readonly _selectedIds: Set<number>;
  private readonly _selectedRows: Set<object>;
  private rdArrayIndices: Set<number>;

  @HostListener('document:click', ['$event.target'])
  public onClick2(targetElement) {
    const clickedInside = this._elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      if (targetElement.getAttribute('id') === this.reFreshBtn) {
        this.getPageData(true);
      }
    }
  }

  @HostListener('click', ['$event.target']) onClick(element) {
    const checkbox = element.parentElement.querySelector('[type=checkbox]');
    const findTr = _node => {
      if (_node.nodeName === 'TR') {
        return _node;
      } else {
        return findTr(_node.parentElement);
      }
    };
    if (checkbox) {
      const tdsRef = findTr(element).querySelectorAll('td');
      const idCell = tdsRef[this.idCol];
      const indexCell = tdsRef[0].querySelector('label');
      if (!checkbox.isEqualNode(element)) {
        checkbox.checked = !checkbox.checked;
      }
      if (checkbox.checked) {
        this._selectedIds.add(+idCell.innerHTML);
        if (indexCell) {
          this._selectedRows.add(this.inputData[+indexCell.innerHTML]);
        }
      } else {
        this._selectedIds.delete(+idCell.innerHTML);
        if (indexCell) {
          this._selectedRows.delete(this.inputData[+indexCell.innerHTML]);
        }
      }
      this.selectedIds.emit(this._selectedIds);
      this.selectedRows.emit(this._selectedRows);
    }
  };

  public constructor(private differs: IterableDiffers,
    private _elementRef: ElementRef,
    private submitService: SubmitService,
    private electronService: ElectronService) {
    this.diff = differs.find([]).create(null);
    this.pagesPrefetch = 5;
    this.filterQuery = '';
    this._selectedIds = new Set<number>();
    this._selectedRows = new Set<object>();
    // this.rdArrayData = [];
    this.rdArrayIndices = new Set<number>();
  }

  ngOnInit() {
    if (this.isInc && this.electronService.containerType !== 'BROWSER') {
      // console.log('SQLite 增量');
      this.getPageData = this.getPageDataL;
    } else {
      this.getPageData = this.getPageDataR;
    }
    this.getPageData(true);
  }

  private getPageDataR = (refreshFlag: boolean = false) => {
    if (refreshFlag) {
      // this.rdArrayData = [];
      // this.rdArrayIndices = new Set<number>();
      this.rdArrayIndices.clear();
      this.inputData = [];
      this.inputDataLength = 0;
    }
    // if (!this.activePage) {
    this.activePage = 1;
    // }
    const indexBegin = (this.activePage - 1) * this.rowsOnPage;
    const indexLength = this.pagesPrefetch * this.rowsOnPage;
    const reqStructure: RequsetStructure = {
      reqParams: {
        offset: String(indexBegin),
        rowLength: String(indexLength)
      },
      withCredentials: true
    };
    if (this.filterQuery && this.filterQuery !== '') {
      reqStructure.reqParams['filterQuery'] = this.filterQuery;
      reqStructure.reqParams['searchScopes'] = JSON.stringify(this.searchScopes);
    }

    this.submitService.getHttpSubmit(this.dataSrc, true, reqStructure)
      .subscribe(data => {
        // this.inputData = [...data['respResultset']];
        let count = 0;
        const _data = [...data['respResultset']];
        const _tmp = [];
        this.inputDataLength = data['respData']['dataLength'];
        let indexEnd = indexBegin + indexLength;
        indexEnd = this.inputDataLength ? indexEnd > this.inputDataLength ? this.inputDataLength : indexEnd : indexEnd;
        for (let index = indexBegin; index < indexEnd; index++) {
          this.inputData[index] = _data[count];
          _tmp[count] = index;
          count++;
        }
        this.rdArrayIndices = new Set(Array.from(this.rdArrayIndices).concat(_tmp));
      }, error => {
        console.log(error);
      });
  };

  private getPageDataL = (refreshFlag: boolean = false) => {
    if (refreshFlag) {
      // this.rdArrayData = [];
      // this.rdArrayIndices = new Set<number>();
      this.rdArrayIndices.clear();
      this.inputData = [];
      this.inputDataLength = 0;
    }
    // if (!this.activePage) {
    this.activePage = 1;
    // }
    const indexBegin = (this.activePage - 1) * this.rowsOnPage;
    const indexLength = this.pagesPrefetch * this.rowsOnPage;
    let wStatement = '';
    if (this.filterQuery) {
      const _searchScopes = this.searchScopes.map(value => `${value} LIKE "%${this.filterQuery}%"`);
      wStatement = this.searchScopes.length > 0 && this.filterQuery !== '' ? ` WHERE ${_searchScopes.join(' OR ')}` : '';
    }
    const lStatement = ` LIMIT ${indexBegin}, ${indexLength}`;
    const queryStatement = `SELECT * FROM zz_rd_pub_hosproleinfo${wStatement}${lStatement};SELECT COUNT(*) AS total FROM zz_rd_pub_hosproleinfo${wStatement};`;
    this.electronService.execQuery(queryStatement).then(data => {
      // console.log(queryStatement);
      // console.log(data);
      if (!data[0]['tablename']) {
        let count = 0;
        this.inputDataLength = data.pop()['total'];
        const _data = [...data];
        const _tmp = [];
        let indexEnd = indexBegin + indexLength;
        indexEnd = this.inputDataLength ? indexEnd > this.inputDataLength ? this.inputDataLength : indexEnd : indexEnd;
        for (let index = indexBegin; index < indexEnd; index++) {
          this.inputData[index] = _data[count];
          _tmp[count] = index;
          count++;
        }
        this.rdArrayIndices = new Set(Array.from(this.rdArrayIndices).concat(_tmp));
      }
    })
  };

  public getPage(): PageEvent {
    return { activePage: this.activePage, rowsOnPage: this.rowsOnPage, dataLength: this.inputDataLength };
  }

  public setPage(activePage: number, rowsOnPage: number): void {
    if (this.rowsOnPage !== rowsOnPage || this.activePage !== activePage) {
      this.activePage = this.activePage !== activePage ? activePage : this.calculateNewActivePage(this.rowsOnPage, rowsOnPage);
      this.rowsOnPage = rowsOnPage;
      this.mustRecalculateData = true;

      // if (this.rdDataStorage[this.storeKey])
      {
        const indexBegin = (this.activePage - 1) * this.rowsOnPage;
        const indexEnd = indexBegin + this.rowsOnPage;
        let updateFrom = -1;
        for (let index = indexBegin; index < indexEnd; index++) {
          if (!this.rdArrayIndices.has(index)) {
            updateFrom = index;
            break;
          }
        }
        if (updateFrom > -1) {
          this.getPageData();
        } else {
          this.onPageChange.emit({
            activePage: this.activePage,
            rowsOnPage: this.rowsOnPage,
            dataLength: this.inputData ? this.inputDataLength : 0
          });
        }
      }

    }
  }

  private calculateNewActivePage(previousRowsOnPage: number, currentRowsOnPage: number): number {
    const firstRowOnPage = (this.activePage - 1) * previousRowsOnPage + 1;
    return Math.ceil(firstRowOnPage / currentRowsOnPage); // newActivePage
  }

  private recalculatePage() {
    const lastPage = Math.ceil(this.inputDataLength / this.rowsOnPage);
    this.activePage = lastPage < this.activePage ? lastPage : this.activePage;
    this.activePage = this.activePage || 1;

    this.onPageChange.emit({
      activePage: this.activePage,
      rowsOnPage: this.rowsOnPage,
      dataLength: this.inputDataLength
    });
  }

  public ngOnChanges(changes: { [key: string]: SimpleChange }): any {
    if (changes['filterQuery']) {
      this.activePage = 1;
      if (this.getPageData) {
        this.getPageData(true);
      }
    }
    if (changes['rowsOnPage']) {
      this.rowsOnPage = changes['rowsOnPage'].previousValue;
      this.setPage(this.activePage, changes['rowsOnPage'].currentValue);
      this.mustRecalculateData = true;
    }
    if (changes['inputData']) {
      this.inputData = changes['inputData'].currentValue || [];
      this.recalculatePage();
      this.mustRecalculateData = true;
    }
  }

  public ngDoCheck(): any {
    const changes = this.diff.diff(this.inputData);
    if (changes) {
      this.recalculatePage();
      this.mustRecalculateData = true;
    }
    if (this.mustRecalculateData) {
      this.fillData();
      this.mustRecalculateData = false;
    }
  }

  private fillData(): void {
    // this.activePage = this.activePage;
    // this.rowsOnPage = this.rowsOnPage;
    const offset = (this.activePage - 1) * this.rowsOnPage;
    let data = this.inputData;
    data = _.slice(data, offset, offset + this.rowsOnPage > this.inputDataLength ? this.inputDataLength : offset + this.rowsOnPage);
    this.data = data;
    // console.log(this.inputData);
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容