Simplified Show/Hide Ribbon Button in Dynamics 365 based on Asynchronous Operation Result using TypeScript ES6

Recently I had a requirement to show/hide button based on some asynchronous operation result. In your case async operation could be anything such as getting data from some external API, or querying dynamics 365 using Xrm.WebApi.retrieveRecord which returns promise instead of the actual record value.

Toggling ribbon button’s visibility based on asynchronous operation is tricky because before the asynchronous operation completes and result is available function call exits, and true result is never returned and therefore button show/hide doesn’t works as expected.

With a synchronous operation call it works perfectly fine because the function doesn’t exits until we have the required value.

In search of the solution I found a nice article by Andrew Butenko, which actually solved the problem and works fine but the implementation is tricky and not straightforward.

The solution in the linked article has the following high level steps

  • Maintain two flags isAsyncOperationCompleted and isButtonEnabled both initialised to false, and are in outer scope of the function.
  • On page load, function defined on enable rule will be called.
  • Inside the function if isAsyncOperationCompleted is true then return the isButtonEnabled, which will never be the case in first function call.
  • The execution will continue and trigger async operation but will exit the function before the result is available.
  • When the async operation result is available it will set isAsyncOperationCompleted to true and update isButtonEnabled flag based on result, and if isButtonEnabled is true the then call the formContext.ui.refreshRibbon().
  • It will call the enable rule function again and this time because isAsyncOperationCompleted is true in outer scope, correct value of isButtonEnabled will be returned, which was set in previous function call.

Clearly it does the trick but it’s tricky, and there should be a better way of doing this.

And another potential issue could be, in subsequent clicks it will return the same value even if the value changes in the backend.

So, I gave it a try and did the similar thing but in a little different way and improved in following areas:

  • No outer scope level flags.
  • No extra call to formContext.ui.refreshRibbon(), resulting in better performance.
  • Every button click will return the latest value.
  • Easy to follow code logic.
  • Cleaner code.

In the following example I am using TypeScript for better type checking and intellisense support, and using @types/Xrm npm package for Xrm types intellisense.

I have used ES6 feature async/await, which makes it much easier and cleaner to implement the async calls.

namespace Contact {
    export async function ShowCreditButton(formContext: Xrm.FormContext) {
        const accountLookup = formContext.getAttribute<Xrm.Attributes.LookupAttribute>("parentcustomerid").getValue();
        if (accountLookup) {
            const accountId = accountLookup[0].id
            const account = await Xrm.WebApi.retrieveRecord("account", accountId, "?$select=creditonhold");
            return account.creditonhold != true;
        }
        return false;
    }
}

Clearly the above example is much concise and cleaner to implement asynchronous operation in enable rule functions of ribbon button.

If you know a better way of solving this problem, please do share. It’s always good to learn better ways of solving the problems.

Update

There is Microsoft documentation exists for handling asynchronous calls in enable rules using promises. Thanks to Andrew Butenko for sharing the link in comment.

If you want to understand this in plain JavaScript you can refer below, you just have to return promises and it will be handled by the platform.

// Old synchronous style
/*
function EnableRule() {
   const request = new XMLHttpRequest();
   request.open('GET', '/bar/foo', false);
   request.send(null);
   return request.status === 200 && request.responseText === "true";
}
*/

// New asynchronous style
function EnableRule() {
   const request = new XMLHttpRequest();
   request.open('GET', '/bar/foo');

   return new Promise(function(resolve, reject) {
       request.onload = function (e) {
           if (request.readyState === 4) {
               if (request.status === 200) {
                   resolve(request.responseText === "true");
               } else {
                   reject(request.statusText);
               }
           }
       };
       request.onerror = function (e) {
           reject(request.statusText);
       };

       request.send(null);
   });
}

Few points to note about the asynchronous calls in enable rule:

  • Async calls on enable rule are supported in Unified Interface only and not in the classic web clients.
  • There is time limit of 10 seconds, if promise not resolved in 10 seconds then false will be returned.

Summary

In this blog post we learned how we can toggle ribbon button’s visibility based on result of asynchronous operation call defined on enable rule of the ribbon button.

We also learned how we can use the latest ES6 features such as async/await in TypeScript/JavaScript to write much cleaner and concise code.

3 thoughts on “Simplified Show/Hide Ribbon Button in Dynamics 365 based on Asynchronous Operation Result using TypeScript ES6”

  1. hello Suresh, this is TS code we are using with Promise – we need to show/hide certain ribbon button based on Business Line (lookup field) of Opportunity records:

    static async isButtonEnabledGrid(opportunityId: String): Promise {
    const machineBusinessLine = ‘762531cf-6gf3-ea11-a815-000d3ld7c1cb’;
    let recordId = opportunityId[0];
    const opportunity = await Xrm.WebApi.retrieveRecord(“opportunity”, recordId, “?$select=_businesslineid_value”);
    let businessLine = opportunity.businesslineid_value;

    if (businessLine == machineBusinessLine) {
    return true;
    }
    else {
    return false;
    }
    }

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s