Enhance your Angular Grid reports with Formatted values and Links

Advanced cell formatting in ag-Grid

Michael Karén
5 min readOct 31, 2018
Photo by Mika Baumeister on Unsplash

All that studying just to become a spreadsheet engineer…

In this article, I explain how to do some convention based dynamic formatting. Wow, that sounds awesome, but what do I mean by all these fancy words?

First of all, we need to have some conventions for how we name our fields. For example, if we always name our car columns ‘car’, then we can look for that name in our code.

And when we have found that car column we can add the formatting code to our column definitions in the grid. So this means that we could run different reports in our grid. And for all reports where we find this car column we can dynamically format the cells.

The end product will be the grid below where we have some nice formatting of dates and numbers. Above the grid, we have a router outlet. When links are clicked we route to it and send the text from the cell via route parameters.

If you are new to ag-Grid you can check out Get started with Angular Grid in 5 minutes and Learn to customize Angular grid in less than 10 minutes by Max, Wizard of the Web.

I will not be explaining much of the topics that are already covered in those articles but will jump right into the next level of cell formatting. If you want to play with the code along the way, I prepared a StackBlitz with the finished code.

Pipe Setup

In my article Custom Angular Pipes and Dynamic Locale, I showed how to create custom pipes for formatting your dates and numbers.

We can use them in our component by importing them from the library we created in that article and then injecting them in the constructor:

import { LocalDatePipe, LocalNumberPipe } from 'lib-shared';constructor(private dateFormatter: LocalDatePipe, private numberFormatter: LocalNumberPipe)`

Similarly, we can use the standard Angular pipes:

import { DatePipe, DecimalPipe } from '@angular/common';constructor(private dateFormatter: DatePipe, private numberFormatter: DecimalPipe) {}

These pipes will work the same as the custom ones if you don’t need the extra locality functionality that comes with them.

Since we are injecting the pipes in the constructor, we need to add them to providers in the module file so that they become injectable.

Dates

By naming all our date columns ‘xxxDate,’ we can easily discover them. In our example, the column is named ‘releaseDate’. When we know which columns contain dates we can format them with the pipe transform method.

From the docs:

“A valueFormatter allows you to format or transform the value for display purposes.”

A value formatter is just what we need. We look for all columns that end with ‘Date’ and add the date pipe to the valueFormatter property:

(params) => this.dateFormatter.transform(params.value, 'shortDate')

ColumnDefs and RowData

We can use the code from the last paragraph to start building on our method to define the columns: setColumns(columns: string[])

As you can see we need to have the columns before we can run this method. And to have the columns in a dynamic setting, we have to fetch the data first.

How can we extract the column names from an array of unknown type?

You could, for example, take the first object from the array and get the column names with Object.keys(). It returns an array of a given object’s property names.

const columns = Object.keys(data[0]);

In our example, we have hard-coded our data and column names. We run this code in the constructor.

const columns = ['car', 'bus', 'releaseDate', 'price'];
this.setColumns(columns);

Numbers

Find out if there are any conventions in the reports for numbers. If not then maybe you can create some. In our example, we look for the ‘price’ column.

When we know which column names to look for we can use our number formatter to format these numbers as we like. Again we use valueFormatter:

(params) => this.numberFormatter.transform(params.value, '1.2-2')

We can also set the column definition type to numericColumn, which aligns the column header and contents to the right.

Like this blog post? Share it on Twitter! 🐦

Routing Setup

Let’s set up a basic routing for our app. Add a router outlet to the template that holds our grid. It acts as a placeholder where the router should display our components.

<router-outlet></router-outlet>
<ag-grid-angular [rowData]="rowData" [columnDefs]="columnDefs">
</ag-grid-angular>

Next, we can set up the routes where we setup route parameters to our components that are going to be the text from the cells.

const appRoutes: Routes = [
{ path: 'car/:car', component: CarComponent },
{ path: 'bus/:bus', component: BusComponent }
];

And lastly, we create a simple component where we subscribe to the route parameters and show them in our template.

Links by Cell Renderers

I want to use the text in cells for specific columns as links. To achieve this valueFormatter is not enough. Instead, we need to create a component for our ag-Grid cells.

From the docs:

“By default the grid will create the cell values using simple text. If you want more complex HTML inside the cells then this is achieved using cell renderers.”

For re-usability, we can make a component that passes a link in as an argument. We can do this with cellRendererParams.

We want our column definition to look like this:

cellRendererFramework: MyLinkRendererComponent,
cellRendererParams: {
inRouterLink: '/yourlinkhere',
}

In our component, we need to implement AgRendererComponent. With it comes the agInit() and refresh() methods. In our refresh, we return false which means your component will be destroyed and recreated if the underlying data changes.

Then there is the template. We can implement an Angular RouterLink where ‘params.value’ is the value from the cell:

<a [routerLink]="[params.inRouterLink, params.value]">
{{params.value}}
</a>

I once ran into some issues where the link was not working, and I had to jump start the Angular change detection. If you run into that kind of problems and like I can’t figure out why its happening you can check my work-around on stack overflow.

But hopefully you won’t have to monkey patch anything, and the code will look like this:

We have to add the renderer to the entry components for the module.

Column Definitions

Now that we have our cell renderer we can set it up in the column definitions.

And that’s it! If you have any other good ideas on how we can enhance our grid, please comment below.

--

--

Michael Karén

Frontend Architect • JavaScript Enthusiast • Educative.io Author • ngVikings organizer.