Loading...

How To Add Custom Filters That Are Asynchronous In Dynamics 365 Customer Engagement

How To Add Custom Filters That Are Asynchronous In Dynamics 365 Customer Engagement
Adding custom filters to lookup entities isn’t entirely simple.  It involves adding a trigger to the PreSearch function in which a call is made to set the filter on the control using addCustomFilter. The PreSearch event must be synchronous since it is triggered once the user has selected the lookup, and needs to apply the filter right then. 

But what if you want to do something like query the lookup entity for the given filter, and relax the criteria if nothing is found (i.e. Search by First and Last name, unless no match is found, then search by Last Name only).  That either involves a blocking synchronous call (Yuck), or some form of black magic to make something asynchronous behave synchronously.  Well, maybe not black magic, just 40 lines of TypeScript:

const _preSearchFilters = {} as { [index:string]:string };
 
/**
* Handles adding a PreSearch that is Asynchronous.  To be called in the onLoad of the form.  This will trigger the getFilter method to attempt to assign the filter.
* The Lookup Attribute will be disabled until the getFilter method has finished executing
* @param info Object that contains the following properties:
*             control - Name of the lookup control to add the PreSearch Filter for.
*             filteringEntity - If entityLogicalName is not specified, the filter will be applied to all entities valid for the Lookup control.
*             getFilter - Function that returns the Promise of the filter Xml.
*             triggeringAttributes - List of attributes, that if changed, will result in the filter needing to be updated.

*/
export function addAsyncPreSearch(info: { control: string, filteringEntity?: string, getFilter: () => Promise<string>, triggeringAttributes?: string[]}) {
    const setAsyncFilter = async () => {
        const enablePostFilter = !Xrm.Page.getControl(info.control).getDisabled();
        if (enablePostFilter) {
            Xrm.Page.getControl(info.control).setDisabled(true);
        }
 
        try {
            _preSearchFilters[info.control] = await info.getFilter();
        } catch (e) {
            console.error(`Error occurred attempting to get the preSearch filter for ${info.control}`, e);
            _preSearchFilters[info.control] = "";
        } finally {
            if (enablePostFilter) {
                Xrm.Page.getControl(info.control).setDisabled(false);
            }
        }
    };
 
    Xrm.Page.getControl(info.control).addPreSearch((context: Xrm.Page.EventContext) => {
        const ctrl = (context.getEventSource() as Xrm.Page.LookupControl);
        if (ctrl && ctrl.addCustomFilter) {
            ctrl.addCustomFilter(_preSearchFilters[ctrl.getName()], info.filteringEntity);
        }
    });
 
    if (info.triggeringAttributes && info.triggeringAttributes.length > 0) {
        for (const att of info.triggeringAttributes) {
     Xrm.Page.getAttribute(att).addOnChange(setAsyncFilter);
}
    }
 
    setAsyncFilter();
}

That’s a lot of code, let’s walk through it.

First, a module level field (_preSearchFilters) that stores the filters for each control is declared and instantiated (note there is an assumption that you will only ever have one filter per control, which I think is pretty safe).  For those of you new to TypeScript, “{ [index:string]:string }” is how you define that the field is an object, which is indexable by a string, returning string.  This would be equivalent to a C# Dictionary<string,string>.

Next is a nested function “setAsyncFilter” that wraps the async function “getFilter” that is passed in.  It handles 2 things, disabling the control until the async function finishes, and storing the result of the async function in the “_preSearchFilters”.  If the control doesn’t get disabled, then there is a chance that the user could attempt to perform a search the async function determines what the filter should actually be.

After the “setAsyncFilter” definition comes the first code that is actually executed, a call to get the control, and add an anonymous function as a preSearch.  The function just calls “addCustomFilter” on the control that triggered the action, with the optional filteringEntity parameter.

Next to last is an if statement to trigger the “setAsyncFilter” function, each and every time a field is updated that could potentially change the filter.  This if followed by the last step, a call to “setAsyncFilter” to initialize the value in the “_preSearchFilters: field.

To use this function, just call it in the onLoad of the form:

CommonLib.addAsyncPreSearch({
    control: Lead.fields.installFee,
    filteringEntity: "new_installationfee",
    getFilter: getInstallationFilter,
    triggeringAttributes: [Lead.fields.campaign,
                           Lead.fields.callType]
});
The call above adds an Async PreSearch to the installFee control.  The filter is to be applied to the “new_installationfee” entity, and is generated by the Promise returning function, “getInstallationFilter”.  Finally, a trigger is added to refresh the filter whenever the campaign or callType fields are updated.  That's it.

Chalk one up to TypeScript for handling the async/await.  Just don’t forget to polyfill your Promise call, or your IE 11 users won’t be too happy Winking smile

Published on:

Learn more
.Net Dust
.Net Dust

NULL

Share post:

Related posts

Embed a Power BI Report as a System Dashboard in Dynamics 365 CE Using Environment Variables

Introduction In today’s data-driven world, integrating powerful analytics tools like Power BI with your Dynamics 365 CE can significantl...

17 hours ago

How to amplify contact centers and field service operations with AI

In this video, Jeff Comstock, Corporate Vice President of Dynamics 365 Customer Service, discusses the transformative power of Copilot in enha...

9 days ago

Decrement field value automatically using workflow in dataverse or dynamics 365

In Dataverse (formerly known as Common Data Service) or Dynamics 365, you can automate the decrementing of a field value using workflows. With...

9 days ago

Fuzzy Matching to Eliminate Duplicates in Dynamics 365 CRM with DeDupeD!

If you're struggling with "fuzzy duplicates" in your Dynamics 365 CRM, the solution is here! Meet DeDupeD, the tool designed to eliminate chao...

10 days ago

{How to} Configure custom card for the inbox dynamics 365 Omnichannel

If you're looking to configure custom card settings for your inbox on Dynamics 365 Omnichannel, then this tutorial is for you. In it, you'll l...

10 days ago

{Do you know} Create mulitple cases from email sent to multiple mailboxes in Dynamics 365 Omnichannel

If you're looking to create multiple cases from an email sent to multiple mailboxes in Dynamics 365 Omnichannel, this post has got you covered...

11 days ago

We need to talk about... Dynamics 365... Dataverse

In this blog post, the author sheds light on an important feature that appears in Microsoft's release waves update for 2024 - Dynamics 365 Dat...

11 days ago

What is WhatsApp Business API – 2024 Guide to Integrate with Microsoft Dynamics 365 CRM!

Are you curious about the WhatsApp Business API? Look no further than this informative guide that provides the essential walkthrough needed to...

11 days ago

How to Enable 2024 Release Wave 1 Updates in the Microsoft Dynamics 365 CE Environment?

If you are trying to enable the 2024 Release Wave 1 updates in your Microsoft Dynamics 365 CE Environment, you can follow these simple steps: ...

11 days ago

Fixed – The latitude or longitude for the User record associated with this resource is invalid – Dynamics 365 Field Service /Dataverse

If you're facing the error message "The latitude or longitude for the User record associated with this resource is invalid" while trying to se...

12 days ago
Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!
* Yes, I agree to the privacy policy