Introduction
As I started working on my first Angular2 project, coming from a .NET web development background, I instantly started missing the .NET controls, which used to provide me with a lot of awesome fast and quick components, that you can use and save a lot of time.
The first component that I really missed was the GridView
which I used whenever I simply needed to display some data.... so I decided to create my own GridView
using Angular2.
I want this component to do the following:
- Fast and simple data display, no HTML writing at all
- Provide paging and row deleting without no extra hassle
- Can be reused, so it has to be customizable according to the different situation
Needed configuration:
- Excel like column sorting on client side, with the option to switch it on/off
- Control which columns you want to sort
- Control allow row delete on/off
- Pagination ability to add client side / server side searching
Background
Beginner level in Angular2, JavaScript and ES6. So if you can create a basic simple Angular2 application and know how to use a component, you are good to go.
Using the Code
In order to use this component, you simply need to include inside your application, the component is using a sorting pipe and the data is passed to the component from the main component.
Parts of the code are as follows:
- AutoGrid.ts
- AutoGrid.html (optional - HTML can be inside template attribute instead)
- AutoGridPipe.ts
- MainComponent.ts (where the component is used and how to call it)
Component Code (AutoGrid.ts)
Needed Packages
import {Component,Input} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import {AutoGridPipe} from './AutoGridPipe';
Component Block
@Component({
selector : 'AutoGrid',
templateUrl : 'AutoGrid.html',
pipes : [AutoGridPipe]
})
Class
Using @Input()
variables, we can configure the behaviour of the component:
AllowSorting
: Main sorting control, switch on/off sorting option at columns AllowDelete
: Hide/display a delete button Columns
: Set which columns to display, and which of them are sortable or not
Using Subject
variable to trigger events in the main component:
RowDeleted$
: Once triggered, will hold the whole deleted row PageIndexChanged$
: Once triggered, will hold the new page index (starting from 0)
export class AutoGrid
{
SortBy : string = "";
Direction: number = 1;
Pages : any = [];
Data : any = [];
Width:string;
@Input() AllowDelete : boolean= true;
@Input() Columns : any = [];
@Input() AllowSorting : boolean= true;
@Input() TotalRows : number = 0;
@Input() PageSize : number = 0;
@Input() PageIndex : number = 0;
public RowDeleted$ = new Subject<any>();
public PageIndexChanged$ = new Subject<any>();
public LoadData(_data : any)
{
this.Data = _data;
}
OnDeleteRow(Row2Delete:any)
{
this.RowDeleted$.next(Row2Delete);
}
OnPageIndexChange(index:number)
{
console.log(index);
this.PageIndex = index-1;
this.Data = [];
this.PageIndexChanged$.next(index-1);
}
ngOnInit(){
let totalPages : any = (this.TotalRows / this.PageSize);
this.Width = ((totalPages * 38) + totalPages * 2) + "px";
}
ngAfterViewInit() {
for(let i=0;i<this.TotalRows / this.PageSize;i++)
this.Pages.push(i+1);
}
Sort(key:string,dir:number){
this.SortBy = key;
this.Direction = dir;
}
}
Component HTML (AutoGrid.html)
As mentioned before, the whole HTML part can be moved into AutoGrid.ts inside template
parameter.
<!--
<style>
.nav{border:solid 1px #777;float:left;padding:8px; margin:1px; width:38px; text-align: center;}
</style>
<!--
<table class="table">
<tr>
<!--
<th *ngFor="let col of Columns">{{col.colName}}
<!--
<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>
<!--
<th [hidden]="!AllowDelete"></th>
</tr>
<!--
<tr *ngFor="let c of Data | AutoGridPipe : [SortBy,Direction]; let i = index;">
<td *ngFor="let col of Columns">{{c[col.colName]}}</td>
<!--
<td [hidden]="!AllowDelete"><a (click)="OnDeleteRow(c)">X</a></td>
</tr>
</table>
<!--
<table class="table">
<tr>
<td>
<div style="margin:0px auto;" [style.width]="Width">
<!--
<div *ngFor="let pageIndex of Pages;let i = index;" class="nav">
<!--
<span [hidden]="PageIndex!=i">{{pageIndex}}</span>
<!--
<a [hidden]="PageIndex==i"
(click)="OnPageIndexChange(pageIndex)">{{pageIndex}}</a>
</div></div>
</td>
</tr>
</table>
Sorting Pipe
As Angular2 doesn't provide you with default sorting and filtering, we need to implement a pipe in order to do the sorting, the pipe is created in a separate file.
The sorting is done based on 2 variables, which column to sort with and which direction (represented by 1 / -1).
In order to make this component reusable regardless of the data, we have to access the property as an array, e.g., a["param"]
or b["param"]
.
AutoGridPipe.ts
import {Pipe ,PipeTransform} from '@angular/core';
@Pipe({
name:'AutoGridPipe',
pure: false
})
export class AutoGridPipe implements PipeTransform
{
transform(array: any[],[SortBy,Dir] : string)
{
array.sort((a: any, b: any) =>
{
if (a[SortBy] > b[SortBy]) {
return 1 * Dir;
}
if (a[SortBy] < b[SortBy]) {
return -1 * Dir;
}
return 0;
});
return array;
}
}
Main Component
Using the grid component is pretty simple, we start by simply importing it, assuming that all the files are in the same folder, and then use the selector that we chose earlier in our definition <AutoGrid>
.
import {AutoGrid} from './AutoGrid';
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
{
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}];
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}];
@ViewChild(AutoGrid) private _AutoGrid:AutoGrid;
ngOnInit(){
this._AutoGrid.LoadData(this.Items2Load);
}
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));
}
}
Points of Interest
This control can be modified and customized to add more features (editing, client side filtering, server side filtering, server side sorting and more), in case we want to implement a new server side event (a subject variable needs to be used and can be handled by subscribing to it in the main component). Otherwise, your code will be inside the grid component.
As I consider myself a beginner in Angular2, my code or the methods that I am using are far from optimum and not to be considered as a best practice so any comments are welcome here.
History
- 9th August, 2016: First version