How to Apply Views or Filters to Buttons on a Subgrid in Dynamics 365

 In Dynamics 365, subgrids are an essential feature that provide users with the ability to interact with related data directly from a form. In some scenarios, you may need to enhance the default functionality of subgrid buttons, such as the "Add Existing" or custom action buttons, by applying specific views or filters to control the data shown to users. This guide will walk you through the process of dynamically applying views or filters to buttons on a subgrid, providing an improved user experience.

1. Understanding the Requirements

When adding or associating records through buttons on a subgrid, it is often necessary to limit which records are displayed to the user. This can be accomplished by dynamically applying views or custom filters. For instance, you might want the "Add Existing" button to only show records that meet specific criteria, such as active items or records associated with a particular category. Achieving this requires utilizing JavaScript in combination with Dynamics 365's Xrm API and tools like Ribbon Workbench.

2. Key Techniques: Using JavaScript and Xrm APIs

To apply views or filters to subgrid buttons, you can use the Xrm.Utility.lookupObjects method combined with Xrm.WebApi.retrieveMultipleRecords. These tools enable you to customize the lookup dialog, select specific views, and even disable recent records (MRU). Below, we will go over how to use these techniques effectively.

2.1 Retrieving View IDs Dynamically

The first step in applying a custom view to a button on a subgrid is to retrieve the GUID of the desired view dynamically. This is done using the Xrm.WebApi.retrieveMultipleRecords function to query the savedquery entity by the view name. Here is a sample function:

async function comm_GetViewIDByName(viewName)
{
    try {
        // Build query to select the view ID by name
        var query = "?$select=savedqueryid&$filter=name eq '" + viewName + "'";

        // Use Xrm.WebApi.retrieveMultipleRecords to fetch the view by name
        const result = await Xrm.WebApi.retrieveMultipleRecords("savedquery", query);

        if (result.entities.length > 0)
        {
            var viewId = result.entities[0].savedqueryid; // Get the view ID
            console.log("View ID retrieved: " + viewId);
            return viewId;
        }
        else
        {
            console.error("No view found with the name: " + viewName);
            throw new Error("No view found with the name: " + viewName);
        }
    } catch (error) {
        console.error("Error retrieving views: " + error.message);
        throw new Error("Error retrieving views: " + error.message);
    }
}

This function allows you to dynamically obtain the GUID of the view by name, making it reusable across different scenarios where different views are required.

2.2 Applying Views and Filters to the Subgrid Button

Once you have retrieved the view ID, you can use it to customize the lookup dialog for your subgrid buttons. You can also apply custom filters to further refine the data displayed in the lookup dialog. Below are two options you can use separately or even combine together to suit your requirements.

Option 1: Apply a View to the Lookup

Here's a function that opens a lookup for products using a specific view ID, which has been dynamically retrieved:

async function openCustomProductLookup(primaryControl)
{
    try {
        // Retrieve the view ID dynamically
        const viewId = await comm_GetViewIDByName("Demo View - Product");

        var lookupOptions =
        {
            allowMultiSelect: true,
            defaultEntityType: "product",
            entityTypes: ["product"],
            defaultViewId: viewId, // Use the dynamically retrieved view GUID
            viewIds: [viewId], // No additional filters needed as the view contains required filters
            disableMru: true // Disables recent records in the lookup dialog
        };

        Xrm.Utility.lookupObjects(lookupOptions).then(function(results)
        {
            if (results.length > 0)
            {
                associateProductsWithParent(primaryControl, results);
            }
        });
    } catch (error) {
        console.error("Failed to open product lookup: " + error.message);
        alert("Failed to open product lookup: " + error.message);
    }
}

In this function, the viewId is retrieved dynamically, and then applied to the lookup options, ensuring that users only see the records relevant to their task. The disableMru parameter is used to disable the display of recent records, making the lookup more focused.

