LiveLoveApp logo

Cell Renderer Class

Cell Renderer Class

We recommend that all cell renderers strictly use vanilla JS.

Why?

  • Using Angular Components for cell renderers will drastically impact the performance.
  • Each cell must bootstrap the Angular component
  • Paint performance and frame rate will be negatively impacted - especially when scrolling the grid.

Ok, let's dive into creating a cell renderer using the class-based approach.

The class must implement the ICellRendererComp interface.

export interface ICellRendererComp<TData = any> {
  destroy?(): void;
  getGui(): HTMLElement;
  init?(params: ICellRendererParams<TData>): AgPromise<void> | void;
  refresh(params: ICellRendererParams<TData>): boolean;
}

Let's review each method:

  • destroy() is invoked once by the grid when the cell is destroyed. This is an opportunity to perform any clean up to avoid memory leaks. Implementing this method is optional.
  • getGui() returns the HTMLElement that is rendered for the cell.
  • init() is invoked once the cell is created. Implementing this method is optional.
  • refresh() instructs the cell renderer to refresh, and returns true if successful. If you return false, then the cell renderer component is destroyed and recreated as necessary.

In our example, we want to add some behavior to our Name column, namely (ha):

  • First, we want to alert the user when they click on the user. This is a bit contrived, but you could imagine we want to perform some sort of action in our application when a user clicks on a cell.
  • Second, we want to abbreviate the name value.

To start, let's define the interface for the data that will be provided to the cell renderer.

export interface NameCellRendererData {
  id: string;
  customer: {
    name: string;
  };
}

Next, let's define the interface for the click event.

export interface NameCellRendererClickEvent<T, E = Event> {
  event: E;
  data: T;
}

And, let's define the interface for our cell renderer's custom params object.

export interface NameCellRendererParams<T> {
  click: (event: NameCellRendererClickEvent<T>) => void;
  document: Document;
}

Using the NameCellRendererParams interface we'll create a new Params type intersection.

type Params<T> = NameCellRendererParams<T> & ICellRendererParams<T, string>;

We're now ready to define the NameCellRenderer class.

export class NameCellRenderer<T extends NameCellRendererData>
  implements ICellRendererComp<T>
{
  /** The button element. */
  private btnEl: HTMLButtonElement | null = null;

  /** Execution context bound function when the button is clicked. */
  private handleClick:
    | ((this: NameCellRenderer<T>, event: MouseEvent) => void)
    | null = null;

  /** Store a reference to the params object */
  private params: Params<T>;

  init(params: Params<T>): void {
    this.params = params;
    this.setGui();
  }

  destroy(): void {
    if (this.handleClick !== null && this.btnEl !== null) {
      this.btnEl.removeEventListener('click', this.handleClick);
    }
  }

  getGui(): HTMLElement {
    return this.btnEl!;
  }

  refresh(params: Params<T>): boolean {
    this.params = params;
    if (this.btnEl) {
      this.btnEl.innerHTML = this.transform();
    }
    return true;
  }

  private setGui(): void {
    // omitted for brevity
  }
}

See example on Stackblitz

Let's review the NameCellRenderer code:

  • First, we define the class that implements the ICellRendererComp interface.
  • We'll keep a reference to the btn and handleClick properties to add and remove the click event listener.
  • We'll store a reference to the params.
  • As required, we implement the init(), getGui(), and refresh() methods.
  • And, we also implement the destroy() method to remove the event listener.