ngx-datatable 的通用“包装”组件

2023-12-19

一些介绍:

我们目前正在开发一个基于 Angular2 的应用程序,该应用程序数据量很大。为了显示这些数据,我们决定给出ngx-数据表 https://github.com/swimlane/ngx-datatable尝试一下。 需要大量组件来显示网格中的数据。我们添加了一个自定义的页脚模板以及一种自定义的页眉,使用<select>元素。

标记行的数量增长了很多,因此我们想移动定义<ngx-datatable>将页眉和页脚添加到单独的网格组件中。现在,我们希望通过允许开发人员使用网格简单地在标记中定义列来重用该组件,以便在涉及列内容时具有完全的灵活性。

这个想法是有一个常用的网格组件,只要求数据作为输入并呈现它。网格中的典型功能(服务器端排序和分页)应该只在网格组件中存在一次。 使用网格组件的组件应该只提供网格组件订阅的数据,仅此而已。

我们目前拥有的:

通用网格组件,其选择器“grid”在 .ts 文件中定义

<div class="gridheader">
    ... page size selector and other elements ...
</div>
<ngx-datatable
  class="material"
  [columnMode]="'force'"
  [rows]="data"
  [headerHeight]="'auto'"
  [footerHeight]="'auto'"
  [rowHeight]="'auto'"
  [externalPaging]="true"
  [externalSorting]="true"
  [count]="totalElements"
  [offset]="currentPageNumber"
  [limit]="pageSize"
  [loadingIndicator]="isLoading"
  (page)='loadPage($event)'
  (sort)="onSort($event)">

  <ng-content>
  </ng-content>

  <ngx-datatable-footer>
    <ng-template 
      ngx-datatable-footer-template 
      let-rowCount="rowCount"
      let-pageSize="pageSize"
      let-selectedCount="selectedCount"
      let-curPage="curPage"
      let-offset="offset">
      <div style="padding: 5px 10px">
        <div>
          <strong>Summary</strong>: Gender: Female
        </div>
        <hr style="width:100%" />
        <div>
          Rows: {{rowCount}} |
          Size: {{pageSize}} |
          Current: {{curPage}} |
          Offset: {{offset}}
        </div>
      </div>
    </ng-template>
  </ngx-datatable-footer>

</ngx-datatable>

具体网格

<grid (onFetchDataRequired)="fetchDataRequired($event)">

  <ngx-datatable-column prop="Id" name=" ">
    <ng-template let-value="value" ngx-datatable-cell-template>
      <a [routerLink]="['edit', value]" class="btn btn-sm btn-outline-primary">
        <i class="fa fa-pencil" aria-hidden="true"></i>
      </a>
    </ng-template>
  </ngx-datatable-column>
  <ngx-datatable-column name="CreatedBy" prop="CreatedBy">
    <ng-template let-value="value" ngx-datatable-cell-template>
      {{value}}
    </ng-template>
  </ngx-datatable-column>

  ... even more columns ...   

</grid>

我们尝试使用<ng-content></ng-content>对于列,但运气不好,网格只是没有渲染,我猜是因为没有定义任何列。

有没有一种方法可以不一遍又一遍地重复网格定义的相同代码,并实现某种处理通用标记的包装器?

感谢您的任何投入。 提前致谢!

Update

我们设法通过 .ts 文件和ng-template在标记中,但我们更愿意仅在标记中定义列。 有人有什么想法吗?


我们决定采用在 .ts 文件中包含列定义的解决方案。

这是我们的解决方案:

通用网格组件,其选择器“grid”在 .ts 文件中定义

网格组件.html

<div class="ngx-datatable material">
    <div class="datatable-footer datatable-footer-inner">
        <div class="page-count">
            Show
            <select (change)="onLimitChange($event.target.value)" class="page-limit">
                <option
                    *ngFor="let option of pageLimitOptions"
                    [value]="option.value"
                    [selected]="option.value == currentPageLimit">
                    {{option.value}}
                </option>
            </select>
            per page
        </div>
    </div>
    <ngx-datatable
        class="material striped"
        [columns]="columns"
        [columnMode]="'force'"
        [rows]="gridModel.Data"
        [headerHeight]="'auto'"
        [footerHeight]="'auto'"
        [rowHeight]="'auto'"
        [externalPaging]="true"
        [externalSorting]="true"
        [count]="gridModel?.TotalElements"
        [offset]="gridModel?.CurrentPageNumber"
        [limit]="gridModel?.PageSize"
        [loadingIndicator]="isLoading"
        (page)='loadPage($event)'
        (sort)="onSort($event)">
    </ngx-datatable>
</div>
<app-spinner [isRunning]="isLoading"></app-spinner>
<ng-template #emptyTemplate let-row="row" let-value="value"></ng-template>
<ng-template #idAnchorEditTemplate let-row="row" let-value="value">
    <a [routerLink]="['edit', value]" class="btn btn-sm btn-outline-primary">
        <i class="fa fa-pencil" aria-hidden="true"></i>
    </a>
</ng-template>
<ng-template #dateTemplate let-row="row" let-value="value">
    {{value | date:'dd.MM.yyyy' }}
</ng-template>
<ng-template #dateTimeTemplate let-row="row" let-value="value">
    {{value | date:'dd.MM.yyyy HH:mm:ss' }}
</ng-template>

网格组件.ts

