async foreach in jquery - javascript

I am trying to display some charts using c3js.
And I have come across the problem of having a need for "pausing" a foreach loop with a async operation in the loop.
I can hack it by a adding an empty alert which allows the "work" to be completed (see the alert in code below). How do I unhack this and make it work?
The showData function is called when pressing a button.
selectedFiles = [];
function showData(){
displayChart(selectedFiles,function(cols){
alert("");//fixme
//display chart
chart = c3.generate({
bindto: '#chart',
data: {
columns: cols,
type:'bar'},
bar: {
width: {
ratio: 0.5
}
}
});
});
}
function displayChart(files,completionHandler)
{
var columns = [];
$.each(files, function( index,value) {
//create array with file name, and averageDuration
var fileName = value + ".json";
var averageDuration;
$.getJSON(fileName, function(json) {
averageDuration = json.averageDuration;
var col = [value,averageDuration];
columns.push(col);
});
});
completionHandler(columns);
}

since ajax is async, you can't use it like that
function displayChart(files, completionHandler) {
var src = $.map(files, function (value) {
var fileName = value + ".json";
return $.getJSON(fileName);
});
$.when.apply($, src).done(function (data) {
var columns;
if (files.length > 1) {
columns = $.map(arguments, function (array, idx) {
return [[files[idx], array[0].averageDuration]]
});
} else {
columns = [
[files[0], data.averageDuration]
]
}
completionHandler(columns);
})
}
Demo: Fiddle

Related

Remove asynchronous code into callback in $.each() loop

In me.getIssuesFromReturnsList the uploadList variable is being returned before the rest of the code executes because it is asynchronous (as per this question)
me.getIssuesFromReturnsList = function () {
var uploadList = [];
$.each(me.returnsList(), function (index, i) {
var issue = issueRepository.GetDefaultIssue();
issue.barcode = i.barcode;
issue.ReturnsAmount(i.amount);
var uploadItem = {
barcode: i.barcode,
amount: i.amount,
issue: ko.observable(issue)
};
uploadList.push(uploadItem);
issueRepository.GetIssuesOffline(i.barcode, function (issues) {
if (issues.length > 0) {
uploadItem.issue(issues[0]);
}
});
});
return uploadList;
}
I want to alter this code so that the calls are no longer asynchronous, and instead waits for all the inner code to execute before returning the uploadList.
I know that I need to add a callback to part of the code so that it will wait for issueRepository.getIssuesOffline to finish, but because this is part of a $.each() loop I am really struggling to see where this callback would go.
(I have asked this as a new question because the answers given in the suggested duplicate answer are generic examples and don't have a $.each() loop in them).
In the comments below, Bergi has asked for the contents of getissuesOffline:
GetIssuesOffline:
GetIssuesOffline: function (barcode, callback) {
var me = this;
issueDatabase.GetIssues(barcode, function (issues) {
me._processIssues(issues);
return callback(issues);
});
}
issueDatabase.getIssues:
GetIssues: function (barcode, callback) {
var me = this;
db.transaction(
function (context) {
var query = "SELECT * FROM issues WHERE barcode LIKE '" + barcode + "%' ORDER BY offSaleDate DESC LIMIT 25";
context.executeSql(query, [], function (context, result) {
var issues = [];
for (var i = 0; i < result.rows.length; i++) {
var issue = result.rows.item(i);
issue.Source = dataSources.Local;
issue.isRanged = issue.isRanged == 1 ? true : false;
issues.push(issue);
}
callback(issues);
}, me.ErrorHandler);
}
, me.ErrorHandler);
}
me.processIssues:
_processIssues: function (issues) {
var me = this;
$.each(issues, function (index, i) {
if (i.issueNumber == null) {
i.issueNumber = '';
}
i.issueNumber = i.issueNumber + '';
i.productNumber = i.productNumber + '';
if (i.issueNumber.length == 1) {
i.issueNumber = '0' + i.issueNumber;
}
i.barcode = parseInt(i.productNumber + '' + i.issueNumber);
i.Status = me.GetIssueStatus(i);
i.supplier = me.GetissueSupplierDetails(i);
i.ReturnsAmount = ko.observable(0);
i.Returns = ko.observable({ totes: [] });
returnsRepository.GetReturn(i.barcode, function (r) {
i.ReturnsAmount(r.amount);
if (r.amount > 0) {
i.Returns(r);
} else {
i.Returns({ totes: [] });
}
});
};
i.RefreshReturnsAmount();
me.IssueDatabase.UpdateIssue(i, function (issue) {
me.IssueDatabase.UpdateIssueLastUpdated(issue);
});
});
}

