Power Apps Grid API – Disabling

As promised, I’ll continue the series of Power Apps grid API with a few use-cases. Today I’m having a look at one of the most requested grid extensibility feature: disabling. There are different kind of disabling requirements, and I would differentiate between:

  • disabling a complete grid
  • disabling complete columns
  • disabling specific cells

The last two ones could be treated the same, but because of the bug of OnRecordSelect not being triggered sometimes, I treat them different.

Then, there is also a difference if we need to work on a:

  • grid in the home area (sitemap) or related grid
  • subgrid

I’ll try to go into details for each case, having also a look at extended requirements where we need to make some requests to get the data we need for the decision. The API doesn’t provide async events, so we need to have a solution to work around that.

At the end there is a decision flowchart, to help you navigate through all cases.

If you are interested in only a specific case, here is the table of content:

  1. Disabling the complete grid
    1. Visual feedback about the disabled state is very subtle
    2. Working asynchronous
  2. Disabling the complete nested grid
    1. Visual feedback much better
  3. Disabling columns
    1. Some columns are disabled by default
    2. Disabling columns on subgrids
    3. Disabling columns of a subgrid with dependency on form fields
    4. Disable subgrid columns async- dependency on related data
      1. Solution 1. Disabling columns async in grid.OnLoad
      2. Solution 2. Disabling async in form.OnLoad works better
      3. Solution 3. Disabling async in form.OnLoad impoved
      4. Solution 4. Disabling columns async based on “async form.OnLoad”
      5. Solution 5. Using tab “onVisible”: tabStateChanged
    5. Disable columns of a grid in the main area (sitemap)?
      1. Async limitation
  4. Disabling specific cells on subgrids
    1. Disabling specific cells in grid.OnLoad doesn’t work
    2. Disabling cells on subgrids async – dependency on related data
      1. Disabling works through OnRecordSelect
    3. Disabling cells in main grid (home area / sitemap / related grid / subgrid)
    4. The limitations for main grid vs subgrid
  5. Conclusion: what to use and when

For this blog let’s consider the following tables: Project, TimeEntries and Milestone. Each Project can have more TimeEntries and more Milestones. Each TimeEntry can be related to a Milestone from the list of the Milestones corresponding to this Project.

Disabling the complete grid

The first option to disable a complete grid is to customize it as “not editable”

Also, there is a case where the grid will be automatically disabled for you: if the form is disabled, the subgrid will be automatically be disabled.

Advertisement

Privacy Settings

But what if you need to change this dynamically, based on some other attributes? We can do that using the gridControl.setDisabled(true)

123const formContext = executionContext.getFormContext();const gridControl = formContext.ui.controls.get("Subgrid_TimeEntries");gridControl.setDisabled(true);

But of course, you probably will have a dependency to set it disabled or not. In that case we need to set the disabled state also when the form attribute changes.

Use-Case: Let’s consider that I have a form for the table “Project”, and there we have the subgrid for “TimeEntries”. The subgrid should be editable only if the project already started
(Project.StartDate< today).


Implementation: We register to form.onLoad, and there we disable the subgrid. You will end up with some similar code:

