EnableRule in ribbon cannot get boolean value from async function - javascript

I want to return true or false from the function showHideAddNewButton. I have an EnableRule in a ribbon button, which calls a custom rule that calls this function showHideAddNewButton. On passing either true, which will show the button, or false, which will hide the button.
I have to access statuscode (Status Reason) and statecode (Status) fields on the entity. I have created a query using the Xrm.WebApi.retrieveMultipleRecords, but cannot get it to return a flag. I want the retrieveMultipleRecords method to only execute on and never be called again but 'return true' below gets executed setting my button to true always.
function showHideAddNewCsrsRecalculation(primaryControl){
var fileNumber = primaryControl.getAttribute("ssg_filenumber").getValue();
Xrm.WebApi.retrieveMultipleRecords("rrg_csrsfile", "?$select=statuscode,statecode,rr_filenumber&$filter=rr_filenumber eq '" + fileNumber + "'").then(
function success(result) {
for (var i = 0; i < result.entities.length; i++) {
var statusCode = result.entities[i].statecode;
var statusReasonCode = result.entities[i].statuscode;
//if draft make button invisible
if (statusReasonCode == 8676725)
return false;
//if submitted make button invisible
if (statusReasonCode == 8676726)
return false;
//if inactive make button invisible
if (statusCode == 1)
return false;
}
},
function (error) {
console.log(error.message);
// handle error conditions
}
);
//if draft make button invisible
//if (primaryControl.getAttribute("statuscode").getValue() == 867670025)
// return false;
//if submitted make button invisible
//if (primaryControl.getAttribute("statuscode").getValue() == 867670026)
// return false;
//if inactive make button invisible
//if (primaryControl.getAttribute("statecode") != 'undefined' && primaryControl.getAttribute("statecode").getValue() == 1)
// return false;
//other options make button visible
return true; --> This keeps getting called as a result my button is always visible
}

Function Xrm.WebApi.retrieveMultipleRecords returns a promise, not an actual boolean value. The function is executed asynchronously, so immediately after the call to this function the next line is executed and that line always returns true.
In fact it is not possible to make an asynchronous call synchronous. Instead we can follow another approach by following these steps:
Do the query in the form's onload function and store the result in a variable.
Refresh the ribbon.
Create a ribbon button handler returning the variable's value.
let isRecalculationButtonVisible = false;
function onLoad(context) {
const formContext = context.getFormContext();
const filter = "$filter=rr_filenumber eq '"
+ formContext.getAttribute("ssg_filenumber").getValue()
+ "' and (statecode eq 1 or statuscode eq 8676725 or statuscode eq 8676726)";
Xrm.WebApi.retrieveMultipleRecords("rrg_csrsfile", "?$select=rrg_csrsfileid&$top=1&" + filter)
.then(function (result) {
isRecalculationButtonVisible = result.entities.length === 0;
})
.catch(function (error) {
console.log(error.message);
})
.finally(() => {
formContext.ui.refreshRibbon(false);
});
}
function showHideAddNewCsrsRecalculation() {
return isRecalculationButtonVisible;
}
As you probably already noticed I added a few improvements.
All your button needs to know is if there are any records meeting specific conditions, so there is no need to actually retrieve them. Therefore these conditions can simply be placed in the query's filter. I also added a $top=1, because the number of records meeting the conditions is not relevant here. As a consequence the only check that needs to be done is whether a record is returned or not.
As explained, retrieveMultipleRecords returns a promise. The recommended error handling for promises is adding a catch function at the end of the chain.

Related

Awaiting code until filter is finished - Nextjs/Javascript