How to reference a slick grid with jquery notation

I'm working with a javascript library called slick grid to display a grid on a webpage. I need to send the grid information via ajax to a server. My problem is I need to used jquery notation to access the grid.
Here is the div grid:
<div id="myGrid" style="width:100%;height:300px;"></div>
Here is the javascript I'm using:
var grid;
$(function () {
for (var i = 0; i < 30; i++) {
data[i] = {
pbc_id: "00" + i,
task: "Task " + i,
due_date: "02/02/2017"
};
}
grid = new Slick.Grid("#myGrid", data, columns, options);
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.registerPlugin(new Slick.AutoTooltips());
// set keyboard focus on the grid
grid.getCanvasNode().focus();
var copyManager = new Slick.CellCopyManager();
grid.registerPlugin(copyManager);
copyManager.onPasteCells.subscribe(function (e, args) {
if (args.from.length !== 1 || args.to.length !== 1) {
throw "This implementation only supports single range copy and paste operations";
}
var from = args.from[0];
var to = args.to[0];
var val;
for (var i = 0; i <= from.toRow - from.fromRow; i++) {
for (var j = 0; j <= from.toCell - from.fromCell; j++) {
if (i <= to.toRow - to.fromRow && j <= to.toCell - to.fromCell) {
val = data[from.fromRow + i][columns[from.fromCell + j].field];
data[to.fromRow + i][columns[to.fromCell + j].field] = val;
grid.invalidateRow(to.fromRow + i);
}
}
}
grid.render();
});
grid.onAddNewRow.subscribe(function (e, args) {
var item = args.item;
var column = args.column;
grid.invalidateRow(data.length);
data.push(item);
grid.updateRowCount();
grid.render();
});
})
var DocumentViewModel = function () {
var self = this;
self.sendGridData = function () {
var data = $("myGrid");
//TODO: access the grid data and convert the data to a json string
$.ajax({
async: true,
type: "POST",
dataType: "json",
url: '/MyService/SubmitRequest',
data: ,
success: function (response) {
alert("success");
},
error: function (xhr, textStatus, exceptionThrown) {
alert("error");
}
});
};
};
Any help would be greatly appreciated.
Edit: trying to access the grid variable where my todo is
I managed to recreate your problem above. Had to add some code to get the example to execute as is. It seems your main problem is that though the grid variable is declared in the global address space, iether the contents of DocumentViewModel cannot access it because everything inside DocumentViewModel is private to this function or perhaps your DocumentViewModel is executing before the other code is declared (more likely) however you've not shown how or WHEN you are triggering DocumentViewModel so I can't tell you for sure.
But by moving DocumentViewModel inside '$(function () {' finally the variable was found and the grid was accessible.
I'll paste my code below which includes yours plus the extra I added to get it to execute.
<html>
<head>
<h1>myGrid SO Question Test</h1>
<script src="http://code.jquery.com/jquery-1.7.min.js"></script>
<script src="http://mleibman.github.com/SlickGrid/lib/jquery.event.drag-2.2.js"></script>
<style src="http://mleibman.github.com/SlickGrid/css/smoothness/jquery-ui-1.8.16.custom.css"></style>
<script src="http://mleibman.github.com/SlickGrid/slick.core.js"></script>
<script src="http://mleibman.github.com/SlickGrid/slick.grid.js"></script>
<script src="http://mleibman.github.com/SlickGrid/slick.editors.js"></script>
<script src="http://mleibman.github.com/SlickGrid/slick.formatters.js"></script>
<script src="http://mleibman.github.com/SlickGrid/slick.dataview.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.cellselectionmodel.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.cellrangeselector.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.cellrangedecorator.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.rowselectionmodel.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.autotooltips.js"></script>
<script src="http://mleibman.github.io/SlickGrid/plugins/slick.cellcopymanager.js"></script>
<script src="my.js"></script>
</head>
<body>
<div id="myGrid" style="width:100%;height:300px;"></div>
</body>
</html>
$(function () {
var grid;
// Addition Start
var columns = [{
id: "column",
name: "",
field: "column",
//selectable: false,
focusable: false
},
{
id: "data",
name: "Data",
field: "data"
}];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
// Addition End
var data = []; // Addition
for (var i = 0; i < 30; i++) {
data[i] = {
pbc_id: "00" + i,
task: "Task " + i,
due_date: "02/02/2017"
};
}
grid = new Slick.Grid("#myGrid", data, columns, options);
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.registerPlugin(new Slick.AutoTooltips());
// set keyboard focus on the grid
grid.getCanvasNode().focus();
var copyManager = new Slick.CellCopyManager();
grid.registerPlugin(copyManager);
copyManager.onPasteCells.subscribe(function (e, args) {
if (args.from.length !== 1 || args.to.length !== 1) {
throw "This implementation only supports single range copy and paste operations";
}
var from = args.from[0];
var to = args.to[0];
var val;
for (var i = 0; i <= from.toRow - from.fromRow; i++) {
for (var j = 0; j <= from.toCell - from.fromCell; j++) {
if (i <= to.toRow - to.fromRow && j <= to.toCell - to.fromCell) {
val = data[from.fromRow + i][columns[from.fromCell + j].field];
data[to.fromRow + i][columns[to.fromCell + j].field] = val;
grid.invalidateRow(to.fromRow + i);
}
}
}
grid.render();
});
grid.onAddNewRow.subscribe(function (e, args) {
var item = args.item;
var column = args.column;
grid.invalidateRow(data.length);
data.push(item);
grid.updateRowCount();
grid.render();
});
// Move DocumentViewModel inside "$(function ()"
var DocumentViewModel = function () {
var self = this;
self.sendGridData = function () {
var data = $("myGrid");
//TODO: access the grid data and convert the data to a json string
var Data_Grid = grid.getData();
var myJsonString = JSON.stringify(Data_Grid);
// $.ajax({
// async: true,
// type: "POST",
// dataType: "json",
// url: '/MyService/SubmitRequest',
// data: ,
// success: function (response) {
// alert("success");
// },
// error: function (xhr, textStatus, exceptionThrown) {
// alert("error");
// }
// });
};
self.sendGridData(); // Addition Trigger our local function
};
// Addition : Trigger our function to prove we can access the grid's data
DocumentViewModel();
})

