Mon, 01/10/2022 - 12:15 By Nguyen Van Hao Reviewed By Lloyd Sebag
1 comment

Dataverse Use Async Promise in ribbon enable rule

When creating a button/command we can use custom enable rule to control the visibility of the button.

Normally, enable rule is a function should return true or false to make the button show or hide. For example:

function EnableRule()
{
    const value = Xrm.Page.getAttribute("column1").getValue();
    return value === "Active";
}

Custom rules that do not return a value quickly can affect the performance of the ribbon. The command bar is only visible until all custom enable rules are evaluated (return true/false).

In case of we have some buttons and each button has complex logic to determine a button is show or hide, then the command bar is blocked, user can not see and click

Let see an example

We have two buttons "Async button 1" and "Aync button 2". Each button need to retrieve record before evaluating the enable rule.

// Sync mode
function asyncButtonEnableRule(executeContext) {
    debugger;
    var globalContext = Xrm.Utility.getGlobalContext();
    var clientUrl = globalContext.getClientUrl();
    console.log(new Date, 'Retrieving account');

    var request = new XMLHttpRequest();
    request.open('GET', encodeURI(clientUrl + '/api/data/v9.1/accounts(93dc4816-535d-eb11-a812-0022481631b1)'), false); // Sync mode
    request.setRequestHeader("Accept", "application/json");
    request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    request.setRequestHeader("OData-MaxVersion", "4.0");
    request.setRequestHeader("OData-Version", "4.0");

    request.send();
    var account = JSON.parse(request.responseText);
    console.log(new Date,'Retrieved account');
    return request.status === 200 && account['name'] != 'XXX';
}

function asyncButtonEnableRule2(executeContext) {
    debugger;
    return asyncButtonEnableRule(executeContext);
}

In this case, the command bar is hidden until 2 complex rules are evaluated one by one

Dataverse Use Async Promise in ribbon enable rule

In the console, we can see the enable rules were executed one by one.

Dataverse Use Async Promise in ribbon enable rule

This is not a good idea when we have several buttons like this since the command bar will be hidden until all rules are evaluated

Solution:

We should return a Promise rather than boolean for asynchronous rule evaluation. The promise must resolve true or false to show or hide button

The new enable rule will be like this

// Async mode
function asyncButtonEnableRule(executeContext) {
    debugger;
    var globalContext = Xrm.Utility.getGlobalContext();
    var clientUrl = globalContext.getClientUrl();
    console.log(new Date, 'Retrieving account');
    var request = new XMLHttpRequest();
    request.open('GET', encodeURI(clientUrl + '/api/data/v9.1/accounts(93dc4816-535d-eb11-a812-0022481631b1)'), true); // Async mode

    request.setRequestHeader("Accept", "application/json");
    request.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    request.setRequestHeader("OData-MaxVersion", "4.0");
    request.setRequestHeader("OData-Version", "4.0");

    return new Promise(function (resolve, reject) {
        request.onload = function (e) {
            if (request.readyState === 4) {
                console.log(new Date, 'Retrieved account');
                if (request.status === 200) {
                    var account = JSON.parse(request.responseText);
                    resolve(account['name'] != 'XXX');
                } else {
                    reject(request.statusText);
                }
            }
        };
        request.onerror = function (e) {
            reject(request.statusText);
        };

        request.send();
    });

}

function asyncButtonEnableRule2(executeContext) {
    debugger;
    return asyncButtonEnableRule(executeContext);
}

In this new version (async), 2 enable rules in 2 buttons are evaluated at the same time.

Dataverse Use Async Promise in ribbon enable rule

Alternatively, we can use Xrm.WebApi when CRUD CRM record

Then the new code will be like this:

function asyncButtonEnableRule(executeContext) {
    debugger;
    console.log(new Date, 'Retrieving account');
    return new Promise(function (resolve, reject) {
        Xrm.WebApi.retrieveRecord('account', '93dc4816-535d-eb11-a812-0022481631b1').then(
            function (accountInfo) {
                console.log(new Date, 'Retrieved account');
                resolve(accountInfo['name'] != "XXX");
            },
            function (error) {
                reject(error.message);
            }
        );
    });
}

function asyncButtonEnableRule2(executeContext) {
    debugger;
    console.log(new Date, 'Retrieving account in asyncButtonEnableRule2');
    return asyncButtonEnableRule(executeContext);
}

 

Conclusion

  1. Button enable rule should return a Promise if possible, especially when retrieving data from server
  2. Promises-based rules will only work on Unified Interface, so they cannot be used if classic Web Client is still being used
  3. If the promise does not resolve within 10 seconds, the rule will resolve with a false value.
  4. When we reject in Promise, the buttons are still visible. In this case, you can consider resolve(false) instead of "reject"

Dataverse Use Async Promise in ribbon enable rule

Comments

this was a big help~ calling a custom api from a ribbon, so using a Promise allowed me to ensure completion!

Sat, 02/26/2022 - 18:55
jim (not verified)

Add new comment

Image CAPTCHA
Enter the characters shown in the image.