Search code examples
javascriptangular5ag-grid

Rendering HTML in AG Grid Column Header (Community v20.0.0 +)


I previously upgraded my AG Grid package from an 18x to 20.0.0 (Community Version). In the previous version, I was simply able to pass an HTML string to the colDefs.headerName property and it would render HTML in the header.

This new version renders header and cell data as a string by default. So now the column headers are rendering like this:

<span ref="eText" class="ag-header-cell-text" role="columnheader">
   "<span id="header-span-1" style="visibility: visible; position: static; display: inline-block; padding-bottom: 5px;">Some Text</span>"
</span>

I obviously want to go with the easiest solution, which seems to be using a Cell Renderer (or something similar as described on https://www.ag-grid.com/javascript-grid-cell-rendering-components/) that simply returns the HTML. How could the following be achieved in using the headerName property?

colDef.cellRenderer = function(params) {
    var html = '<span id="header-span-1" style="visibility: visible; position: static; display: inline-block; padding-bottom: 5px;">Some Text</span>';
    return html;
}

If it can't, would someone please provide a working example of how to use a header component for what I'm specifically after? I saw where I could define a template, but it seems like overkill considering I only need what I've described above. I've searched around and can't seem to find an example.

UPDATE

Thanks to Dominic's response below, I was able to come up with a solution that didn't require excessive (and unnecessary) effort. Again, for my particular use case, I have limited control over the column data coming back from the server, but needed to have a way to format it - beyond the functionality provided by AG Grid's formatter and getter properties.

In my project, the column definitions are provided via a class...

export class colDefs {
    constructor(
        public headerName: any,
        public field: any,
        ...
    ) { } 
}

...and instantiated in the applicable typescript components like this:

for (var i = 0; i < arr.length; i++) {
  ... // Do stuff
  this.columnDefs[i] = new colDefs(headerName, field, ...);
  ... // Do stuff
 }

Taking Dominic's advice, I simply created a new class named HTMLRenderer (extended from HeaderComp). I also added a new property to exported class colDefs named "headerComponent", and I pass the HTMLRenderer as its value.

import { HeaderComp } from 'ag-grid-community/dist/lib/headerRendering/header/headerComp';
class HTMLRenderer extends HeaderComp {
    init(params) { 
    super.init(params);
    // @ts-ignore: Unreachable code error
    this.eGui.querySelector('[ref="eText"]').innerHTML = params.displayName; 
  }
}

export class colDefs {
    constructor(
        public headerName: any,
        public headerComponent: any = HTMLRenderer,
        public field: any,
        ...
    ) { } 
}

Please take note of the // @ts-ignore: Unreachable code error comment. It's needed. Otherwise, if you're using typescript, it will throw an error and fail to compile.

Finally, in my typescript components that instantiate the colDefs class, I pass the applicable values. But for the headerComponent property, you MUST pass undefined, which basically says that we want to go with the default value as defined in the exported class:

for (var i = 0; i < arr.length; i++) {
  ... // Do stuff
  this.columnDefs[i] = new colDefs(headerName, undefined, field, ...);
  ... // Do stuff
 }

This did it for me without the overkill that would have been involved with creating a full header component for just a small string of HTML in the header. The HTML in my AG Grid column headers are rendering now!

Please note there is actually another approach for accomplishing this that involves isolating the HTMLRenderer in its own class file. You ultimately have to register it as a provider in app.module AND in each typescript file you want to use it in...NOT to mention you also have to export the class AND make it an Injectable.

To me, this approach seems like too much work for the little bit of HTML I have in the column headers.


Solution

  • Sadly they decided that HTML wouldn't be useful in the header or accidentally broke it so you can't even use something like ° or Δ for units. Even worse is that the architecture around the header renderer is awful - in order to customize it they want you to also take over handling for sorting, filtering, menu etc. Bizarre that this would be their first assumption but anyway... At least they should provide an innerRenderer like they do with cells.

    The only thing they export is headerRootComp which seems totally useless. In order to not take over all the responsibility for the sorting etc mentioned you have to get the component that they use to render the default header. Note that this isn't officially exported and could change:

    import { HeaderComp } from 'ag-grid-community/dist/lib/headerRendering/header/headerComp';
    
    class HeaderHTMLRenderer extends HeaderComp {
      init(params) {
        super.init(params);
        this.eGui.querySelector('[ref="eText"]').innerHTML = params.displayName;
      }
    }
    

    Pass this in as your default renderer:

    defaultColDef={{
      headerComponent: HeaderHTMLRenderer,
    }}