How to count duplicate array elements? (javascript)

I'm trying to make an XML based menu with JavaScript, XML and jQuery. I've been successful at getting the categories of the menu, but haven't been able to generate the items in the categories.
My script is as follows, and later in this thread, I've asked for suggestions for this code:
var animalsXMLurl = 'http://dl.dropboxusercontent.com/u/27854284/Stuff/Online/XML_animals.xml';
$(function() {
$.ajax({
url: animalsXMLurl, // name of file you want to parse
dataType: "xml",
success: function parse(xmlResponse) {
var data = $("item", xmlResponse).map(function() {
return {
id: $("animal_id", this).text(),
title: $("animal_title", this).text(),
url: $("animal_url", this).text(),
category: $("animal_category", this).text().split('/'),
};
}).get();
var first_item = category_gen(data, 0);
$('ul.w-nav-list.level_2').append(first_item);
var categnumber = new Array();
for (i = 1; i <= data.length; i++) //for splitting id, and getting 0 for category_number (1 or 2 or 3...and so on)
{
categnumber[i] = data[i].id.split('_');
console.log(categnumber[i][0]);
for (j = 1; j <= data.length; j++) //appending via a function.
{
var data_text = category_or_animal(data, categnumber, j);
console.log(data_text);
$('ul.w-nav-list.level_2').append(data_text);
}
}
function category_or_animal(d, catg, k) {
var catg1 = new Array();
var catg2 = new Array();
var catg1 = d[k].id.split('_');
if (d[k - 1]) {
var catg2 = d[k - 1].id.split('_');
//if(d[k-1].id)
if (catg1[0] != catg2[0])
return category_gen(d, k);
} else
return '</ul>' + animal_gen(d, k);
}
function category_gen(d, z) {
var category_var = '<li class="w-nav-item level_2 has_sublevel"><a class="w-nav-anchor level_2" href="javascript:void(0);"><span class="w-nav-title">' + d[z].category + '</span><span class="w-nav-arrow"></span></a><ul class="w-nav-list level_3">';
return category_var;
}
function animal_gen(d, z) {
var animal_var = '<li class="w-nav-item level_3"><a class="w-nav-anchor level_3" href="animals/' + d[z].url + '"><span class="w-nav-title">' + d[z].title + '</span><span class="w-nav-arrow"></span></a></li>';
return animal_var;
}
}, error: function() {
console.log('Error: Animals info xml could not be loaded.');
}
});
});
Here's the JSFiddle link for the above code: http://jsfiddle.net/mohitk117/d7XmQ/4/
In the above code I need some alterations, with which I think the code might work, so I'm asking for suggestions:
Here's the function that's calling separate functions with arguments to generate the menu in above code:
function category_or_animal(d, catg, k) {
var catg1 = new Array();
var catg2 = new Array();
var catg1 = d[k].id.split('_');
if (d[k - 1]) {
var catg2 = d[k - 1].id.split('_');
//if(d[k-1].id)
if (catg1[0] != catg2[0])
return category_gen(d, k);
} else
return animal_gen(d, k) + '</ul>';
}
At the if(catg1[0] != catg2[0]) it checks if the split string 1_2 or 1_3 is equal to 1_1 or 1_2 respectively. By split, I mean the first element: 1 .... if you have a look at the xml: [ :: Animals XML :: ], you'll see that the animal_id is in the format of %category_number% _ %item_number% ... So I need to create the menu with CATEGORY > ITEM (item=animal name)
Now if I could return category_gen() + animal() with animal(){ in a for loop for all the matching category id numbers} then maybe this could be complete! But I don't of a count script for conditioning the for loop (i=0;i<=count();i++)...
Would anyone know of how to get this script functioning?
Hard to tell what the provided JSFiddle is trying to do.
This is my best stab at it. I used JQuery to parse the XML out into categories and generate lists of items.
http://jsfiddle.net/d7XmQ/8/
"use strict";
var animalsXMLurl = 'http://dl.dropboxusercontent.com/u/27854284/Stuff/Online/XML_animals.xml';
$(function () {
var $menu = $('#menu');
$.ajax({
url: animalsXMLurl, // name of file you want to parse
dataType: "xml",
success: handleResponse,
error: function () {
console.log('Error: Animals info xml could not be loaded.');
}
});
function handleResponse(xmlResponse) {
var $data = parseResponse(xmlResponse);
createMenu($data);
}
function parseResponse(xmlResponse) {
return $("item", xmlResponse).map(function () {
var $this = $(this);
return {
id: $this.find("animal_id").text(),
title: $this.find("animal_title").text(),
url: $this.find("animal_url").text(),
category: $this.find("animal_category").text()
};
});
}
function createMenu($data) {
var categories = {};
$data.each(function (i, dataItem) {
if (typeof categories[dataItem.category] === 'undefined') {
categories[dataItem.category] = [];
}
categories[dataItem.category].push(dataItem);
});
$.each(categories, function (category, categoryItems) {
var categoryItems = categories[category];
$menu.append($('<h2>').text(category));
$menu.append(createList(categoryItems));
});
}
function createList(categoryItems) {
var $list = $('<ul>');
$.each(categoryItems, function (i, dataItem) {
$list.append(createItem(dataItem));
});
return $list;
}
function createItem(dataItem) {
return $('<li>').text(dataItem.title);
}
});
You can solve this without using any for/while loop or forEach.
function myCounter(inputWords) {
return inputWords.reduce( (countWords, word) => {
countWords[word] = ++countWords[word] || 1;
return countWords;
}, {});
}
Hope it helps you!

