65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular2 网格组件 - 自动生成。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (11投票s)

2016年8月9日

CPOL

3分钟阅读

viewsIcon

32264

downloadIcon

825

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日:第一个版本
© . All rights reserved.