---
source_url: https://itmustbecode.com/powerapps-grid-control-how-to-make-cell-renderers-more-generic/
title: PowerApps Grid Control - How to make Cell Renderers more Generic
date: 2023-10-02T01:46:28+00:00
categories:
  - blog
tags:
  - cell-renderer
  - pcf
  - react
word_count: 2452
reading_time_minutes: 12
type: posts
---

The **Power Apps grid control** is a new and improved version for the **data grid** control for Power Platform **model-driven applications**. It introduces a modern data grid user experience and a range of noteworthy features, including the ability to perform inline editing and seamless infinite scrolling of the grid records, amongst others.

Simply implement the **Power Apps grid control** component on your applications data grids (table views or form subgrids) to unlock up its capabilities.

{{< figure src="./image.png" alt="" caption="" >}}

{{< figure src="./power-apps-grid-control-editing.gif" alt="" caption="" >}}

Moreover, as a developer, one of the most appealing aspects of the new grid control is the ability to **customize** the look and feel of the grid columns using specialized PCF (PowerApps Component Framework) controls known as **Cell Renderers**.

Although, like my fellow MVP [**Diana Birkelback**](https://twitter.com/dianabirkelbach) explained in her [**great post**](https://dianabirkelbach.wordpress.com/2022/07/27/power-apps-grid-control-first-glimpse-to-the-cell-renderer-and-editors/) on the subject, the current architecture of cell renderers **lacks** the ability to inject **parameters** to the control ( _like you can do with a normal PCF control_) to make them more **generic** and reusable.

In this post I will show how I was able to attain a certain degree of genericity by making the cell renderer aware of their **root table**(entity) and how to obtain runtime information about **lookup** fields present in the underlying grid data.

The end-result can be appreciated in my latest community PCF project, the **[RecordImage Cell Renderer](https://github.com/drivardxrm/RecordImage.CellRenderer)**.

The control turns a plain out-of-the box grid like this one.

{{< figure src="./image-10.png" alt="" caption="" >}}

Into a visually appealing grid as seen below.

{{< figure src="./image-9.png" alt="" caption="" >}}

Please check the source code for more in-depth info

🔗[drivardxrm/RecordImage.CellRenderer: Cell renderer for PowerApps Grid (github.com)](https://github.com/drivardxrm/RecordImage.CellRenderer)

## **What is the PowerApps Grid Control**

Before diving in the cell renderer specific stuff, I think that the **PowerApps Grid control** deserves a bit of attention as **it will become** the default experience for model-driven grids in the near future.

> " _This control will eventually **replace all read-only and editable grids** in model-driven apps._"
>
> 🔗 [Power Apps grid control - Power Apps \| Microsoft Learn](https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/the-power-apps-grid-control)
>
> official Microsoft documentation

The **PowerApps Grid control** acts as a one-stop shop where you can define the behavior and the look of your apps grids. The official documentation is a great resource to get a better understanding of all the features included with the control, but here are some of my favorites.

### **Switch from Read-Only to Editable grid**

Finish are the times when you needed to have a different controls for **editable** and for **read-only** grids. With this new control, simply configure the **'Enable editing**' parameter of the control at design time and it will be rendered accordingly.

{{< figure src="./image-11.png" alt="" caption="" >}}

### **Infinite scrolling by default**

One aspect that always bugged me with classic data grids within modern-driven apps is the pagination system.

Especially for grids that contains a lot of data, navigating within the grid can quickly become a tedious task. Users find themselves constantly scrolling up and down, and if they can't locate what they need, they're forced to click through previous and next pages.

The **PowerApps Grid control** have infinite scrolling enabled by default, allowing seamless navigation of lengthy records lists without context switching.

Simply toggle the ' **Enable Pagination**' setting to 'Yes' to revert to the classic rendering style.

{{< figure src="./image-5.png" alt="" caption="" >}}

### **Enters the Cell Renderers**

From a developer perspective, the **PowerApps Grid control** truly shines by allowing the injection of a **Customizer control** (or cell renderer) on top of the grid. Just enter the name of the desired cell renderer in the ' **Customizer control**' parameter. ( _Format : {publisher prefix}\_{namespace}.{control name}_)

{{< figure src="./image-6.png" alt="" caption="" >}}

It gives a way to customize the look and feel of **specific cells** in a grid without having to recreate your own data grid custom control from scratch. This approach allows you to piggyback on the out-of-the-box control and add your own customization on top of it, giving you the best of both worlds.

Head to the official documentation to get you started on cell customizer development process.

🔗[Customize the editable grid control - Power Apps | Microsoft Learn](https://learn.microsoft.com/en-us/power-apps/developer/component-framework/customize-editable-grid-control?WT.mc_id=DX-MVP-5004959)
🔗Customized editable grid - Power Apps | Microsoft Learn

Also, don't forget to get a look at these posts from Diana Birkelback

🔗Power Apps Grid Control – First Glimpse to the Cell Renderer and Editors – Dianamics PCF Lady (wordpress.com)
🔗Fetch Related Records with Power Apps Grid Customizer Control – Dianamics PCF Lady (wordpress.com)


I will not go in every details of the implementation of a cell customizer but here are the key points.

- In the **init** method of the customizer PCF control, an **EventName** parameter will be received and will serve as a reference to the datagrid ( _this is supplied at runtime by the platform, you don't need to do anything_).
- You then need to instantiate a **PAOneGridCustomizer** that will contain the business logic in the form of a **cellRendererOverrides**.
- **Bind** that customizer to the grid instance ( **EventName**) by invoking a **fireEvent**.

{{< figure src="./image-10-1.png" alt="" caption="" >}}

The **cellRendererOverrides** implementation will contain code that will fire every time a cell of the grid is rendered. The goal here is to intercept the columns of interest and carry out the desired custom rendering.

Note that every type of columns can have their own renderers so you need to define different overrides for each column types. Also, you have access to the column name so you can filter out the unwanted columns.

{{< figure src="./image-12.png" alt="" caption="" >}}

However, as previously mentioned, despite Cell Renderers being constructed using the PCF control framework, a notable **limitation** is the inability to supply any runtime parameters to a specific instance of the control, as you would with a standard PCF control. This limitation presents challenges in creating generic cell renderers that are reusable across the system.

Let's explore ways to alleviate some of these challenges using my latest community PCF control.

## The RecordImage Cell Renderer

The story behind the [**RecordImage Cell renderer**](https://github.com/drivardxrm/RecordImage.CellRenderer) is to leverage the use of individual record image ( **primary image**) and showcase these images within data grids that surface these records.

{{< figure src="./image-7.png" alt="" caption="" >}}

> Enabling record images is a straightforward process that is often underestimated in model-driven application projects. It can introduce a layer of depth and visual appeal to your application.
>
> To enable record image on any table, create an **image column** and configure the column as the **Primary Image** of the table.
>
> {{< figure src="./image-14.png" alt="" caption="" >}}

Inside a data grid, link to records can come in two flavors, either by the **primary name** column or related data **lookups** columns contained in the view.

{{< figure src="./image-8.png" alt="" caption="" >}}

Under the hood of the cell customizer, whenever a cell of interest rendering is fired, a mini-React application ( **RecordImageCellApp**) will be returned to override the out-of-the-box rendering. To ensure accurate rendering of the image, the app relies on two crucial pieces of information passed as props (parameters).

- The logical name of the table ( **entityname**)
- The ID of the individual record ( **recordid**)

{{< figure src="./image-22.png" alt="" caption="" >}}

Here is how I was able to retrieve them.

### **Get Entity Reference for Primary Name column**

Since the **primary name** column is of type ' **Text**', the first step is to determine if the cell that is being rendered is actually the PrimaryName column of the displayed view and skip the rendering for all other Text type columns. Fortunately the platforms gives us this information in the **rendererParams** object ( **colDefs\[columnIndex\].isPrimary**)

Knowing that, it's also easy to get the ID of the row's record using the \[ **RECID**\] property of the **rowData** object.

{{< figure src="./image-20.png" alt="" caption="" >}}

To get the table name, it's a bit more tricky. First, we need to determine what is the type of grid that is being rendered. The grid can either be rendered as a **main entity view** or as a **subgrid** in a form that shows related records.

{{< figure src="./image-1.png" alt="" caption="" >}}

{{< figure src="./image-2.png" alt="" caption="" >}}

I found a way to get that information by inspecting the **factory.\_customControlProperties.pageType** that is buried inside the the **context** object of the PCF control. _Note that the **context** object needs to be casted as an **any** because these properties are not exposed in the official type definition of the context._

```
(context as any).factory._customControlProperties.pageType
```

As seen below, the **pageType** value will be ' **EntityList**' if the **datagrid** is rendered on a **main entity view** and ' **EditForm**' when rendered as a **subgrid** on a form

Now depending on the **pageType** value the name of the table can be infered accordingly.

```
const entityname = pageType == 'EntityList' ?
            (pcfContextService.context as any).page.entityTypeName :
            (pcfContextService.context as any).factory._customControlProperties.descriptor.Parameters.TargetEntityType
```

#### **Main Entity view**

{{< figure src="./image-1.png" alt="" caption="" >}}

{{< figure src="./image-21.png" alt="" caption="" >}}

#### **Subgrid on a form**

{{< figure src="./image-4.png" alt="" caption="" >}}

{{< figure src="./image-5-1.png" alt="" caption="" >}}

### **Get Entity Reference for Lookup columns**

The second technique I want to show is how to get the entity reference information about **lookup** columns referenced inside the grids underlying view.

The information about a particular lookup value (table name and id of the record) can be found in the **rowData** object received by the cell renderer **rendererParams**. The only challenge is that the property name inside the **rowData** object is dynamic and correspond to the name of the column inside the view.

{{< figure src="./image-9-1.png" alt="" caption="" >}}

Using the  `keyof typeof obj` technique, we can get the lookup info (entity name and guid) by dynamically accessing the **rowData** object property that equals to the column name of the lookup. The code below speaks fort itself.

More onkeyof typeof obj here :
🔗[Dynamically access an Object's Property in TypeScript | bobbyhadz](https://bobbyhadz.com/blog/typescript-access-object-property-dynamically)

```
["Lookup"]: (props: CellRendererProps, rendererParams: GetRendererParams) => {
        const {columnIndex, colDefs, rowData } = rendererParams;
        const columnName = colDefs[columnIndex].name;

        type ObjectKey = keyof typeof rowData;
        const columnNameProperty = columnName as ObjectKey;

        const lookup = rowData?.[columnNameProperty] as any
        if(lookup == null){
            return null
        }
        const lookupentity = lookup.etn
        const lookupid = lookup.id.guid
```

The table name can be derived from the **lookup.etn** and the record id can be found in **lookup.id.guid**.

### **Get the PrimaryImage column name from Metadata**

Having access to the table name either from the **main table** or from a a **lookup**, the next challenge is to get the name of the **primary image** column of these tables so we can retrieve the individual record images further on.

To achieve this, its a matter of making a **metadata** call on the table name using the **getEntityMetadata** function exposed by the **ComponentFramework.Context** object. Within the metadata call's response, we can conveniently extract the value of the `PrimaryImageAttribute`, which holds the logical name of the primary image column.

{{< figure src="./image-17.png" alt="" caption="" >}}

{{< figure src="./image-15.png" alt="" caption="" >}}

{{< figure src="./image-25.png" alt="" caption="" >}}

### **Get the record image**

Finally, to retrieve the image data, all is needed is to make a **[webAPI.retrieveRecord](https://learn.microsoft.com/en-us/power-apps/developer/component-framework/reference/webapi/retrieverecord?WT.mc_id=DX-MVP-5004959)** call to query the table (**entityname**) and get the **primaryimage** attribute of the current cell record (**recordid**)

Given that the content of the **primaryimage** field is stored in **base64** within the Dataverse table, the function shown below will produce a string that is appropriately formatted and suitable for injection into an HTML `<img>` element's `src` attribute. This string will be structured as follows: `data:image/jpeg;base64,{primaryimage_data}`.

```
async getRecordImage (entityname:string, recordid:string, primaryimage:string) : Promise<string> {

    let record = await this.context.webAPI.retrieveRecord(entityname,recordid,`?$select=${primaryimage}`)

    return  record?.[primaryimage]
            ? `data:image/jpeg;base64,${record?.[primaryimage]}`
            : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='  //1 px transparent  https://png-pixel.com/
  }
```

In my implementation I have wrapped the result of the function in a custom hook, but you can see the resulting image data being injected in a **FluentUI** Image object **src** attribute.

{{< figure src="./image-26.png" alt="" caption="" >}}

### Clickable link

To preserve the standard functionality of Primary Name and lookup columns, it's essential to wrap the text of the cell in a clickable link that redirects the user to the record expressed by the cell.

This can be achieve by using the ComponentFramework.Context **n** **avigation.openForm** function and binding the resulting promise to the OnClick event of a link tag. Here i'm using FluentUI Link object.

```
async openRecord (entityname:string, recordid:string):Promise<ComponentFramework.NavigationApi.OpenFormSuccessResponse> {
    return this.context.navigation.openForm(
      {
        entityName: entityname,
        entityId: recordid
      }
    )
  }
```

{{< figure src="./image-23.png" alt="" caption="" >}}

### What about performance and caching?

As you can imagine, having code that can run on any cell of a data grid needs to execute lightning fast.

That's why for my implementation I opt-in for a **PCF Virtual control**. This flavor of PCF control uses the React and Fluent provided by the PowerApps runtime and are known to be smaller in size and execute faster than regular PCF control.

See official docs :
🔗[React controls & platform libraries (Preview) - Power Apps | Microsoft Learn](https://learn.microsoft.com/en-us/power-apps/developer/component-framework/react-controls-platform-libraries?WT.mc_id=DX-MVP-5004959)

Additionally, given that a substantial portion of the metadata and Dataverse WebApi calls might be the same across a grid rendering, it would be a counterproductive to re-query the same data over and over again.

For this, I'm using the awesome **[React Query](https://tanstack.com/query/latest/)** package to graciously handle the asynchronous state management and caching of the app.

Learning how to effectively use React Query is beyond the scope of this blog post and will probably be the subject of future writings. I highly recommend that Check the source code to get a grasp of its usage.

## Takeaway

Exploring Cell Renderers for the PowerApps Grid control has been an enjoyable learning experience. They offer unprecedented ways to enhance the user experience of data grids.

While it might take a bit of practice to master their implementation, the payoff is well worth it, as we have seen with the **[RecordImage Cell Renderer](https://github.com/drivardxrm/RecordImage.CellRenderer)**.

I hope that the framework will evolve overtime to remove some of the current limitation and I can't wait to see what kind of renderers will emerge from the community.

Happy coding!

## Links
{{< linkcard url="https://learn.microsoft.com/en-us/power-apps/maker/model-driven-apps/the-power-apps-grid-control" title="Power Apps grid control - Power Apps" summary="Learn about the Power Apps grid control for model-driven apps." image="/linkcards/power-apps-grid.png" domain="learn.microsoft.com" new_tab="true" nofollow="true" >}}

{{< linkcard url="https://learn.microsoft.com/en-us/power-apps/developer/component-framework/customize-editable-grid-control?WT.mc_id=DX-MVP-5004959" title="Customize the editable grid control - Power Apps" summary="Learn how you can customize the editable grid control." image="/linkcards/f69ad1e3eed32c06.png" domain="learn.microsoft.com" new_tab="true" nofollow="true" >}}


{{< linkcard url="https://learn.microsoft.com/en-us/power-apps/developer/component-framework/react-controls-platform-libraries?WT.mc_id=DX-MVP-5004959" title="React controls & platform libraries (Preview) - Power Apps" summary="You can achieve significant performance gains using React and platform libraries. When you use React and platform libraries, you are using the same infrastructure used by the Power Apps platform. This means you no longer have to package React and Fluent packages individually for each control." image="/linkcards/4fac7452f522bab5.png" domain="learn.microsoft.com" new_tab="true" nofollow="true" >}}


{{< linkcard url="https://dianabirkelbach.wordpress.com/2022/07/27/power-apps-grid-control-first-glimpse-to-the-cell-renderer-and-editors/" title="Power Apps Grid Control – First Glimpse to the Cell Renderer and Editors" summary="We’ve saw the Power Apps grid control (Preview) in the latest Wave Announcements. Each time we hear about great improvements. In Release Wave 1/2022 we’ve got inline-editing and infinit…" image="/linkcards/585717aa9873e65d.png" domain="dianabirkelbach.wordpress.com" new_tab="true" nofollow="true" >}}


{{< linkcard url="https://dianabirkelbach.wordpress.com/2022/10/01/fetch-related-records-with-power-apps-grid-customizer-control/" title="Fetch Related Records with Power Apps Grid Customizer Control" summary="The Power Apps Grid customizer control is a preview feature I really look forward to. In the last two blogs I’ve already shown how to use it for: Host your own read-only or editable PCF insid…" image="/linkcards/29f966cb01824bbe.png" domain="dianabirkelbach.wordpress.com" new_tab="true" nofollow="true" >}}

{{< linkcard url="https://bobbyhadz.com/blog/typescript-access-object-property-dynamically" title="Dynamically access an Object’s Property in TypeScript | bobbyhadz" summary="A step-by-step guide on how to dynamically access an object’s property in TypeScript." image="https://bobbyhadz.com/images/blog/typescript-access-object-property-dynamically/banner.webp" domain="bobbyhadz.com" new_tab="true" nofollow="true" >}}

{{< linkcard url="https://tanstack.com/query/latest/" title="TanStack Query | React Query, Solid Query, Svelte Query, Vue Query" summary="Powerful asynchronous state management, server-state utilities and data fetching for TS/JS, React, Solid, Svelte and Vue" image="https://github.com/tanstack/query/raw/beta/media/repo-header.png" domain="tanstack.com" new_tab="true" nofollow="true" >}}


{{< linkcard url="https://pcf.gallery/recordimage-cell-renderer/" title="RecordImage Cell Renderer" summary="A control (generic Cell renderer) that works in conjunction with the Power Apps Grid Control. It renders each record’s primary name with its primary image, improving visual clarity and user experience." image="/linkcards/46bcaa9c38e1abc8.png" domain="pcf.gallery" new_tab="true" nofollow="true" >}}


{{< linkcard url="https://github.com/drivardxrm/RecordImage.CellRenderer" title="GitHub - drivardxrm/RecordImage.CellRenderer: Cell renderer for PowerApps Grid" summary="Cell renderer for PowerApps Grid. Contribute to drivardxrm/RecordImage.CellRenderer development by creating an account on GitHub." image="/linkcards/61b857611978cdd2.png" domain="github.com" new_tab="true" nofollow="true" >}}

_[Photo by Fayette Reynolds M.S.](https://www.pexels.com/photo/cell-seen-under-microscope-11198495/)_