Javascript - Highcharts - input data array of arrays

I am trying to feed the highcharts diagram with input data but face one problem. I get the data via json and then add them to data just like this:
function getGraphValues() {
period.deviceID = user.deviceID;
var postData = JSON.stringify(period);
var postArray = {json:postData};
$.ajax({
type: 'POST',
url: "getData.php",
data: postArray,
success: function(data)
{
data1 = JSON.parse(data);
if (data1.status == "ok" )
{
var stats = data1.metrics.split("/");
$(function () {
$('#container').highcharts(
{
chart: {type: 'bubble',zoomType: 'xy'},
title: {text: 'Highcharts Bubbles'},
series: [{data: [getData(stats)]}]
});});
}
else
{
alert("error");
}}
});}
function getData(stats)
{
var data;
var stats;
for (var j = 0; j < stats.length; j++)
{
data += "[" + j + "," + stats[j] + "," + stats[j]*8 + "],";
}
stats = data.split(",");
return stats;
}
So, in a way like this I do get the stats in this form:
[47,47,21],[20,12,4],[6,76,91],[38,30,60],[57,98,64],[61,17,80],[83,60,13],[67,78,75]
stored in string variable. My problem is that I can't pass this string for the data input as it waits number and not string.
How should I feed the data attribute in an accpetable way ?
How can I create an array of array in the getData function if this is needed ?
Thank you.
If I read your question right, you want getData to return a 2-dimensional array. I believe the following function does what you need:
function getData(stats) {
var result = [];
for (var j = 0; j < stats.length; j++) {
var data = [j, stats[j], stats[j]*8];
result.push(data);
}
return result;
}
And here's a jsFiddle demo.
Here is another example that uses the getData function defined above inside a highcharts bubble chart:
$(function () {
function getData(stats) {
var result = [];
for (var j = 0; j < stats.length; j++) {
var data = [j, stats[j], stats[j]*8];
result.push(data);
}
console.debug(result);
return result;
}
$('#container').highcharts({
chart: {type: 'bubble',zoomType: 'xy'},
title: {text: 'Highcharts Bubbles'},
series: [{data: getData([1,2,3])}]
});
});
And the corresponding jsFiddle demo.

