我们目前正在开发一个基于Angular2的应用程序,这个应用程序非常重要。为了显示这些数据,我们决定尝试ngx-datatables。
需要大量组件来显示网格中的数据。我们添加了一个自定义页脚模板以及一种使用<select>
元素显示页面大小选择器的自定义标题。
标记行的数量增长了很多,因此我们希望将带有页眉和页脚的定义<ngx-datatable>
移动到单独的网格组件中。现在我们想通过允许使用网格的开发人员简单地定义标记中的列来重用该组件,以便在列内容方面具有完全的灵活性。
我们的想法是拥有一个常用的网格组件,它只要求数据作为输入并呈现它。网格中的典型功能(服务器端排序和分页)应仅在网格组件中存在一次。 使用网格组件的组件应该只提供网格组件所订阅的数据,即它。
带有选择器&#39;网格&#39;的公共网格组件在.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>
作为列,但没有运气,网格只是没有渲染,我猜是因为没有定义列。
有没有办法不一遍又一遍地重复网格定义的相同代码,并实现某种处理公共标记的包装器?
感谢任何输入。 提前谢谢!
更新
我们设法通过.ts文件和标记中的ng-template
来完成,但我们更愿意仅在标记中定义列。
任何人都不知道吗?
答案 0 :(得分:14)
我们决定使用.ts文件中包含列定义的解决方案。
这是我们的解决方案:
<强> 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, 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
)。
这允许在单个文件中维护整个应用程序中使用的列模板。
需要的另一种类型是 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;
}
}
有一天也许某人会从中受益:)