I am looking on how to make my code after my filter function await the results of my filter function to complete before running. However I am not sure how to do this.
My filter function takes in another function (useLocalCompare) which causes the execution of my filter function to be a little longer than normal, which then leads to my next piece of code (that depends on the results of my filter function) executing before my filter function is complete.....which leads to undefined.
Is there anything similar to a callback I can use to force my subsequent piece of code to wait till the filter is finished?
Relevant code is written below.
if (flatarrayofvalues !== null && genre !== null) {
const filtteredarray = await flatarrayofvalues.filter(
(placeholder) => {
if (useLocalCompare(genre, placeholder.name) == true) {
console.log("HURAY!!!!", placeholder.id, placeholder.name);
placeholder.name == placeholder.name;
}
}
);
console.log("MY FILTERED ARRAY IS", filtteredarray);
console.log("The ID FOR MY MY FILERED ARRAY IS two ID", filtteredarray[0]?.id);
return filtteredarray[0].id;
}
}
}
For those curious, useLocalCompare basically checks to see if the genre parameter pulled down from the URL is the same as a name parameter from the array I am filtering. Reason I have this is due to people having different case sensitivity when putting in URLS. EX: it will pull down "HORrOR" and match it to the object name in the array I am filtering called "horror". I then extract the ID from that object.
you have to return the conditional from filter as it is "explicit return"
const filtteredarray = await flatarrayofvalues.filter(
(placeholder) => {
if (useLocalCompare(genre, placeholder.name) == true) {
console.log("HURAY!!!!", placeholder.id, placeholder.name);
return placeholder.name == placeholder.name; // here
// why not just return true ?? instead of above line
}return false
}
);
Also I'm not sure this makes sense
placeholder.name == placeholder.name; you mean just return true; ?

In sails dynamically change what to populate and use connection?

