Announcements
In my Javascript code I defined a variable of type Array, which will be populated with the entities retrieved from Dynamics Crm.
I need to check if the variable is populated both when it's a simple array and when it has become an array of entities: my code only works in the first case (for the simple array)
I defined the variable this way:
var arrayOfEntities = new Array();
This is the code to check whether the array is empty:
if (arrayOfEntities.length != 0) { return arrayOfEntities; }
The second time the previous instruction will be hit, arrayOfEntities will be already populated, so that I need to modify the condition: in fact, by asking if (arrayOfLength != 0), the condition is valued true even when there aren't entities in there.
I assume that the problem is that a simple array has a different structure than an array of entities: in the second case, "length" is a child node of "entities".
How can I modify my condition to make it valid in both cases?
You can use following code:
if (typeof (PREFIX) == "undefined") {
PREFIX = { __namespace: true };
}
if (typeof (PREFIX.collection) == "undefined") {
PREFIX.collection = { __namespace: true };
}
PREFIX.collection.Ribbon = (function () {
function onLoad(executionContext){
// some logic here
}
function myFunction_OnClick(executionContext) {
// some logic here
}
return {
myFunction_OnClick: myFunction_OnClick,
onLoad: onLoad
};
})();
Ok, thanks.
I have another doubt. I would like to have another function with the same namespace, it's an OnLoad function that will be associated to the entity form (not to ribbon button), may I put it inside the same PREFIX.collection.Ribbon function, as in the following?
if (typeof (PREFIX) == "undefined") { PREFIX = { __namespace: true }; } if (typeof (PREFIX.collection) == "undefined") { PREFIX.collection = { __namespace: true }; } PREFIX.collection.Ribbon = (function () { function onLoad(executionContext){ // some logic here }; function myFunction_OnClick(executionContext) { // some logic here }; return { myFunction_OnClick: myFunction_OnClick }; })();
And then I would add the PREFIX.collection.Ribbon.onLoad to the OnLoad event handler in the entity form properties, would it be correct? -->
Hello,
The way I wrote the code is the typical way to have "private" if you would functions and allow exposing only those pieces that are required. The return statement on line 41 is used to return and expose that myFunction_OnClick "internal" function as a function under "PREFIX.collection.Ribbon namespace.
Here is why your spinner did not work you can define few functions hoping it will call it sequenty - you have to wrap it inside another function:
function success (result){
Xrm.Utility.closeProgressIndicator(); // line 34
resolve(result.entities);
},
function (e) {
Xrm.Utility.closeProgressIndicator(); //line 36
reject(e);
});
Thank you very much,
it works now.
What if I wanted to add a spinner to tell the user to wait until the WebApi call returns the result?
I tried by adding the following instructions before the api call (line 32 in your previous code) and inside the success and error callBack (line 34 and 36 in your previous code), but didn't work:
Xrm.Utility.showProgressIndicator("Please wait..."); // at line 32, before the API call
function success (result){ Xrm.Utility.closeProgressIndicator(); // line 34 resolve(result.entities); }, Xrm.Utility.closeProgressIndicator(); //line 36 reject);
Secondly, as I'm new to JavaScript and Asynchronous calls, I'm trying to understand the architecture and syntax you made use of, can you please explain to me what's happening here?
As far as I understood, the namespace PREFIX.collection.Ribbon is used like a function, and it contains two functions and a return instruction inside:
1) myFunction_OnClick, which is being associated to the ribbon button;
2) checkIfAssociatedStores, which is being called by the former.
3) And I have the return at line 40, I'm trying to understand this one.
You used:
return { MyFunction_OnClick: myFunction_OnClick };
and it works fine, whereas when I wrote return PREFIX.collection.Ribbon.MyFunction_OnClick didn't work (when I tried to use it, the function PREFIX.collection.Ribbon was reached even without clicking on the ribbon button, and the MyFunction_OnClick was never hit).
So that I need to use the block of instructions that you provided at lines 40-42 to make things work: what the colon at line 41 stands for?
Using the two curly braces after the return statement means that it's returning a function as value?
As regards to the architecture (using the namespace as a function, putting everything inside it and returning the main function that way), is it used when we have to handle with asynchronous calls? And does this pattern have a specific name?
Try to use the following code instead:
if (typeof (PREFIX) == "undefined") { PREFIX = { __namespace: true }; } if (typeof (PREFIX.collection) == "undefined") { PREFIX.collection = { __namespace: true }; } PREFIX.collection.Ribbon = (function () { function myFunction_OnClick(executionContext) { debugger; var formContext = executionContext; var collectionid = formContext.data.entity.getId(); checkIfAssociatedStores(collectionid).then(function (associatedLibraries) { if (associatedLibraries.length != 0) { console.log("ok"); } else { console.log("error"); } }, function (error) { Xrm.Navigation.openAlertDialog(error.message); }); } function checkIfAssociatedStores(collectionid) { return new Promise(function (resolve, reject) { var storesFetchXml = ""; storesFetchXml = "?fetchXml=" encodeURIComponent(storesFetchXml); Xrm.WebApi.retrieveMultipleRecords("new_store", storesFetchXml).then( function success(result) { resolve(result.entities); }, reject); }); }; return { MyFunction_OnClick: myFunction_OnClick }; })();
And to call the function use PREFIX.collection.Ribbon.MyFunction_OnClick function
if (typeof (PREFIX) == "undefined") { PREFIX = { __namespace: true }; } if (typeof (PREFIX.collection) == "undefined") { PREFIX.collection = { __namespace: true }; } PREFIX.collection.Ribbon = new function () { var _self = this; _self.MyFunction_OnClick = function (executionContext) { debugger; var formContext = executionContext; var collectionid = formContext.data.entity.getId(); checkIfAssociatedStores(collectionid).then(function (associatedLibraries) { if (associatedLibraries.length != 0) { console.log("ok"); } else { console.log("error"); } }, function (error) { Xrm.Navigation.openAlertDialog(error.message); }); } checkIfAssociatedStores = function (collectionid) { return new Promise(function (resolve, reject) { var storesFetchXml = ""; storesFetchXml = "?fetchXml=" encodeURIComponent(storesFetchXml); Xrm.WebApi.retrieveMultipleRecords("new_store", storesFetchXml).then( function success(result) { resolve(result.entities); }, reject); }); }; }
I'm using a namespace and the _self prefix refers to it.
Yes, the fetchXml is valid, I already tested it, and it worked with my previous version of this code.
On Ribbon Workbench, I added the function as Custom JavaScript Action to a command associated with a Ribbon button (complete name of the function including namespace: PREFIX.collection.Ribbon.MyFunction_OnClick )
What is _self at the start?
Did you add the valid FetchXml to the line
var storesFetchXml = // fetch
?
Can you please provide the exact and full version of your code?
Hi,
thank you very much for your sample with the Promise object.
I modified my code, but it doesn't work for me, it's recognized by the browser but I can't debug it (I can't reach the debugger instruction at line 2) :
_self.MyFunction_OnClick = function (executionContext) { debugger; var formContext = executionContext; var collectionid = formContext.data.entity.getId(); checkIfAssociatedStores(collectionid).then(function (associatedLibraries) { if (associatedLibraries.length != 0) { console.log("ok"); } else { console.log("error"); } }, function (error) { Xrm.Navigation.openAlertDialog(error.message); }); } checkIfAssociatedStores = function (collectionid) { return new Promise(function (resolve, reject) { var storesFetchXml = // fetch storesFetchXml = "?fetchXml=" encodeURIComponent(storesFetchXml); Xrm.WebApi.retrieveMultipleRecords("new_store", storesFetchXml).then( function success(result) { resolve(result.entities); }, reject); }); };
I also tried by commenting out the return new Promise but it didn't work:
_self.MyFunction_OnClick = function (executionContext) { debugger; var formContext = executionContext; var collectionid = formContext.data.entity.getId(); checkIfAssociatedStores(collectionid).then(function (associatedLibraries) { if (associatedLibraries.length != 0) { console.log("ok"); } else { console.log("error"); } }, function (error) { Xrm.Navigation.openAlertDialog(error.message); }); } checkIfAssociatedStores = function (collectionid) { console.log("test"); };
Joel,
As for me, you're trying to overcomplicate things.
Here is the modified version of your code that uses Promise to work with async result of WebApi call:
MyFunction_OnClick = function (executionContext) { var formContext = executionContext; var collectionid = formContext.data.entity.getId(); checkIfAssociatedStoress(collectionid).then(function(associatedStores) { if (associatedStores.length != 0) { // perform tasks } else { // perform other tasks } }, function(error) { Xrm.Navigation.openAlertDialog(error.message); }); } checkIfAssociatedStoress = function (collectionid) { return new Promise(function(resolve, reject) { var storesFetchXml = // fetchXML to retrieve the stores whose collectionid_value is eq to collectionid storesFetchXml = "?fetchXml=" encodeURIComponent(storesFetchXml); Xrm.WebApi.retrieveMultipleRecords("new_store", storesFetchXml).then( function success(result) { resolve(result.entities); }, reject); }); };
MyFunction_OnClick = function (executionContext) { var formContext = executionContext; var collectionid = formContext.data.entity.getId(); var associatedStores = checkIfAssociatedStoress(collectionid); if (associatedStores.length != 0) { // perform tasks } else { // perform other tasks } } var associatedStores = new Array(); checkIfAssociatedStoress = function (collectionid) { ava // need to set this condition properly: if (associatedStores.length != 0) { return associatedStores; } var storesFetchXml = // fetchXML to retrieve the stores whose collectionid_value is eq to collectionid storesFetchXml = "?fetchXml=" encodeURIComponent(storesFetchXml); Xrm.WebApi.retrieveMultipleRecords("new_store", storesFetchXml).then( function success(result) { associatedStores = result; // recall MyFunction_OnClick() }, function (error) { Xrm.Navigation.openAlertDialog(error.message); } ); return associatedStores; };
Hi, this is the code.
General overview:
associatedStores is a variable declared globally (line 15), it's being used to store the result of the WebApi call (line 31)-->
As the WebApi call is asynchronous, when associatedStores is returned first time (line 38) is not populated, as it will be populated after the server response.
So that my intention is to recall the MyFunction_OnClick() inside the callBack function: when the MyFunction_OnClick() is recalled, checkIfAssociatedStores() is called again (line 18), and this time the condition at line 21 is gonna be true, as associatedStores kept the value previously assigned inside the WebApi call. Therefore, its value is returned and I go back to line 7.
My problem:
the condition at line 7 (associatedStores.entities.length != 0) is valued true both when it contains entities and when has no entities inside, so that the check only works in the case when some entities are found from the WebApi call; if no entities are found, the associatedStores.length is > 0 is valued true as well, I assume it's because of the different structure of the result/object returned by the WebApi call (it has the additional child node "entities"). I tried to overcome the issue by writing the following to specify that the values I'm looking for are inside the child node "entities" but the code does not work:
if ((associatedStores.length !== 0) || (associatedStores.entities.length !== 0)) { return associatedStores; }
André Arnaud de Cal... 291,359 Super User 2024 Season 2
Martin Dráb 230,370 Most Valuable Professional
nmaenpaa 101,156