async function openFilteredProductLookup(primaryControl)
{
    try {
        var lookupOptions =
        {
            allowMultiSelect: true,
            defaultEntityType: "product",
            entityTypes: ["product"],
            disableMru: true, // Disables recent records in the lookup dialog
            filters: [
                {
                    filterXml: "<filter type='and'><condition attribute='statecode' operator='eq' value='0' /></filter>", // Filter to show only active products
                    entityLogicalName: "product"
                }
            ]
        };

        Xrm.Utility.lookupObjects(lookupOptions).then(function(results)
        {
            if (results.length > 0)
            {
                associateProductsWithParent(primaryControl, results);
            }
        });
    } catch (error) {
        console.error("Failed to open product lookup with filters: " + error.message);
        alert("Failed to open product lookup with filters: " + error.message);
    }
}

n this example, the filterXml parameter is used to apply a filter that only shows active products (statecode = 0). This allows you to tailor the lookup dialog to show only the records that meet specific conditions, providing more precise control over what is displayed to the user.

Combining Views and Filters

You can also apply both a view and a filter simultaneously to further refine the records shown to the user. By using both the viewIds parameter and the filters parameter in the lookupOptions object, you can ensure that the lookup dialog provides an even more focused dataset, meeting both predefined and dynamic criteria.

   var lookupOptions =

        {
            allowMultiSelect: true,
            defaultEntityType: "product",
            entityTypes: ["product"],
            defaultViewId: viewId, // Use the dynamically retrieved view GUID
            viewIds: [viewId],
            disableMru: true, // Disables recent records in the lookup dialog
            filters: [
                {
                    filterXml: "<filter type='and'><condition attribute='statecode' operator='eq' value='0' /></filter>", // Filter to show only active products
                    entityLogicalName: "product"
                }
            ]
        };

2.4 The associateProductsWithParent Function

The associateProductsWithParent function is used to associate the selected products with the parent record in the form. Here is an example implementation:

function associateProductsWithParent(primaryControl, selectedProducts)
{
    var parentProductId = primaryControl.data.entity.getId().replace("{", "").replace("}", "");

    selectedProducts.forEach(function(product) {
        var productId = product.id.replace("{", "").replace("}", "");
        var entityReference = {
            "@odata.id": Xrm.Utility.getGlobalContext().getClientUrl() + "/api/data/v9.0/products(" + productId + ")"
        };

        Xrm.WebApi.createRecord("dsl_course_course_stream", {
            "productidone@odata.bind": "/products(" + parentProductId + ")",
            "productidtwo@odata.bind": "/products(" + productId + ")"
        }).then(
            function success(result)
            {
                console.log("Association created successfully with ID: " + result.id);
            },
            function error(err)
            {
                console.error("Error creating association: " + err.message);
            }
        );
    });
}


This function uses Xrm.WebApi.createRecord to create an entry in the relationship (bridge) table that links the parent product with each selected product. It is a straightforward way to manage associations between entities, ensuring the correct records are linked as intended.

3. Integrating with Ribbon Workbench

After defining the JavaScript functions, the next step is to integrate them into your subgrid button using Ribbon Workbench:

  1. Open Ribbon Workbench and load the solution containing the target entity.

  2. Locate the subgrid button you want to customize, such as the "Add Existing" button, or Add a new custom button.

  3. Replace the default command for the button with a custom JavaScript action that invokes openCustomProductLookup() or openFilteredProductLookup().

This setup ensures that your button dynamically filters the records based on the criteria defined in the JavaScript function.



4. Benefits of Using Dynamic Views and Filters

  • Improved User Experience: Applying views and filters based on context helps to simplify the user interface and minimize errors.

  • Flexible Customization: By retrieving view IDs dynamically or applying filters, you can reuse functions for different buttons or views without hardcoding specific GUIDs.

  • Control Over Displayed Data: Ensuring that only relevant records are displayed can significantly improve data accuracy and usability for end users.

5. Conclusion

Applying views or filters to buttons on subgrids in Dynamics 365 requires leveraging the power of JavaScript and the Xrm API to create dynamic, flexible user experiences. By using functions like comm_GetViewIDByName, openCustomProductLookup, and openFilteredProductLookup, you can efficiently control what data is shown to users and make subgrid interactions more intuitive.

Start by dynamically retrieving the view ID, applying custom filters, and customizing the subgrid buttons using Ribbon Workbench, and you'll provide a more tailored, efficient experience for your users.



No comments:

Post a Comment