ngx-datatable的通用“包装器"组件 [英] Common 'wrapper' component for ngx-datatable

查看:141
本文介绍了ngx-datatable的通用“包装器"组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们目前正在开发基于Angular2的应用程序,该应用程序数据量很大.为了显示此数据,我们决定尝试 ngx-datatables . 需要大量组件以网格形式显示数据.我们添加了一个自定义的页脚模板,以及一种使用<select>元素显示页面大小选择器的自定义页眉.

We are currently developing an application based on Angular2 that is quite data-heavy. In order to show this data, we decided to give ngx-datatables a try. Plenty of components will be needed showing data in grids. We added a customized footer template as well as a kind of customized header showing a page size selector using a <select> element.

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

The number of markup lines grew quite a lot, therefore we would like to move the definition <ngx-datatable> with header and footer to a separate grid component. Now we would like to reuse that component by allowing the developer using the grid to simply define the columns in markup, to have full flexibility when it comes to the column content.

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

The idea is to have a commonly used grid component that only asks for data as input and renders it. The typical functionality (server-side sorting and paging) in the grid should only exist once in the grid component. The component which uses the grid component should just provide the data which the grid components subscribes to, that's it.

在.ts文件中定义了选择器'grid'的公用网格组件

<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>,但没有运气,只是未渲染网格,我想是因为未定义任何列.

We tried to use <ng-content></ng-content> for the columns but no luck, the grid is just not rendered, I guess becauseno no columns are defined.

有没有一种方法可以不一遍又一遍地为网格定义重复相同的代码,而不必执行某种处理通用标记的包装程序?

Is there a way of not repeating the same code for the grid definition over and over again and to implement some kind of wrapper that takes care of the common markup?

感谢任何输入. 预先感谢!

Grateful for any input. Thanks in advance!

更新

我们设法通过.ts文件和标记中的ng-template做到了这一点,但我们希望仅在标记中定义列. 有人知道吗?

We managed to do it via the .ts file and a ng-template in the markup, but we would prefer to define columns only in the markup. Any idea anyone?

推荐答案

我们决定采用在.ts文件中具有列定义的解决方案.

We decided to go with the solution having the column definitions in the .ts file.

这是我们的解决方案:

grid.component.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>

grid.component.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();
    }
}

此网格组件包装器的用法

data-grid.component.html

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

data-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). 这样可以维护在整个应用程序中单个文件中使用的列模板.

The cool thing about it is, that it has commonly used templates pre-defined, e.g. for the id column (idAnchorEditTemplate), date columns (dateTemplate) or date/time columns (dateTimeTemplate). This allows maintenance of column templates that are used throughout the application in a single file.

另一种需要的类型是 GridModel :

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;
    }
}

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

Maybe someone benefit from it someday :)

这篇关于ngx-datatable的通用“包装器"组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