import { Component, Injectable, Input, Output, OnInit, OnDestroy, EventEmitter, ViewChild, TemplateRef } from '@angular/core';
import { NgxDatatableModule, DatatableComponent } from '@swimlane/ngx-datatable';
import { TableColumn } from '@swimlane/ngx-datatable/release/types';

import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/Rx';

import { GridModel } from '../grid/grid-model.model'

@Injectable()
@Component({
    selector: 'grid',
    templateUrl: './grid.component.html'
})
export class GridComponent<T> implements OnInit, OnDestroy {
    @Input()
    columns: TableColumn[];

    private _gridModelInput = new BehaviorSubject<GridModel<T>>(undefined);

    @ViewChild('emptyTemplate') 
    public emptyTemplate: TemplateRef<any>;

    @ViewChild('idAnchorEditTemplate') 
    public idAnchorEditTemplate: TemplateRef<any>;

    @ViewChild('dateTemplate') 
    public dateTemplate: TemplateRef<any>;

    @ViewChild('dateTimeTemplate') 
    public dateTimeTemplate: TemplateRef<any>;

    // change data to use getter and setter
    @Input()
    set gridModelInput(value) {
        // set the latest value for _data BehaviorSubject
        if (value !== undefined) {
            this._gridModelInput.next(value);
        }
    };

    get gridModelInput() {
        // get the latest value from _data BehaviorSubject
        return this._gridModelInput.getValue();
    }

    @Output()
    onFetchDataRequired = new EventEmitter<GridModel<T>>();

    private gridModel: GridModel<T>;
    private isLoading: boolean = false;
    private currentPageLimit: number = 0;
    private pageLimitOptions = [
        {value: 10},
        {value: 25},
        {value: 50},
        {value: 100},
    ];

    constructor() {
    }

    ngOnInit(): void {
        this.gridModel = new GridModel<T>();

        this._gridModelInput.subscribe(gridModel => {
            this.gridModel = gridModel;
            this.isLoading = false;
        }, err => console.log(err));

        this.loadPage();
    }

    protected loadPage(pageEvent = {offset: 0}){
        this.gridModel.CurrentPageNumber = pageEvent.offset;
        this.onFetchDataRequired.emit(this.gridModel);
        this.isLoading = true;
    }

    protected onSort(event) {
        if (this.gridModel.SortBy != event.sorts[0].prop) {
            //this means we are sorting on a new column
            //so we need to return the paging to the first page
            this.gridModel.CurrentPageNumber = 0;            
        }

        this.gridModel.SortBy = event.sorts[0].prop;
        this.gridModel.SortDir = event.sorts[0].dir;

        this.loadPage();
    }

    public onLimitChange(limit: any): void {
        this.gridModel.PageSize = this.currentPageLimit = parseInt(limit, 10);
        this.gridModel.CurrentPageNumber = 0;
        this.loadPage();
    }

    ngOnDestroy(): void {
        this._gridModelInput.unsubscribe();
    }
}

该网格组件包装器的用法

数据网格.component.html

<grid
    (onFetchDataRequired)="fetchDataRequired($event)"
    [gridModelInput]="gridModel">
</grid>

数据网格.component.ts

import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';

import { GridComponent } from '../shared/grid/grid.component';
import { GridModel } from '../shared/grid/grid-model.model';
import { DataGridRowModel } from './data-gridrow.model';
import { DataFetchService } from './data-fetch.service';

@Component({
  templateUrl: 'data-grid.component.html'
})
export class DataGridComponent implements OnInit {
  @ViewChild(GridComponent) grid: GridComponent<DataGridRowModel>;
  gridModel: GridModel<DataGridRowModel> = new GridModel<DataGridRowModel>('DateCreated', 'desc');

  ngOnInit(): void {
    this.grid.columns = [
      { prop: 'Id', cellTemplate: this.grid.idAnchorEditTemplate, headerTemplate: this.grid.emptyTemplate }
      , { prop: 'CreatedBy' }
      , { prop: 'DateCreated', cellTemplate: this.grid.dateTimeTemplate, name: 'Created Date' }
    ];
  }

  constructor(private dataFetchService: DataFetchService) {
  }

  fetchDataRequired(gridModel: GridModel<DataGridRowModel>) {
    this.dataFetchService
      .getSortedPagedResults(gridModel)
      .subscribe(gridModelResponse => {
        this.gridModel = gridModelResponse;
    });
  }
}

它最酷的一点是,它预定义了常用的模板,例如对于 id 列(idAnchorEditTemplate)、日期列 (dateTemplate)或日期/时间列(dateTimeTemplate)。 这允许在单个文件中维护整个应用程序中使用的列模板。

需要的另一种类型是网格模型:

export class GridModel<T> {
    PageSize: number;
    TotalElements: number;
    TotalPages: number;
    CurrentPageNumber: number;
    SortBy: string;
    SortDir: string;
    Data: Array<T>;

    constructor(defaultSortBy: string = 'Id', defaultSortDir: string = 'asc') {
        this.PageSize = 10;
        this.TotalElements = 0;
        this.TotalPages = 0;
        this.CurrentPageNumber = 0;
        this.Data = new Array<T>();

        this.SortBy = defaultSortBy;
        this.SortDir = defaultSortDir;
    }
}

也许有一天有人会从中受益:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ngx-datatable 的通用“包装”组件 的相关文章

随机推荐