" MicromOne: How to Disable Subgrids in Microsoft Dynamics 365 Using JavaScript

Pagine

How to Disable Subgrids in Microsoft Dynamics 365 Using JavaScript

When working with Microsoft Dynamics 365, there are situations where we want users to see the records displayed in a subgrid but prevent them from interacting with the grid.

For example, a form may contain the following subgrids:

  • Activities

  • Marketing Lists

  • Contacts

  • Opportunities

  • Custom related records

Sometimes the business requirement is not to hide these grids, but simply to make them read-only from the user interface.

In this article, we will look at a JavaScript approach that disables user interaction with specific Dynamics 365 subgrids.

The basic approach

The idea is simple:

  1. Execute JavaScript when the form loads.

  2. Retrieve the Dynamics 365 formContext.

  3. Load the application's utility functions.

  4. Locate the required subgrid in the DOM.

  5. Disable mouse interaction with the grid.

The following example disables the Activities and Marketing Lists grids:

utilities.disableAllGrids("Activities");
utilities.disableAllGrids("MarketingLists");

The grid names passed to the function correspond to the data-control-name attribute used by the Dynamics 365 interface.

Form OnLoad JavaScript

A typical form OnLoad function can look like this:

'use strict';

if (typeof (DPS) === "undefined") {
    var DPS = {};
}

if (typeof (DPS.Main) === "undefined") {
    DPS.Main = {
        __namespace: true
    };
}

DPS.Main = new function () {

    var _self = this;
    var _formContext = null;
    var _executionContext = null;
    var utilities = null;

    _self.OnLoad = async function (executionContext) {

        if (executionContext) {
            _formContext = executionContext.getFormContext();
            _executionContext = executionContext;
        }

        utilities = DPS.utilities;

        utilities.load(_formContext);

        utilities.disableAllGrids("Activities");
        utilities.disableAllGrids("MarketingLists");
    };
};

The executionContext is important because it allows us to obtain the current form context:

_formContext = executionContext.getFormContext();

Using getFormContext() is preferable to relying on older APIs such as Xrm.Page.

Creating a reusable utility function

Instead of writing separate code for every subgrid, we can create a reusable function.

For example:

'use strict';

if (typeof (DPS) === "undefined") {
    var DPS = {};
}

if (typeof DPS.utilities === "undefined") {
    DPS.utilities = {
        __namespace: true
    };
}

DPS.utilities = new function () {

    var _self = this;

    _self.disableAllGrids = function (dataControlName) {

        var interval = setInterval(function () {

            const grid = window.parent.document.querySelector(
                '[data-control-name="' + dataControlName + '"]'
            );

            if (grid) {

                grid.style.pointerEvents = "none";
                grid.style.opacity = "0.8";
                grid.style.cursor = "not-allowed";

                clearInterval(interval);
            }

        }, 500);
    };
};

The function accepts the name of the grid:

disableAllGrids("Activities");

or:

disableAllGrids("MarketingLists");

This makes the function reusable for multiple subgrids.

Why use setInterval()?

One of the challenges with Dynamics 365 subgrids is that they are not necessarily available in the DOM immediately when the form's OnLoad event executes.

The form can load first while the subgrid is rendered a little later.

If we execute:

document.querySelector(...)

immediately, the result may be null.

For this reason, the example uses:

var interval = setInterval(function () {
    ...
}, 500);

The function checks every 500 milliseconds until the grid becomes available.

Once the grid is found, the interval is stopped:

clearInterval(interval);

This prevents unnecessary repeated execution.

Locating the subgrid

The key part of the function is:

const grid = window.parent.document.querySelector(
    '[data-control-name="' + dataControlName + '"]'
);

For example, when:

dataControlName = "Activities";

the selector becomes:

[data-control-name="Activities"]

querySelector() then attempts to find the corresponding HTML element.

Once the element is found, we can manipulate its CSS properties.

Preventing user interaction

The most important line is:

grid.style.pointerEvents = "none";

CSS pointer-events: none prevents mouse interaction with the element.

The example also changes the appearance of the grid:

grid.style.opacity = "0.8";
grid.style.cursor = "not-allowed";

The result is a visual indication that the grid is not intended to be interactive.

Conceptually, the user sees the records but cannot click the grid controls.

Complete example

The following is a simplified complete implementation.

Main.js

'use strict';

if (typeof DPS === "undefined") {
    var DPS = {};
}

if (typeof DPS.Main === "undefined") {
    DPS.Main = {
        __namespace: true
    };
}

DPS.Main = new function () {

    var _self = this;
    var _formContext = null;

    _self.OnLoad = async function (executionContext) {

        if (executionContext) {
            _formContext = executionContext.getFormContext();
        }

        var utilities = DPS.utilities;

        utilities.load(_formContext);

        utilities.disableAllGrids("Activities");
        utilities.disableAllGrids("MarketingLists");
    };
};

Utilities.js

'use strict';

if (typeof DPS === "undefined") {
    var DPS = {};
}

if (typeof DPS.utilities === "undefined") {
    DPS.utilities = {
        __namespace: true
    };
}

DPS.utilities = new function () {

    var _self = this;

    _self.disableAllGrids = function (dataControlName) {

        var interval = setInterval(function () {

            var grid = window.parent.document.querySelector(
                '[data-control-name="' + dataControlName + '"]'
            );

            if (grid) {

                grid.style.pointerEvents = "none";
                grid.style.opacity = "0.8";
                grid.style.cursor = "not-allowed";

                clearInterval(interval);
            }

        }, 500);
    };
};

Is this a true security solution?

This is an important distinction.

Setting:

pointerEvents = "none";

only prevents interaction with the user interface. It does not change the user's Dynamics 365 security privileges.

If a user has permission to create, update, or delete records, JavaScript should not be considered a security mechanism for preventing those operations.

For real security requirements, use the appropriate Dataverse security roles, privileges, business rules, server-side validation, or other supported platform mechanisms.

The JavaScript approach is primarily a UI/UX solution.

DOM manipulation and Dynamics 365

There is another consideration with this technique.

The code accesses:

window.parent.document

and searches for:

[data-control-name="..."]

This means it depends on the current HTML implementation of the Dynamics 365 interface.

Microsoft can change internal DOM structures and CSS classes as the platform evolves. Therefore, DOM manipulation is generally more fragile than using officially supported client APIs.

For this reason, this technique should be used carefully and tested after Dynamics 365 updates.

When is this approach useful?

This technique can be useful when the requirement is essentially:

"The user should be able to see the related records, but should not be able to interact with this subgrid."

Typical scenarios include:

  • Showing Activities without allowing interaction.

  • Displaying related Marketing Lists as informational data.

  • Preventing users from opening grid commands.

  • Creating a UI that behaves differently depending on the record state.

  • Temporarily preventing interaction while another operation is taking place.

For example, the function can easily be reused:

utilities.disableAllGrids("Contacts");
utilities.disableAllGrids("Opportunities");
utilities.disableAllGrids("Activities");



Disabling Dynamics 365 subgrids with JavaScript can be useful when a business process requires a read-only visual representation of related records without hiding the subgrid completely.

The main technique is to wait until the grid is rendered, locate it using its data-control-name, and disable pointer interaction:

grid.style.pointerEvents = "none";

However, it is important to remember that this is a client-side UI technique, not a security mechanism.

For production implementations, always consider whether the requirement can be achieved through supported Dynamics 365 and Dataverse APIs or security configuration before relying on direct DOM manipulation.