Common 'wrapper' component for ngx-datatable

后端 未结 1 1730
有刺的猬
有刺的猬 2021-02-08 11:47

Some introduction:

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

相关标签:
1条回答
  • 2021-02-08 12:25

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

    Here is our solution:

    Common grid component with selector 'grid' defined in .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();
        }
    }
    

    Usage of this grid component wrapper

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

    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.

    One additional type that will be needed is 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 :)

    0 讨论(0)
提交回复
热议问题