I am having issues with the order of my javascript, I'm sure this is very easy:
I want to check what a variable is (using an if statement), and then set it either from another var, or from the value in a txt file
So far I can load the file, and set the var (I can check it in the console) successfully
However the later parts of the script do not wait for the var to be set, so the var is not used, resulting in an error for the var "cleanTradeDate"
My question is, is the below correct or should I be structuring the var differently. How do I make the subsequent code wait/use the "cleanTradeDate" value?
Here is a sample of the code:
var URLTradeDate = document.URL.substring(document.URL.search('tradeDate=') + 10, document.URL.search('tradeDate=') + 22);
var cleanTradeDate;
$(document).ready(function () {
if (URLTradeDate == '') {
$.get('data/0.max_trade_date.txt', function(maxTradeDate) {
cleanTradeDate = maxTradeDate.replace(/[\n\r]+/g, '');
});
} else {
cleanTradeDate = URLTradeDate;
}
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
$("#datepicker").datepicker('setDate', cleanTradeDate);
});
It's not where the variable is declared, but that it's filled asynchronously. You better use promises:
$(document).ready(function () {
var index = location.search.indexOf('tradeDate=');
var URLTradeDate = location.search.slice(index + 10, index + 22);
var promise = (index == -1 || URLTradeDate == '')
? $.get('data/0.max_trade_date.txt').then(function(maxTradeDate) {
return maxTradeDate.replace(/[\n\r]+/g, '');
});
: $.when(URLTradeDate);
promise.then(function(cleanTradeDate) {
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
$("#datepicker").datepicker('setDate', cleanTradeDate);
});
});
$.get is asynchronous (meaning the code doesn't wait for it to finish before carrying on), so you'll want something more like:
var URLTradeDate = document.URL.substring(document.URL.search('tradeDate=') + 10, document.URL.search('tradeDate=') + 22);
$(document).ready(function () {
if (URLTradeDate == '') {
$.get('data/0.max_trade_date.txt', function(maxTradeDate) {
setTradeDate(maxTradeDate);
});
} else {
setTradeDate(URLTradeDate);
}
});
function setTradeDate(tradeDate){
tradeDate = tradeDate.replace(/[\n\r]+/g, '');
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
$("#datepicker").datepicker('setDate', tradeDate);
}
This way once the data is received from the $.get, you call a function 'setTradeDate' that sets the datepicker.
EDIT: moved the formatting of maxTradeDate inside the setTradeDate function and fixed a couple of errors I noticed.
Related
I'm creating a var where I want to assign it to a var that I get from another entity
function SubmitAction(executionContext) {
var lookupItem = formContext.getAttribute("alfa_member").getValue()[0].id;
var theTotalMembersTravling = formContext.getAttribute("alfa_numberofdependent").getValue();
var remainFlightCredit;
debugger;
Xrm.WebApi.online.retrieveRecord("contact",lookupItem, "?$select=new_remainstravelcredit").then(
function employessPackage(result) {
var new_remainstravelcredit = result["new_remainstravelcredit"];
if(new_remainstravelcredit !== null){
if(new_remainstravelcredit > 0)
{
remainFlightCredit = new_remainstravelcredit;
console.log(remainFlightCredit+" This not inside any if condition");
var newRemain = (parseInt(remainFlightCredit)) - (parseInt(theTotalMembersTravling));
console.log(newRemain+ " This in the remain if condition");
var entity = {};
entity.new_remainstravelcredit = newRemain.toString();
Xrm.WebApi.online.updateRecord("contact",lookupItem, entity).then(
function success(result) {
var updatedEntityId = result.id;
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);
} if(new_remainstravelcredit <= 0)
{
Xrm.Utility.alertDialog("You have exceeds the travel credit");
console.log(remainFlightCredit);
}
}
},
function(error) {
Xrm.Utility.alertDialog(error.message);
}
);
console.log(remainFlightCredit);
}
So as result in this line
remainFlightCredit = new_remainstravelcredit;
console.log(remainFlightCredit+" This not inside any if condition");
Which inside the webapi call I'm able to get the value but outside in the main function at the end when I write
console.log(remainFlightCredit);
I'm unable to get the value remainFlightCredit, do you have any suggestions to solve this issue?
This is expected behavior, as this is a promise call (asynchronous) the code outside the main api call will execute before the success callback function employessPackage.
So remainFlightCredit value get assigned after the last console.log line in your code.
You can place a breakpoint to debug and see it in action.
I'm trying to use deferred/promise in a loop, but I get strange behavior. My code is as follows:
var images = [];
var numImages = Blobs.length;
var image = {};
console.log("numImages: " + numImages);
function doAsyncOriginal(i) {
var defer = $.Deferred();
image.original = Blobs[i].key;
image.resized = '';
image.thumbnail = '';
images.push(image);
console.log("i: " + i + " image: " + image.original);
console.log("images[" + i + "]: " + images[i].original);
defer.resolve(i);
return defer.promise();
}
$(function(){
var currentImage = doAsyncOriginal(0);
for(var i = 1; i < numImages; i++){
currentImage = currentImage.pipe(function(j) {
return doAsyncOriginal(j+1);
});
}
$.when(currentImage).done(function() {
console.log(JSON.stringify(images));
});
});
The Blob used in the code is an array of objects that I get from remote webservice, which contains properties about the images (it comes from filepicker.io's pickandstore method to be precise).
When I run this, I get the following in console:
numImages: 2
i: 0 image: pictures_originals/3QnQVZd0RryCr8H2Q0Iq_picture1.jpg
images[0]: pictures_originals/3QnQVZd0RryCr8H2Q0Iq_picture1.jpg
i: 1 image: pictures_originals/MD3KO6GjT8SNFYoPcG8J_picture2.jpg
images[1]: pictures_originals/MD3KO6GjT8SNFYoPcG8J_picture2.jpg
[
{
"original":"pictures_originals/MD3KO6GjT8SNFYoPcG8J_picture2.jpg",
"resized":"",
"thumbnail":""
},
{
"original":"pictures_originals/MD3KO6GjT8SNFYoPcG8J_picture2.jpg",
"resized":"",
"thumbnail":""
}
]
Although it shows images[0] and images[1] correctly, when printing separately, the object array shows only twice images[1]!!!
Am I doing something wrong???
Thanks in advance for your time.
UPDATE: I corrected the code based on comment of #TrueBlueAussie
You are reusing the same image object in every call to doAsyncOriginal(), so every element of your images array is pointing to the same object.
You need to create the object inside your function:
var image = {}; // <-- delete this
function doAsyncOriginal(i) {
var image = {};
// ...
}
This problem is unrelated to promises/deferreds, and promises/deferreds really aren't serving any purpose in your code. You could just do this:
$(function(){
var images = Blobs.map(function (blob) {
return {
original: blob.key,
resized: '',
thumbnail: ''
};
});
console.log(JSON.stringify(images));
});
In doAsyncOriginal you resolve your deferred before returning it's promise or even before adding the done handler on it.
You should delay the defer.resolve(i) call, so the deferred will be resolved later and enter the done handler...
function doAsyncOriginal(i) {
var defer = $.Deferred();
// ...
// Function.bind equivalent to jQuery.proxy
window.setTimeOut(defer.resolve.bind(defer, i), 0);
return defer.promise();
}
I have been trying to write a protractor test that selects an item from a custom dropdown menu. The only problem is that when it tries to click an element other than the last one in the list it hangs and timesout. When I remove the click() method invocation it seems to work fine. Since all these calls are done asynchronously I also don't see a way of stopping the loop when it finds the element. My code looks like this:
var it = null;
for(var i = 1; i <= totalNumberOfAccounts; i++) {
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
var item = browser.driver.findElement(protractor.By.xpath(listItemLocator));
item.getText().then(function(value) {
if(value === accountNumber) {
it = item;
}
console.log(value);
})
.then(function clickOption() {
console.log('Clicking...');
if (it) {
console.log('Clicking desired item');
it.click();
console.log('Clicked..');
}
})
}
I also tried this approach:
this.selectRichSelectOption = function (selector, item) {
var selectList = browser.driver.findElement(selector);
selectList.click();
var desiredOption = '';
var i = 1;
selectList.findElements(protractor.By.tagName('li'))
.then(function findMatchingOption(options) {
console.log(options);
options.some(function (option) {
console.log('Option:');
console.log(option);
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
console.log(listItemLocator);
var element = option.findElement(protractor.By.xpath('//label/div/div[2]/div[2]/span[2]'));
console.log('Element:');
console.log(element);
i++;
element.getText().then(function (value) {
console.log('Value: ' + value);
console.log('Item:');
console.log(item);
if (item === value) {
console.log('Found option..');
desiredOption = option;
return true;
}
return false;
});
});
})
.then(function clickOption() {
console.log('Click option');
console.log(desiredOption);
if (desiredOption) {
console.log('About to click..');
desiredOption.click();
}
});
};
The result of this one is even more strange. Now all of a sudden the getText() method invocation returns an empty String. But when I try to retrieve the e.g. the class attribute I get the correct value back. Where did the Text value go?
Can somebody please help me out?
This seems to be an issue with page load. After you select, the page does not load completely.
Try using a browser.sleep(timeInMs);
try using node 8+'s async functions such as await. I went through this headache and it was solved by awaiting for certain things to appear or have certain attributes.
await browser.wait(EC.presenceOf(element(by.xpath('path leading to element based off attribute'))))
Good luck
I have a very strange issue that I am running into. I am using jsTree from JQueryUI on one of my sites, and I have different implementations of it used in different .js files. One of them seems to work, which is very confusing as it uses almost identical code (only the variable names are different) to the implementation that is broken. The problem comes from the contextmenu function. The code I am using is as follows:
$(document).ready(function () {
if(typeof dryerList == 'undefined' || dryerList.length == 0) {
var dryerList = [];
$.ajax({
url:'../TrackingApp/getGrainBins.php?t=234.23423452353',
async: false,
success: function(text) {
try {
dryerList = $.parseJSON(text);
} catch (e) {
alert('ERROR: ' + e);
}
if(dryerList.length == 0) {
alert('ERROR: No fleet data received.')
}
}
});
}
$("#dryerListTree").jstree({
plugins : ['json_data', 'ui', 'themes', 'contextmenu'],
contextmenu: {items: customBinMenu},
json_data : { data: binNodes }
});
$('#dryerListTree').bind("dblclick.jstree", function (event) {
var node = $(event.target).closest("li");
var id = node[0].id;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
centerMap(dryerList[i].y, dryerList[i].x);
break;
}
}
});
});
function customBinMenu(node) {
if ($(node).hasClass("folder")) {
return;
}
var items = {
centerItem: {
label: "Locate",
action: function () {
// Centers map on selected bin
var id = node[0].id;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
centerMap(dryerList[i].y, dryerList[i].x);
break;
}
}
}
},
dashboardItem: {
label: "Dashboard",
action: function () {
// Opens dryer info window over map
var id = node[0].id;
var dryerIndex = -1;
for(i=0; i < dryerList.length; i++) {
if(id == dryerList[i].id) {
dryerIndex = i;
break;
}
}
}
}
};
return items;
}
The strange bit is, the double-click handler works just fine. When I get to the customBinMenu() function, the dryerList array is there, and dryerList[0] contains 4 of the 5 values that it should- but somehow the 'id' element has been dropped from that object. I have been looking at this for quite some time, and I can't figure out how it can drop a single element from the object without losing any other data, especially when identical code is working for a similar list. Any suggestions?
Ok, I read in your question: 'and dryerList[0] contains 4 of the 5 values that it should- but somehow the 'id' element has been dropped from that object'
So by 'element' and 'value' I assume you mean 'attribute': the node's 'id'-attribute to be precise ??
I see in your code: var id = node[0].id;
That should be: var id = node[0].getAttribute("id");
Good luck!
UPDATE 1:
Ok, if (as per your comment) var id = node[0].id; (getting id from node[0]) is ok, then if(id == dryerList[i].id) looks wrong, since you just (re-)defined id to be the value of node[0]'s id.
Actually I would not use 'id' as a var-name (in this case).
So what if you did: var idc = node[0].getAttribute("id");
and then: if(idc === dryerList[i].getAttribute("id"))
UPDATE 5: You still have some errors by the way:
You forgot a semi-colon to close the alert in:
if(dryerList.length == 0) {
alert('ERROR: No fleet data received.')
}
You should use '===' to compare with '0' on line 2 and 14
naturally in real life you would define function customBinMenu(node) before it was used in your document.ready function.
Fixed by swapping code order.
The same goes for this document.ready function where you used var dryerList before it was defined.
Fixed by: var dryerList = dryerList || []; if(dryerList.length === 0){//ajax & json code}
Could you please confirm if this fiddle, which is now valid javascript, represents your intended baseline-code that still results in your problem of the 'id'-attribute being 'undefined' in dryerList's node-collection (since the code you posted contained some simple errors that are fixed in this jsfiddle, excluding the things mentioned in update 1, since you commented that this is not the problem) ?
May I ask (since you start at document.ready), why do you (still) check if dryerList already exists?
May I ask if you could update that corrected fiddle with some demo-data for us to toy around with?
My code sends requests to Twitter for search data gets responses in the from of JSON. After getting the JSON, it stores the count of responses that match a certain condition in an array.
Here is the code that makes the call to the function that queries Twitter.
$(document).ready(function() {
...
graph = new HighCharts.chart({
chart: {
events: {
load: function() {
console.log("events.load");
var that = this;
var update = function() {
if (polling) {
console.log("update()");
// The least index series will be nearest the x-axis
var stackNumber = 0;
var numTweets = updateValues();
console.log(numTweets + "");
for (var i = 0, currentSeries = that.series; i < currentSeries.length; i++) {
var x = (new Date()).getTime(), // current time
y = numTweets[i];
stackNumber += y;
currentSeries[i].addPoint([x, y], true, true);
}
}
}
// set up the updating of the chart each second
var series = this.series[0];
setInterval(update, 1000);
}
}
(I'm probably missing some brace somewhere in the code-paste here, but I know for sure that my problem isn't related to a missing brace)
And here is the function that actually queries Twitter using a series of jQuery calls. The updateValues() function (which is outside the document-ready section) goes as follows:
function updateValues() {
var url = "http://search.twitter.com/search.json?callback=?&q=";
var cls = "Penn_CIS240"
var query = "%23" + cls;
var voteCount = [0,0,0,0];
// create an array of zeros
//for (var i = 0; i < 4; i++)
// voteCount.push(0);
$.getJSON(url + query, function(json){
console.log(json);
$.each(json.results, function(i, tweet) {
var user = tweet.from_user_id;
if (user % 2 == 0) {
voteCount[0] += 1;
}
else if (user % 3 == 0) {
voteCount[1] += 1;
}
else if (user % 5 == 0) {
voteCount[2] += 1;
}
else {
voteCount[3] += 1;
}
console.log("updateValues() -> getJSON -> each -> voteCount = " + voteCount);
});
console.log("updateValues() -> getJSON -> voteCount = " + voteCount);
});
console.log("updateValues() -> voteCount = " + voteCount);
return voteCount;
}
What is happening is that the variable voteCount is getting incremented properly inside the jQuery calls. However, outside of the calls, it is getting reset. So the log outputs look something like this:
updateValues() -> getJSON -> each -> voteCount = [1,0,0,0]
updateValues() -> getJSON -> voteCount = [1,0,0,0]
updateValues() -> voteCount = [0,0,0,0]
Does this problem have to do with jQuery's asynchronous calls, and I'm having interesting variable modification conflicts? Or is it something else?
When you use asynchronous callbacks in JavaScript, they execute later... asynchronously. So if you have:
var x = 5;
console.log("before getJSON", 5);
$.getJSON("/some/url", function (json) {
x = 10;
console.log("inside callback", x);
});
console.log("after getJSON", x);
the output will be
before getJSON 5
after getJSON 5
inside callback 10
Any code you want to execute after the request returns must be inside the callback; putting it physically "after" the $.getJSON call will not suffice. You should think of $.getJSON as "firing off" the JSON-getting process, then immediately returning to you; only later, when your script is done executing normal code and the server has responded, will the JavaScript event loop say "hey I got a response and am idle; time to call that callback that was waiting on the response!"
Because of this, your updateValues function will need to accept a callback of its own in order to notify its own caller that the values have been updated; just calling updateValues will only fire off the value-updating process, and the values won't be updated later until that idle time. Something like:
function updateValues(onUpdated) {
var url = "http://search.twitter.com/search.json?callback=?&q=";
var cls = "Penn_CIS240"
var query = "%23" + cls;
var voteCount = [0,0,0,0];
$.getJSON(url + query, function (json) {
// use of json to update voteCount ellided
onUpdated(voteCount);
});
}
Then calling code uses it as
updateValues(function (voteCount) {
// use the updated vote counts inside here.
});