Well consider a function inside a controller as below:
test_func: async function(id, update_vals, connection=undefined) {
if (connection !== undefined) {
await Data.update({id: id}, update_vals).usingConnection(connection);
} else {
await Data.update({id: id}, update_vals);
}
}
I have to create an if statement, based on wether or not we are using the function inside a transaction. This becomes even worse when we add another flag "fetch" to the list. Suddenly there are 4 cases and each need its own if statement due to the syntax requirements.
Or in a place where it becomes even impossible: a function that gets data and populates based on an input list:
get_test_func: async function(id, populated_list) {
if (populated_list.length === 0) {
return Data.find({id: id}).populate(populated_list[0]);
} else if (populated_list.length === 1) {
return Data.find({id: id}).populate(populated_list[0])
.populate(populated_list[1]);
......
Can these additions to the query (db connection & populate specially) be moved inside the query?

Angularfire2 function is returning infinite loop

I am trying to show in a table a flag icon if the person who made a booking has had an order canceled in the past.
When admin changes a booking status to cancelled it sets a 'hasCanceled' boolean to true on the user who made the booking in firebase.
in my HTML table I am using an *ngIF to run a function and show the icon if the function returns true passing the order key as a parameter to the function.
<td><i *ngIf="hasUserCanceled(order.$key)" class="fa fa-flag"></i></td>
and here is the function itself:
hasUserCanceled(key) {
//Get the userId from the order
this.order = this.af.object('/orders/' + key);
this.order.subscribe(res => {
this.userId = res.userId;
});
this.canceledUsers = this.af.object('/users/' + this.userId);
this.canceledUsers.subscribe((res) => {
this.hasCanceled = res.hasCanceled
});
if (this.hasCanceled == true) {
console.log('THIS IS TRUE')
return true
}
}
I can see the flag in the right places of the users who have canceled, so the functions purpose is working, But the issue is that in the console, the function is running over and over and not stopping. Is there anything noticeable that I am doing wrong?
Console log Image:

Stop event from continuing on deferred.reject

Hoping some of you may help me with this problem.
I have a few navigation link on top of my application which have active and not-active state. In theory, I want to jump from one to another and if there exists a form which isn't complete/valid, I trigger validation errors and stay on the same page. What is happening in my case is form validation works fine but navigation links on top change state from non-active to active, whichever was clicked.
I have a ValidateForm function that validates and submits the form if its is valid, else it returns deferred.reject();
function ValidateForm(submitAnyway) {
var deferred = $.Deferred();
var form = $('form');
// if form doesn't exist on the page - quit
if (typeof form[0] === "undefined") return true;
// now check for any validation errors
if (submitAnyway) {
if (!$(form).valid()) {
deferred.reject();
} else {
$(form).submit();
deferred.resolve();
}
} else {
deferred.resolve();
}
return deferred.promise();
}
I have a click event for those top navigation links as below:
var DonutsClickEvent = function (e, arg) {
var url = $(this).attr('data');
var data = { param: arg };
if (typeof url === "undefined") return false;
$.when(window.ValidateForm(false)).then(
function () {
LoadPartialView_Main(url, data);
},
function(){
return false; // I'm trying to return false here to stop event from continuing
}
);
Console.log("This statement runs before $.when completes");
// and event continues and the clicked nav button changes
// its state from non-active to active, even though form doesn't validate
}
I recently added $.Deferred functionality to my code due to some events firing with messed up sequence... Before my validateForm method would return true or false and based on that i'd continue executing event if true, if false i'd stop and it was all good.
Not sure what am I doing wrong here. I'd appreciate any kinda help.
Thanks!
Johny
You can't asynchronously decide whether you want to block the default action or not. By the time you get your async result, your function has already returned and the default action has already occurred.
If you have to validate asynchronously, then you will have to always block the default action. If the validation succeeds, then you will have to manually carry out the desired action with Javascript.
So, assuming that LoadPartialView_Main() is what you want to have happen when validation succeeds, you can do something like this:
var DonutsClickEvent = function (e, arg) {
var url = $(this).attr('data');
var data = { param: arg };
if (typeof url === "undefined") return false;
window.ValidateForm(false).then(function () {
LoadPartialView_Main(url, data);
}, function(err){
// probably show validation error message to the user here
});
// always prevent default action
return false;
}
Also, there's no reason to use $.when() with a single promise. You can just use window.ValidateForm(...).then(...).

Casper JS waitForResource with a restful API

We are having a little problem with a functional test with casper.js.
We request the same resource twice, first with the GET and then with POST method.
Now when waiting for the second resource (POST) it matches the first resource and directly goes to the "then" function.
We would like to be able to check for the HTTP method in the "test" function, that way we can identify the resource properly. For now we use the status code (res.status), but that doesn't solve our problem fully, we really need the http method.
// create new email
this.click(xPath('//div[#id="tab-content"]//a[#class="button create"]'));
// GET
this.waitForResource('/some/resource',
function then() {
this.test.assertExists(xPath('//form[#id="email_edit_form"]'), 'Email edit form is there');
this.fill('form#email_edit_form', {
'email_entity[email]': 'test.bruce#im.com',
'email_entity[isMain]': 1
}, true);
// POST
this.waitForResource(
function test(res) {
return res.url.search('/some/resource') !== -1 && res.status === 201;
},
function then() {
this.test.assert(true, 'Email creation worked.');
},
function timeout() {
this.test.fail('Email creation did not work.');
}
);
},
function timeout() {
this.test.fail('Email adress creation form has not been loaded');
});
Or maybe there is a better way to test this scenario? Although since this is a functional test we need to keep all those steps in one test.
You can try to alter the form action url to add some query string, therefore generating a new resource appended to the stack. Could be done this way:
casper.thenEvaluate(function() {
var form = __utils__.findOne('#email_edit_form');
form.setAttribute('action', form.getAttribute('action') + '?plop');
});
That's a hack though, and functional testing should never be achieved that way. Let's hope more information will be added to the response objects in the future.
The res parameter that is passed to the test function has an ID. I created a helper that tests against this ID and blacklists it, so the same resource won't get accepted a second time.
var blackListedResourceIds = [],
testUniqueResource = function (resourceUrl, statusCode) {
return function (res) {
// check if resource was already loaded
var resourceFound = res.url.search(resourceUrl) !== -1;
// check statuscode
if (statusCode !== undefined) {
resourceFound = resourceFound && res.status === statusCode;
}
// check blacklisting
if (!resourceFound || blackListedResourceIds[res.id] !== undefined) {
return false;
} else {
blackListedResourceIds[res.id] = true;
return true;
}
};
};

Categories