我们目前正在开发一个基于Angular2的应用程序,它非常重要.为了显示这些数据,我们决定尝试ngx-datatables.
需要大量组件来显示网格中的数据.我们添加了一个自定义页脚模板以及一种显示页面大小选择器的自定义标题,使用< select>元件.
标记行的数量增长了很多,因此我们希望移动定义< ngx-datatable>带有页眉和页脚的单独网格组件.现在,我们希望通过允许使用网格的开发人员简单地定义标记中的列来重用该组件,以便在列内容方面具有完全的灵活性.
我们的想法是拥有一个常用的网格组件,它只要求数据作为输入并呈现它.网格中的典型功能(服务器端排序和分页)应仅在网格组件中存在一次.
使用网格组件的组件应该只提供网格组件所订阅的数据,就是这样.
我们现在所拥有的:
在.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>对于列,但没有运气,网格只是没有渲染,我想因为没有定义列.
有没有办法不一遍又一遍地重复相同的网格定义代码,并实现某种处理常见标记的包装器?
感谢任何输入.
提前致谢!
更新
这是我们的解决方案:
在.ts文件中定义的带有选择器’grid’的公共网格组件
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(); } }
使用此网格组件包装器
数据grid.component.html
<grid (onFetchDatarequired)="fetchDatarequired($event)" [gridModelInput]="gridModel"> </grid>
数据grid.component.ts
import { Component,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).
这允许在单个文件中维护整个应用程序中使用的列模板.
另一种需要的类型是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; } }
也许有一天会有人受益:)