Angular2 网格组件 - 自动生成。






4.87/5 (11投票s)
Angular2 网格组件。
引言
当我开始我的第一个 Angular2 项目时,由于我之前有 .NET Web 开发的背景,我立刻开始怀念 .NET 控件,它总是能给我提供大量超棒且快速的组件,你可以使用它们并节省大量时间。
我真正想念的第一个组件是 GridView
,每当我需要简单地显示一些数据时,我就会使用它……所以我决定使用 Angular2 创建我自己的 GridView
。
我希望这个组件能做到以下几点
- 快速而简单的数据显示,根本不需要编写 HTML
- 提供分页和行删除,而无需额外的麻烦
- 可以重复使用,因此必须根据不同的情况进行定制
需要的配置
- 客户端类似 Excel 的列排序,可以选择开启/关闭
- 控制您要排序的列
- 控制是否允许开启/关闭行删除
- 分页功能,可以添加客户端/服务器端搜索
背景
Angular2、JavaScript 和 ES6 方面的初级水平。所以如果你可以创建一个基本的简单的 Angular2 应用程序,并且知道如何使用组件,那就没问题了。
Using the Code
为了使用此组件,您只需将其包含在您的应用程序中,该组件使用排序管道,并且数据从主组件传递到该组件。
部分代码如下
- AutoGrid.ts
- AutoGrid.html(可选 - HTML 可以放在 template 属性中)
- AutoGridPipe.ts
- MainComponent.ts(组件的使用位置以及如何调用它)
组件代码 (AutoGrid.ts)
需要的包
import {Component,Input} from '@angular/core';
//Needed for triggering events
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
//Needed for sorting
import {AutoGridPipe} from './AutoGridPipe';
组件块
@Component({
selector : 'AutoGrid',//tag name in when calling the component
templateUrl : 'AutoGrid.html',//path of the HTML can be replaced with template:...
pipes : [AutoGridPipe]//link the Pipe
})
类
使用 @Input()
变量,我们可以配置组件的行为
AllowSorting
:主排序控件,开启/关闭列排序选项AllowDelete
:隐藏/显示删除按钮Columns
:设置要显示的列,以及哪些列是可排序的
使用 Subject
变量来触发主组件中的事件
RowDeleted$
:触发后,将保存整个已删除的行PageIndexChanged$
:触发后,将保存新的页面索引(从 0 开始)
export class AutoGrid
{
SortBy : string = "";//This param is used to determine which column to use for sorting
Direction: number = 1;//1 is ascending -1 is descending
Pages : any = [];//Dummy array to load the pagination
Data : any = [];//Main data container
Width:string;
@Input() AllowDelete : boolean= true;//Can a row be deleted
@Input() Columns : any = [];//Name of the columns to display / order
@Input() AllowSorting : boolean= true;//Allow client side sorting
@Input() TotalRows : number = 0;//Total number of rows for paging
@Input() PageSize : number = 0;
@Input() PageIndex : number = 0;//To control the start page index
public RowDeleted$ = new Subject<any>();//Subscribe to this to handle delete event
public PageIndexChanged$ = new Subject<any>();//Subscribe to this to handle "page index change" event
public LoadData(_data : any)
{//Main method to load the data
this.Data = _data;
}
OnDeleteRow(Row2Delete:any)
{//private method to raise RowDeleted
this.RowDeleted$.next(Row2Delete);
//client side delete for data can be done here
}
OnPageIndexChange(index:number)
{//private method to raise RowDeleted
console.log(index);
this.PageIndex = index-1;
this.Data = [];
this.PageIndexChanged$.next(index-1);
}
ngOnInit(){
//used for pagination style
let totalPages : any = (this.TotalRows / this.PageSize);
this.Width = ((totalPages * 38) + totalPages * 2) + "px";
}
ngAfterViewInit() {
//fill the dummy array
for(let i=0;i<this.TotalRows / this.PageSize;i++)
this.Pages.push(i+1);
}
Sort(key:string,dir:number){
//Change the sorting criteria
this.SortBy = key;
this.Direction = dir;
}
}
组件 HTML (AutoGrid.html)
如前所述,整个 HTML 部分可以移动到 AutoGrid.ts 的 template
参数中。
<!--Just to give the pagination a better look-->
<style>
.nav{border:solid 1px #777;float:left;padding:8px; margin:1px; width:38px; text-align: center;}
</style>
<!--End of style-->
<table class="table">
<tr>
<!--Generate column names ... loop on columns array-->
<th *ngFor="let col of Columns">{{col.colName}}
<!--If sorting is allow and its enabled for this column, we switch between ascending/descending-->
<a [hidden]="Direction==-1 || !AllowSorting ||
!col.sortable" (click)="Sort(col.colName,-1)">?</a>
<a [hidden]="Direction==1 || !AllowSorting ||
!col.sortable" (click)="Sort(col.colName,1)">?</a>
</th>
<!--For the delete link-->
<th [hidden]="!AllowDelete"></th>
</tr>
<!--Loop on the data-->
<tr *ngFor="let c of Data | AutoGridPipe : [SortBy,Direction]; let i = index;">
<td *ngFor="let col of Columns">{{c[col.colName]}}</td>
<!--show delete if enabled, and pass the whole row to OnDeleteRow-->
<td [hidden]="!AllowDelete"><a (click)="OnDeleteRow(c)">X</a></td>
</tr>
</table>
<!--Pagination-->
<table class="table">
<tr>
<td>
<div style="margin:0px auto;" [style.width]="Width">
<!--Width is calculated based on number of pages, to make it center-->
<div *ngFor="let pageIndex of Pages;let i = index;" class="nav">
<!--If this is the current page, page number displayed as a text only-->
<span [hidden]="PageIndex!=i">{{pageIndex}}</span>
<!--Page number displayed as a link,
which call OnPageIndexChange and pass the new PageIndex + 1 -->
<a [hidden]="PageIndex==i"
(click)="OnPageIndexChange(pageIndex)">{{pageIndex}}</a>
</div></div>
</td>
</tr>
</table>
排序管道
由于 Angular2 没有为您提供默认的排序和过滤功能,我们需要实现一个管道来进行排序,该管道是在单独的文件中创建的。
排序是基于 2 个变量完成的,即按哪一列排序以及排序方向(用 1 / -1 表示)。
为了使此组件可重用,而无需考虑数据,我们必须将该属性作为数组访问,例如,a["param"]
或 b["param"]
。
AutoGridPipe.ts
import {Pipe ,PipeTransform} from '@angular/core';
@Pipe({
name:'AutoGridPipe',//name will be used in the page
pure: false
})
export class AutoGridPipe implements PipeTransform
{
//Sort,Dir ... will be passed through the component
transform(array: any[],[SortBy,Dir] : string)
{
array.sort((a: any, b: any) =>
{
if (a[SortBy] > b[SortBy]) {
return 1 * Dir;//we switch ascending and descending by multiply x -1
}
if (a[SortBy] < b[SortBy]) {
return -1 * Dir;//we switch ascending and descending by multiply x -1
}
return 0;
});
return array;
}
}
主组件
使用网格组件非常简单,我们首先简单地导入它,假设所有文件都在同一个文件夹中,然后使用我们在定义中选择的选择器 <AutoGrid>
。
import {AutoGrid} from './AutoGrid';
//ViewChild Needed whenever you want to access a child component property or method
import {Component,ViewChild} from '@angular/core';
@Component({
selector:'MainComponent',
template: `
<AutoGrid
[Columns]="Columns2Display"
[AllowDelete]="true" [TotalRows]="100" [PageSize]="20">
</AutoGrid>`,
directives: [AutoGrid]
})
export class MainComponent
{
//Dummy data to be loaded
Items2Load : any = [
{Key:'1', paramName:'Dummy',
readType:'Post' ,regEx:'d+',mapValue:'31',priority:2},
{Key:'2', paramName:'Something',
readType:'GET' ,regEx:'&^',mapValue:'44',priority:1},
{Key:'3', paramName:'Hello',
readType:'JSON' ,regEx:'w+',mapValue:'333',priority:4},
{Key:'4', paramName:'Goo',
readType:'XML' ,regEx:'OSOSOS',mapValue:'555',priority:6}];
//Columns to display, enable / disable sort
//Basically any column base configuration needed, can be added here
//Such as Display name, column Icon ....
Columns2Display : any =[
{colName: "Key", sortable:true},
{colName: "paramName", sortable:true},
{colName: "readType", sortable:false},
{colName: "regEx", sortable:true},
{colName: "mapValue", sortable:true},
{colName: "priority", sortable:true}];
//Through this we can access the child component through _AutoGrid
@ViewChild(AutoGrid) private _AutoGrid:AutoGrid;
//Load the data into the child component
//Cannot be done inside constructor as you don't have access to ViewChild in constructor
ngOnInit(){
//Pass the data to child component, through LoadData method
this._AutoGrid.LoadData(this.Items2Load);
//Can be loaded using ajax call or service
//this._dummy service.LoadItems((res:any)=>{
// this._AutoGrid.LoadData(res);
//});
}
//Handle the events (Delete / PageIndexChanged)
ngAfterViewInit() {
this._AutoGrid.RowDeleted$.subscribe(c => console.log("I saw you, deleted " + c.Key));
this._AutoGrid.PageIndexChanged$.subscribe(c=> console.log("New page id " + c));
}
}
关注点
可以修改和定制此控件以添加更多功能(编辑、客户端过滤、服务器端过滤、服务器端排序等),如果我们想要实现一个新的服务器端事件(需要使用一个 subject 变量,并且可以通过在主组件中订阅它来处理)。否则,您的代码将在网格组件内部。
由于我认为自己是 Angular2 的初学者,因此我的代码或我使用的方法远非最佳,不应被视为最佳实践,因此欢迎提出任何意见。
历史
- 2016年8月9日:第一个版本