javascript keys and objects

I have a javascript code below:
function init() {
var data_pie = [];
var data_key = [];
data_pie.push(10,12,30,40,80,25);
data_key.push("x1","x2","x3","x4","x5","x6");
g.update(data_pie, data_key);
}
update: function(data, key) {
var i=-1;
var streakerDataAdded = d3.range(data.length).map(function() {
i++;
return {
name: key[i],
totalPlayers: data[i]
}
});
}
How can I optimize my code to use this object:
var data='{"data":[{"x1":"10","x2":"12","x3":"30","x4":"40","x5":"80","x6":"25"}]}';
Instead data_pie and data_key arrays?
Without being able to test this (not having any access to the rest of your code), you might try something like:
function init() {
var data='{"data":[{"x1":"10","x2":"12","x3":"30","x4":"40","x5":"80","x6":"25"}]}';
for(key in data.data[0]){
g.update(data.data[0][key], key, data.data[0].length);
}
}
update: function(data, key, length) {
var streakerDataAdded = d3.range(length).map(function() {
return {
name: key,
totalPlayers: data
}
});
}
It is very unclear as to why you would use such a data structure but anyway, here we go:
var jsonData = '{"data":[{"x1":"10","x2":"12","x3":"30","x4":"40","x5":"80","x6":"25"}]}';
// var cleanerJsonData = '{"x1":"10","x2":"12","x3":"30","x4":"40","x5":"80","x6":"25"}';
function update(json){
var data = JSON.parse(json).data[0]; // why use an array here?
// var data = JSON.parse(json) - using cleanerJsonData.
var streakerDataAdded = d3.map(data).entries().map(function(d){
return {name: d.key, totalPlayers: d.value} })
}
}

Categories

Resources