Niall Crosby
Sep 21, 2016 · 5 min read

Last week saw the release of version 6.x of ag-Grid. This release signifies a large step forward for ag-Grid, offering full support for Angular 2 Components within the grid itself.

Benefits of Angular 2 Components

As a Angular 2 developer, you’re probably already aware of the power of the Angular 2 framework — you can now apply this to components within ag-Grid. You’ll get all the flexibility that the Angular 2 frameworks offers from within the grid.

Ok, I’m ready for the complicated configuration — bring it on!

To have ag-Grid available to your Angular 2 application, you need to import the AgGridModule. All you need to do is:

@NgModule({
imports: [
BrowserModule,
AgGridModule.forRoot(),
...
})

The forRoot part is to ensure our providers are injected at the root level (or rather, are singletons). We do this as we cache the dynamically created modules under the hood.

Remember, Angular 2 Components can only belong to a single Module…

Under the hood, ag-Grid has a module that dynamically creates modules & components on the fly, and adapts them to corresponding ag-Grid components (Renderers, Editors, Filters etc). ag-Grid offers a simple configuration for Angular 2 components.

Colour me a Picture

ag-Grid supports rendering of cells via Angular with either templates, or components.

For quick and easy rendering, use templates:

// then reference the Component in your colDef like this
colDef = {
// instead of cellRenderer we use cellRendererFramework
cellRendererFramework: {
template: '{{params.value | currency}}',
moduleImports: [CommonModule]
},
// specify all the other fields as normal
headerName: "Currency Pipe Template",
field: "value",
width: 200

Easy! From this you can see that we also support pipes in the templates — in this case we have to provide the CommonModule as we're using the provided currency pipe, but you can provide your own.

For richer cell rendering, you can provide a full Angular 2 component. In this example we have a “parent” component that uses a “child” one.

First, the components:

// the parent component
@Component({
selector: 'ratio-cell',
template: `
<ag-ratio style="height:20px" [topRatio]="params?.value?.top" [bottomRatio]="params?.value?.bottom"></ag-ratio>
`,
styles: [`
//styles omitted for brevity
`]
})
export class RatioParentComponent implements AgRendererComponent {
private params:any = {
value: {top: 0.25, bottom: 0.75}
};
agInit(params:any):void {
this.params = params;
}
}
// the child component (ag-ratio)
@Component({
selector: 'ag-ratio',
template: `
<svg viewBox="0 0 300 100" preserveAspectRatio="none">
<rect x="0" y="0" [attr.width]="topRatio * 300" height="50" rx="4" ry="4" class="topBar" />
<rect x="0" y="50" [attr.width]="bottomRatio * 300" height="50" rx="4" ry="4" class="bottomBar" />
</svg>
`,
styles: [
//styles omitted for brevity
`]
})
export class RatioComponent {
@Input() topRatio: number = 0.67;
@Input() bottomRatio: number = 0.50;
}

Finally, lets tell the grid we want to use these components in our cell:

cellRendererFramework: {
component: RatioParentComponent,
dependencies: [RatioComponent]
},

In this case we need to tell Angular that we need RatioComponent too — we need to do this as the component creation is dynamically!

The result would be something like this (taken from our ag-grid-angular-example project):

I want to Provide Input!

ag-Grid also supports editing of cells via Angular 2 Components.

Configuration is as easy as it is with renderers — first, we start with defining our Component:

@Component({
selector: 'editor-cell',
template: `
<div #container class="mood" tabindex="0" (keydown)="onKeyDown($event)">
<img src="../images/smiley.png" (click)="setHappy(true)" [ngClass]="{'selected' : happy, 'default' : !happy}">
<img src="../images/smiley-sad.png" (click)="setHappy(false)" [ngClass]="{'selected' : !happy, 'default' : happy}">
</div>
`,
styles: [`
//styles omitted for brevity
`]
})
class MoodEditorComponent implements AgEditorComponent, AfterViewInit {
private params:any;
@ViewChild('container', {read: ViewContainerRef}) private container;
private happy:boolean = false;
// dont use afterGuiAttached for post gui events - hook into ngAfterViewInit instead for this
ngAfterViewInit() {
this.container.element.nativeElement.focus();
}
agInit(params:any):void {
this.params = params;
this.setHappy(params.value === "Happy");
}
getValue():any {
return this.happy ? "Happy" : "Sad";
}
isPopup():boolean {
return true;
}
setHappy(happy:boolean):void {
this.happy = happy;
}
toggleMood():void {
this.setHappy(!this.happy);
}
onKeyDown(event):void {
let key = event.which || event.keyCode;
if (key == 37 || // left
key == 39) { // right
this.toggleMood();
event.stopPropagation();
}
}
}

Now tell the grid that we want to use this Component as an editor:

cellEditorFramework: {
component: MoodEditorComponent,
moduleImports: [CommonModule]
}

In this case we tell ag-Grid that we need the CommonModule too - we need it for this Component as we're using some of the built in directives (ngClass etc).

The result would be something like this (again taken from our ag-grid-angular-example project):

Hard to see from a simple image, but that simple component allows for full keyboard control to popup the editor, switch between the options and select Enter to make a choice. Simple!

Too much Information!

ag-Grid supports filtering of rows via Angular 2 Components.

As with the rendering and editing components above, we start with our component:

@Component({
selector: 'filter-cell',
template: `
Filter: <input style="height: 10px" #input (ngModelChange)="onChange($event)" [ngModel]="text">
`
})
class PartialMatchFilterComponent implements AgFilterComponent {
private params:IFilterParams;
private valueGetter:(rowNode:RowNode) => any;
private text:string = '';
@ViewChild('input', {read: ViewContainerRef}) private input; agInit(params:IFilterParams):void {
this.params = params;
this.valueGetter = params.valueGetter;
}
isFilterActive():boolean {
return this.text !== null && this.text !== undefined && this.text !== '';
}
doesFilterPass(params:IDoesFilterPassParams):boolean {
return this.text.toLowerCase()
.split(" ")
.every((filterWord) => {
return this.valueGetter(params.node).toString().toLowerCase().indexOf(filterWord) >= 0;
});
}
getModel():any {
return {value: this.text};
}
setModel(model:any):void {
this.text = model.value;
}
afterGuiAttached(params:IAfterFilterGuiAttachedParams):void {
this.input.element.nativeElement.focus();
}
componentMethod(message:string) : void {
alert(`Alert from PartialMatchFilterComponent ${message}`);
}
onChange(newValue):void {
if (this.text !== newValue) {
this.text = newValue;
this.params.filterChangedCallback();
}
}
}

And again, let’s tell the grid that we want to use this Component as a filter:

filterFramework: {
component: PartialMatchFilterComponent,
moduleImports: [FormsModule]
},

Easy again! In this case we have to provide the FormsModule as we're using the provided directives (ngModel etc), but again you can provide your own.

In this example we have a fairly simple looking UI, but the functionality allows for partial matches of entered words:

There’s so much more you can do if you decide to combine Angular 2 Components with ag-Grid — powerful functionality, fast grid and easy configuration. What are you waiting for?!

ag-Grid

We are ag-Grid and our mission is to build the best datagrid in the world

Niall Crosby

Written by

I am the CEO of ag-Grid. I have 15 years of experience building Enterprise applications before launching my own product ag-Grid Enterprise.

ag-Grid

ag-Grid

We are ag-Grid and our mission is to build the best datagrid in the world

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade