ag-Grid v7: AOT & Angular 2 Components

Sean Landsman
Nov 30, 2016 · 4 min read

Our first pass of Angular 2 support offered a wrapper that allowed users to use ag-Grid within an Angular 2 application but without fully Angular 2 support. You could use ag-Grid fully — and it worked well! — but you could not use Angular 2 components, or use the power that Angular 2 offers within the grid (two-way binding and so on).

Our second pass of Angular 2 was a fairly big leap — we were able to offer full Angular 2 Component support. You could use it just about everywhere, as Renderers, Editors and Filters!

We used the RuntimeCompiler in the background to dynamically compile modules & components - this was great and allowed us to offer the above, as well as being able to offer support for template strings withing a cell.

but you could not use Angular 2 Components together with AOT. You had to choose one or the other.

v7 — The New World

With v7, we drop our reliance on the RuntimeCompiler and move to using the ComponentFactoryResolver instead. We are able to do this by making use of entryComponents within our NgModule, which tells the AOT compiler to create a ComponentFactory for the Component, and registers it with the ComponentFactoryResolver.

// AgGridModule
@NgModule({
imports: [],
declarations: [
AgGridNg2,
AgGridColumn
],
exports: [
AgGridNg2,
AgGridColumn
]
})
export class AgGridModule {
static withComponents(components:any):ModuleWithProviders {
return {
ngModule: AgGridModule,
providers: [
Ng2FrameworkFactory,
Ng2ComponentFactory,
{provide: BaseComponentFactory, useExisting: Ng2ComponentFactory},
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true}
],
};
}
}
// Dynamically instantiating the user supplied Angular 2 Component
factory = this._componentFactoryResolver.resolveComponentFactory(componentType);
let component = viewContainerRef.createComponent(factory);

With this new arrangement, we can now offer full Angular 2 Component support, together with either AOT or JIT support.

The only downside is that we are no longer able to offer support for template strings, as we are no longer able to dynamically create new Components and Modules. On balance, we feel this trade off was a worthwhile one.

The new way of declaring Components pushes the responsiblity of Module's and Component's up to the user, back where it belongs. It also allows for a simplified interface to the grid - instead of:

{
headerName: "Clickable Component",
field: "name",
cellRendererFramework: {
component: ClickableParentComponent,
dependencies: [ClickableComponent]
},
width: 200
}

You now only need to do this:

{
headerName: "Clickable Component",
field: "name",
cellRendererFramework: ClickableParentComponent,
width: 250
}

Show Me More!

Let’s replicate what ag-Grid does with a more concrete example. Let’s assume a user wants to use an external component (i.e. ag-Grid) and let it do its thing but also wants to be able to supply domain specific components to this external component, for use within it.

First, here is our domain specific Component:

@Component({
selector: 'dynamic-component',
template: '<span> Dynamic Component! </span>',
})
export class DynamicComponent {
}

It’s not much more than a simple piece of text that we want displayed in our library.

Next, here is our library component:

@Component({
selector: 'grid-component',
template: `
<button (click)="addDynamicGridComponent()">Add Dynamic Grid component</button>
<br/>
`
})
export class GridComponent {
@Input() componentType: any;
constructor(private viewContainerRef: ViewContainerRef,
private cfr: ComponentFactoryResolver) {
}
addDynamicGridComponent() {
let compFactory = this.cfr.resolveComponentFactory(this.componentType);
this.viewContainerRef.createComponent(compFactory);
}
}

In this case, we have a button that when clicked will dynamically create a supplied component type beneath it.

The key part of this component is this block:

let compFactory = this.cfr.resolveComponentFactory(this.componentType);

This will retrieve the ComponentFactory for the supplied component type, which we can then use to create new instances of it.

For this to work, we need to ensure that the AOT compiler knows that it needs to create a ComponentFactory for the user supplied components. If we don't then our code would work for Just-In-Time (JIT) but not for Ahead-Of-Time (AOT) compilation.

@NgModule({
declarations: [
GridComponent
],
exports: [
GridComponent
]
})
export class GridComponentModule {
static withComponents(components: any[]) {
return {
ngModule: GridComponentModule,
providers: [
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true}
]
}
}
}

The key part of this module is this line:

{provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: components, multi: true}

Which will add the the entries to entryComponents, which in turn will let the AOT compiler know to create ComponentFactory for the specified components.

Tying this together, the user code would do the following:

@NgModule({
imports: [
BrowserModule,
GridComponentModule.withComponents([DynamicComponent])
],
declarations: [AppComponent, DynamicComponent],
bootstrap: [AppComponent]
})
export class AppModule {
}

And the client component would look like this:

@Component({
selector: 'my-app',
template: `<grid-component [componentType]="getComponentType()"></grid-component>
`
})
export class AppComponent {
getComponentType() : any {
return DynamicComponent;
}
}

You can find all the code for the above example over at GitHub.

Benefits of using AOT

The speed and size of the resulting application when using AOT can be significant. In our ag-grid-angular-example project, we estimate the size of the resulting application went from 3.9Mb down to 2.4Mb — a reduction of just under 40%, without optimising for size or being particularly aggressive with rollup.

Speed-wise, the loading time when using AOT is significantly more responsive on startup — this makes sense given that Angular doesn’t have to compile all the code once again. Take a look at the examples project and try both the JIT and AOT versions out for yourself!

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

Sean Landsman

Written by

Lead Developer — Frameworks. Responsible for integrating all of ag-Grid’s supported frameworks (Angular, Vue, React etc)

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