window.Project_Subgrid_TimeEntries = (function(){    functiondisableGridByStartDate(grid, form){        const dateFromAttribute = form?.getAttribute("diana_startdate");        const dateFromValue = dateFromAttribute?.getValue();        const isDisabled = dateFromAttribute==null|| (dateFromValue != null&& dateFromValue > newDate());        grid?.setDisabled(isDisabled);    }    functiononLoad(executionContext){        const formContext = executionContext.getFormContext();        const gridControl = formContext.ui.controls.get("Subgrid_TimeEntries");            disableGridByStartDate(gridControl, formContext);        //addOnChange on form attribute        const dateFromAttribute = formContext?.getAttribute("diana_startdate");        if(dateFromAttribute){            dateFromAttribute.addOnChange(()=>{                disableGridByStartDate(gridControl, formContext);                 //need to refresh the grid here                gridControl.refresh();            });        }    }      return{        onLoad    }})();

In form.OnLoad we’ll register Project_Subgrid_TimeEntries.onLoad (similar with the starting blog of this series, where we’ve went through the grid APIs).

Notice that in onChange event is not enough to call grid.setDisabled(true). We need to call also the grid.refresh() after that, otherwise we won’t see the change. Now the grid gets disabled/enabled every time I change the StartDate (in the past/future).

Visual feedback about the disabled state is very subtle

What the user will see is very subtle that probably nobody knows that is not editable until they try to edit a cell. As a comparison, this is the grid in editable mode:

Editable, when the focus is outside the selected row
Editable when the focus over the selected row

And now let’s see how looks a grid set as disabled:

No visual feedback as long only the rows are selected
Disabled grid, when the focus is over the selected row (the cell has a slight gray background and a black border instead of the blue one).
The last cell selected stays with a black border, even if I hover over other rows.

So a trained eye would notice that the grid is disabled right before trying to edit it, but in my opinion the most of the users would try to edit, and when they double-click to go into edit mode, the grid will navigate to the record form instead. Probably not the best user-experience. Honestly, I always get unpleasant surprised when it doesn’t work as I expect. But disabling the grid does offer a way to protect the data, since the user won’t be able to edit it. And probably we can train the users to detect the small feedback.

https://www.youtube.com/embed/9D2vmzvcT64?feature=oembedA short demo on disabling and enabling the complete grid

Working asynchronous

Of course here we don’t have issues. If the condition to disable/enable the grid is not available on the form, we can use the form onLoad and/or onChange events to retrieve the data and disable/enable the grid after that.

Disabling the complete nested grid

I am not aware of a way to get access to the nested grid using code. So probably it’s not possible to disable the nested grid using code. The only way I know is by setting the corresponding property in the customizing:

Visual feedback much better

Of course.. the property is called “disable editing” while the one for the main grid was called “Enable editing”. For the parent grid we don’t have the customizing option to set it on disabled. There is only the “editable or not” option (but oddly we can use code to set it on disabled (setDisabled), and that has the same effect as the “Not editable” customizing option). Not sure if it’s really different..
But the nested grids can be set on disabled. And the the visual feedback is much better: the cells are grayed out:

TimeEntries Table is now shown through a nested grid, and disabled nested grids are shown grayed out.

Disabling columns

In this case, we don’t have to disable the complete grid. Instead, we need to disable only specific columns. In this case we don’t differentiate on the data on the row, we just need to complete disable the one or more columns.

Some columns are disabled by default

Some columns are disabled by default. There are two types of “disabling”:

  • there are some columns grayed out. This seems to be the case with the columns which are not writable on the Dataverse side, like FormulaColumn or CreatedOn in my screenshot below. When the user hovers over these cells, the “uneditable” icon is shown on the right side of the cell.
  • some columns are disabled but the user sees that only on hover over those cells (like State or Status columns in my screenshot). The same “uneditable” icon is shown.
FormulaColumn and CreatedOn are grayed out, while State and StatusReason shows locked which a visual feedback only on hover.

But the other columns are editable. What if we want to lock them? We need some code to do that.

Disabling columns on subgrids

If we need to disable the columns of a subgrid, one approach would be to use the “OnRecordSelect” event. Later in this blog we will use that. But since the OnRecordSelect is not always triggered before the cells could be edited, and the visual representation if shown only on trying to edit the cell, we will try now another approach: by using the grid.onLoad event (event described in the grid API blog).

Advertisement

Privacy Settings

Use-Case: Let’s consider the tables as before: we have a form for “Project”, there a subgrid with the reported “Time Entries”. The project could be of different types, and only some projects allow milestones. That’s why we have a field on the form “Has milestones”. If the value is “no”, the Milestone column in the subgrid should be disabled.

Implementation: We have a similar code structure as before, using the form.onLoad to attach the “grid.onLoad” event. The “disableColumn” function gets the rows loaded through the executionContext, and each row gets the attribute “diana_milestone” and sets it on disabled using rowAttribute’s “setDisabled” method.

The controls for an attribute is a collection, but in the row context we cannot have more controls for the same attribute (as it can happen on the form), so we use always the first control (attribute.controls.get(0)).

window.Project_Subgrid_TimeEntries = (function(){     functiondisableColumn(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        const form = gridExecutionContext.getFormContext();        if(!grid) {            console.error("cound't find grid in onLoad");        }        const isEnabled = form.getAttribute("diana_hasmilestones")?.getValue() ?? true;                const rows = grid.getGrid().getRows().get();        //disable the column "diana_milestone"        for(const row of rows){            const attribute = row.data.entity.attributes.get("diana_milestone");                      attribute.controls.get(0).setDisabled(!isEnabled);                    }    }  //using form.onLoad to attach the grid.onLoad event    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(disableColumn);                  }    return{        onLoad    }})();

Using this script the “Milestone” column is locked, but the user sees the “uneditable” icon only on hover, or when he/she tries to edit the cell.

When the user scrolls down, more rows are loaded, another grid.onLoad event is triggered, and the function disableColumn with disable the cells also for the new loaded rows.

Disabling columns of a subgrid with dependency on form fields

The example of disabling the columns works fine. But disabling depends on the form attribute “HasMilestone”. If the user changes the value to “yes”, the Milestone column in the subgrid should be enabled again.

For that we need to attach the an onChange event to the “HasMilestone” attribute on the form. We can do that in form.OnLoad. All we have to do in the “onChange” event is to refresh the grid. That fill cause a little “flash” on the subgrid, but we’ve changed something on the form, not inside the grid itself.. so it should be ok.

window.Project_Subgrid_TimeEntries = (function(){      functiondisableColumn(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        const form = gridExecutionContext.getFormContext();        if(!grid) {            console.error("cound't find grid in onLoad");        }        const isEnabled = form.getAttribute("diana_hasmilestones")?.getValue() ?? true;                const rows = grid.getGrid().getRows().get();              //disable the column "diana_milestone"        for(const row of rows){            const attribute = row.data.entity.attributes.get("diana_milestone");            attribute.controls.get(0).setDisabled(!isEnabled);                    }    }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(disableColumn);                       form.getAttribute("diana_hasmilestones").addOnChange(()=>{                      grid.refresh();        });            }    return{        onLoad         }})();

In the pervious chapter we had a dependency on a form attribute. But what if we need to make a request first in order to get the data the disabling depends on? Let’s see a few solutions.

Solution 1. Disabling columns async in grid.OnLoad

One approach could be to use the grid.OnLoad event to make the request and disable the cell only after that.

Limitation: But the problem with that: the grid renders the cells, and the cells are not shown as disabled until the user tries to edit them. I’ll simulate the async process with a timeout. The promise is resolved after the grid.OnLoad event ends. The cell gets the disabled state, but the cells are not rendered anymore, so the user sees this only on trying to edit. So this is probably not the best option.

Hovering over cells doesn’t show them disabled. Only when the user tries to edit the cell, gets the “uneditable” icon.

The implementation: The code I’ve used:

window.Project_Subgrid_TimeEntries = (function(){    letdisabledValue = false;      functiongridOnLoad(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        if(!grid) {            console.error("cound't find grid in onLoad");        }                const rows = grid.getGrid().getRows().get();              window.setTimeout(() => { //emulates async request            disabledValue = true;            //disable the column "diana_milestone"            for(const row of rows){                const attribute = row.data.entity.attributes.get("diana_milestone");                attribute.controls.get(0).setDisabled(disabledValue);                        }        }, 5000);    }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(gridOnLoad);                                         }    return{        onLoad    }})();

Solution 2. Disabling async in form.OnLoad works better

The second way, would be to make the async request in form.OnLoad, and cache the needed data. In form.OnLoad I simulate the request with a timeout. Notice that I need to call the grid.refresh() for the case where the grid rendered before the promise is back.

Advertisement

Privacy Settings

The implementation: The code I’ve used:

window.Project_Subgrid_TimeEntries = (function(){    letdisabledValue = false;      functiondisableColumn(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        if(!grid) {            console.error("cound't find grid in onLoad");        }                const rows = grid.getGrid().getRows().get();              //disable the column "diana_milestone"        for(const row of rows){            const attribute = row.data.entity.attributes.get("diana_milestone");            attribute.controls.get(0).setDisabled(disabledValue);                    }    }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(disableColumn);                        window.setTimeout(() => {            disabledValue = true;            grid?.refresh();        }, 5000);                           }    return{        onLoad          }})();

Limitation: This works pretty well. The cells are disabled also onHover. The problem with is that the request might be slow. If the grid renders before the promise is resolved, the cells are editable for a short while. So the better approach is to set the cells on disabled, and enable it only if need after that. That causes a little flash.
Another issue: we need to load the data in form.onLoad, even if the grid is not used, and maybe on another tab.

Solution 3. Disabling async in form.OnLoad impoved

To improve the previous version, we could hide the grid control and show it only after the promise is completed. In that case no render is made until everything is prepared.

Here is the code I’ve used:

window.Project_Subgrid_TimeEntries = (function(){    letdisabledValue = false;      functiondisableColumn(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        if(!grid) {            console.error("cound't find grid in onLoad");        }                const rows = grid.getGrid().getRows().get();              //disable the column "diana_milestone"        for(const row of rows){            const attribute = row.data.entity.attributes.get("diana_milestone");            attribute.controls.get(0).setDisabled(disabledValue);                    }    }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(disableColumn);        grid.setVisible(false);                        window.setTimeout(() => {            disabledValue = true;            grid.setVisible(true);        }, 5000);                     }   return{        onLoad       }})();

Since we don’t need a grid refresh (no image flickering) and the data can be loaded when we are ready, this could be the best option.

Solution 4. Disabling columns async based on “async form.OnLoad”

Another way to work with async requests before disabling, would be to use the asynchronous form.OnLoad. For that we need first to enable the “Async onload hander” in the app settings.

Then we don’t need a grid.refresh(), and the data is safe, since the controls are not rendered until the promise is resolved.

Super Early Bird
Limitationno control is rendered until the promise is completed. If you expect the promise to be completed fast, is not that bad. Maybe you could make also a lot of other requests in the meantime, for other requirements. But if the promise could take a little longer, it’s not the best user experience since it affects the complete form and the user cannot see anything in the meanwhile.

Advertisement

Privacy Settings

Implementation: Notice that I return the promise from form.OnLoad so the form rendering waits for the promise.

window.Project_Subgrid_TimeEntries = (function(){    letdisabledValue = false;      functiondisableColumn(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        if(!grid) {            console.error("cound't find grid in onLoad");        }                const rows = grid.getGrid().getRows().get();              //disable the column "diana_milestone"        for(const row of rows){            const attribute = row.data.entity.attributes.get("diana_milestone");            attribute.controls.get(0).setDisabled(disabledValue);                    }    }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");               grid.addOnLoad(disableColumn);        const {promise, resolve} = Promise.withResolvers();        window.setTimeout(() => {            disabledValue = true;            resolve();        }, 5000);        returnpromise;                    }    return{        onLoad    }})();

Solution 5. Using tab “onVisible”: tabStateChanged

This is a small change to the version 3, which can be applied if the subgrid is not placed on the first tab. We hide the grid on form.onLoad and show it only when the tab is visible and the data is retrieved. The code is similar with the solution “3”, but the onLoad looks like this

//we start with undefined. Then we know when the request was made and the disabled value is set letdisabledValue = undefined;  const onLoad = (executionContext) => {      const form = executionContext.getFormContext();      const grid = form.ui.controls.get("Subgrid_TimeEntries");             grid.addOnLoad(disableColumn);      grid.setVisible(false);   //after hiding the gridControl, we attach to OnTabStateChhange event      const tab = form.ui.tabs.get("tab_timeentries");      tab.addTabStateChange(() => {          if(disabledValue != null) return; //is the value was not already retrieved          window.setTimeout(() => { //make only now the async request              disabledValue = true;              grid.setVisible(true);          }, 5000);      });                    }

The advantage of this solution is that we make the requests only if the tab is activated. If the user doesn’t visit the tab, the data is not fetched.

Disable columns of a grid in the main area (sitemap)?

Here things get harder, since we don’t have the grid.OnLoad event, as we did for subgrids.

I was thinking that it could be a solution to add copies of the column as FormulaColumn, That could work. But it has some serious cons: like bloating the table with technical columns, and loosing some features for some column types (like links for lookups). But the main issue with this approach is that the user is able to add columns to the view .. and could add the original column which is not disabled. So this is not an option.

Implementation using OnRecordSelect: The only way we can disable the columns for the main grid is by using the OnRecordSelect event.

The script to disable “Milestone” columns looks like this:

window.Project_Subgrid_TimeEntries = (function(){    functiononRecordSelect(selectExecutionContext){           const row = selectExecutionContext.getEventSource();        const attribute = row?.attributes?.get("diana_milestone");        if(!attribute) return;               attribute.controls?.get(0)?.setDisabled(true);    }    return{        onRecordSelect    }})();

To register the script we need the switch to “classic”, select the Entity, and choose the “Events” tab:

Limitation: the cells are shown as disabled as soon we’ve visited the row at least once. If we try to edit the cell, the grid realizes it’s locked and shows the symbol from now on.

The visual feedback is not the biggest problem. As I’ve shown in the other blog, the PowerApps grid allows the users to navigate by keys, and edit cells that way without triggering OnRecordSelect. In the example below:

  • Click on Name on Row 2 and edit the name
  • Click Enter
  • User “Arrow Down” to navigate to the Name in another row
  • Type the value for the name on another row, sets the row in edit mode without triggering the “OnRecordSelect”. You see that the lookup “Milestone” is in edit mode, since the shown text is “Select lookup”
  • Use “TAB” to navigate to Milestone. You are able to edit the Milestone , since is not the selected row.
1. Use key navigation to select the Name on another row
2. Now it’s possible to edit the Milestone, since the OnRecordSelect was not triggered

Async limitation

Disabling the columns for main area (sitemap) has a few limitations. The first limitation is the one we just saw: cell editing is possible without selection the row.

Advertisement

Privacy Settings

Another one is that we don’t have an onLoad event to retrieve related data.

For instance we cannot disable columns depending on other columns that are not shown in the view (it could be a column from the same table or from a related lookup). The OnRecordSelect won’t wait, so at least the first selection of the row might provide the data needed for disable too late.

Disabling specific cells on subgrids

So we’ve saw how to disable a complete column. But what if we need to disable the cells only on some rows, depending on the row data?

Disabling specific cells in grid.OnLoad doesn’t work

The first attempt would be to use the same idea as in disabling the complete columns:

  • load the related data in form.onLoad
  • use the grid.onLoad event to disable the cells

Unfortunately this doesn’t work. I have the feeling that attribute.controls always access the same instance of the control and that accessing a specific instance of the control works only if we’ve selected the row. It seems to me that the value applied is only the one for the last row.

This is the code I’ve tried out and didn’t work (my cells were still editable):

window.Project_Subgrid_TimeEntries = (function(){       letallRelatedData = {};      const retrieveRelatedDataByRowIds = async(id) => {                   //as shown above    }    functiongetMilestoneDisabledState(attribute){    //as shown above    }    functiongridOnLoad(gridExecutionContext){        const grid = gridExecutionContext.getEventSource();        if(!grid) {            console.error("cound't find grid in onLoad");        }                        for(const row of grid.getGrid().getRows().get()){            const attributeMilestone = row.data.entity.attributes.get("diana_milestone");            const milestoneLocked = getMilestoneDisabledState(attributeMilestone);             for(const attribute of row.data.entity.attributes.get()){                                              attribute.controls.get(0).setDisabled(milestoneLocked);                            }        }            }        const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        const grid = form.ui.controls.get("Subgrid_TimeEntries");           retrieveRelatedDataByRowIds(form.data.entity.getId());             grid.addOnLoad(gridOnLoad);                               }    return{        onLoad    }})();

This time we need more related data, since every row might have another dependency. In a lot of cases we might need to retrieve the related data first, which we can do in form.onLoad (since we saw that the grid.onLoad is too late to apply changes).

Use-Case: In this part I would like to disable all cells of a row, if the attribute “Milestone” is inactive (Milestone.statecode == 1). So we need to retrieve the state of all Milestones associated with the project, and cache that, so we can use it when the events are triggered

Implementation: I will need some help functions. For instance to retrieve data and to check if the row should be disabled:

window.Project_Subgrid_TimeEntries = (function(){       //used to cache the related data    letallRelatedData = {};    const retrieveRelatedDataByRowIds = async(id) => {                    const fetchXml = ["<fetch mapping='logical'>",             "<entity name='diana_milestone'>",            "<attribute name='diana_milestoneid'/><attribute name='diana_name'/><attribute name='statecode'/>",                        "<filter type='and'>",                            `<condition attribute='diana_project'operator='eq'value='${id}'/>`,            "</filter>",            "</entity></fetch>"].join("");        const result = awaitXrm.WebApi.retrieveMultipleRecords("diana_milestone", "?fetchXml="+fetchXml);        const newData = result.entities.reduce((acc, entity) => {            acc[entity.diana_milestoneid] = entity.statecode;            returnacc;        }, {});       allRelatedData = newData;        console.log("allRelatedData", allRelatedData);    }    functiongetMilestoneDisabledState(attribute){        const milestioneValue = attribute.getValue();                if(milestioneValue == null|| milestioneValue.length == 0){                        return;        }        const milestioneid = milestioneValue[0]?.id?.toLowerCase()?.replace("{", "")?.replace("}","");        returnallRelatedData[milestioneid] == 1;       }})();

Disabling works through OnRecordSelect

So we need to use the OnRecordSelect event to disable the cell. This one works (except for the case when the user navigates by key, since the OnRecordSelect is not triggered in that case, as we saw before)

Advertisement

Privacy Settings

Implementation:

window.Project_Subgrid_TimeEntries = (function(){              letallRelatedData = {};    const retrieveRelatedDataByRowIds = async(id) => {                   //as shown above    }    functiongetMilestoneDisabledState(attribute){       //as shown anbove      }    const onLoad = (executionContext) => {        const form = executionContext.getFormContext();        retrieveRelatedDataByRowIds(form.data.entity.getId());         }    functiononRecordSelect(eventContext){        const rowEntity = eventContext.getEventSource();        const attribute = rowEntity?.attributes?.get("diana_milestone");        if(!attribute) return;        const milestoneLocked = getMilestoneDisabledState(attribute);        for(const attribute of rowEntity.attributes.get()){                                          attribute.controls.get(0).setDisabled(milestoneLocked);                        }     }      return{        onLoad,         onRecordSelect    }})();

Of course I need to register both the “form onLoad” and the “grid onRecordSelect” event using the customizing (as described in the grid API blog)

Limitation: Since this is based onRecordSelect, the uses sees the disabled state only after the row is selected at least once.

In case we have all the needed data inside the view, we just need to use the OnRecordSelect and disable the controls.

Use-Case: In the following example I’m using the column “Name”. If the value is “PCF” I’ll disable the milestone.

Implementation: We need the OnRecordSelect event to disable the cells. Since the dependency is on a column from the view, we need also to register the OnChange event for the Name.

The code:

window.Project_Subgrid_TimeEntries = (function(){        functiononRecordSelect(selectExecutionContext){           const row = selectExecutionContext.getEventSource();        const attribute = row?.attributes?.get("diana_milestone");        if(!attribute) return;        const nameAttribute = row.attributes.get("diana_name");        if(!nameAttribute){            console.error("Couldn't find diana_name attribute");            return;        }        const milestoneLocked = nameAttribute.getValue() == "PCF";        attribute.controls?.get(0)?.setDisabled(milestoneLocked);    }    functiononNameChange(executionContext){        const row = executionContext.getFormContext();        const attributeMilestone = row.data.entity.attributes.get("diana_milestone");        if(!attributeMilestone) return;        const milestoneLocked = row.data.entity.attributes.get("diana_name").getValue() == "PCF";        attributeMilestone.controls.get(0).setDisabled(milestoneLocked);    }    return{                onRecordSelect,        onNameChange    }})();

The result:

When we change the Name to “PCF” the Milestone is shown as disabled
But it’s not enough to only select a row to see the disabled state. We need to enter the “edit mode” for at least once cell in that row

LimitationsIf the data is not available in the view, I am not aware of a way to retrieve data async beforehand, so we cannot use the API to disable the cells. For that case we can only use the customizer control for Power Apps grid (PCF for Power Apps Grid).

The limitations for main grid vs subgrid

As we saw, in the subgrid we can use the form onLoad event to retrieve related data. In the home area/sitemap we don’t have that event. As a workaround we could try to add the related data in the view, to have it available when the row is selected. But the user can remove the columns and the disable rule cannot be evaluated anymore.

After removing the “Name” column data is not available in the row anymore, so the disable rule cannot be evaluated

Conclusion: what to use and when

It looks pretty hard to remember which rule to be used in which case. I’ve tried to make a flowchart to help you decide which solution to be used.

Advertisement

Privacy Settings

For a subgrid I follow these rules:

For a grid in the home area, we have some serious limitations. There we could only choose between:

  • disable in the customizing
  • use OnRecordSelect (maybe in combination with record OnChange)

Know the limitations! Where this is no solution available or the solution is not good enough, we will need a customizer control. I have an older blog about disabling using the customizer control (PCF). More on this to come soon.

Maybe you have a better solution for a special use-case. I would like to hear about that, to learn from each other.

About the Author

Diana Birkelbach

Diana Birkelbach, MVP

Dianamics PCF Lady | Microsoft MVP | Blogger | Power Platform Community Super User | 👩‍💻 Dynamics 365 & PowerPlatform Developer | ORBIS SE

Reference:

Birkelbach, D (2025). Power Apps Grid API – Disabling. Available at: Power Apps Grid API – Disabling – Dianamics PCF Lady [Accessed: 15th January 2025].

Share this on...

Rate this Post:

Share:

Topics:

Power Apps

Tags: