Multiple Instances of Google Visualizations Chart Inside Separate Divs [Followup] - javascript

This is a followup to a question I've already asked on StackOverflow. So please make sure you read that one to get the whole picture:
Multiple Instances of Google Visualizations Chart Inside Separate Divs
So in an attempt to make this whole thing dynamic, I wrote the following code:
var containers = document.getElementsByClassName('gaugeWrapper');
console.log(containers);
google.load('visualization', '1', { packages: ['gauge'] });
for(var i = 0; i < containers.length; i++) {
var id = containers[i].getAttribute('id');
var name = containers[i].getAttribute('data-name');
var value = containers[i].getAttribute('data-value');
google.setOnLoadCallback(function () { drawChart(id, name, value) });
}
function drawChart(id, name, value) {
console.log(id);
console.log(name);
console.log(value);
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
[name, value]
]);
var options = {
width: 400, height: 120,
redFrom: 90, redTo: 100,
yellowFrom: 75, yellowTo: 90,
minorTicks: 5
};
var chart = new google.visualization.Gauge(document.getElementById(id));
chart.draw(data, options);
}
This does not work. The problem is that the console outputs the data of the last div only. Which means that the function is being called 5 (containers.length) times with the same set of parameters.
UPDATE:
As per Ateszki's answer, here's my updated code:
google.load('visualization', '1', { packages: ['gauge'] });
google.setOnLoadCallback(drawChart);
function drawChart() {
var containers = document.getElementsByClassName('gaugeWrapper');
for (var i = 0; i < containers.length; i++) {
var id = containers[i].getAttribute('id');
var name = containers[i].getAttribute('data-name');
var value = containers[i].getAttribute('data-value');
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
[name, value]
]);
var options = {
width: 400, height: 120,
redFrom: 90, redTo: 100,
yellowFrom: 75, yellowTo: 90,
minorTicks: 5
};
var cont = document.getElementById(id);
console.log(cont);
var chart = new google.visualization.Gauge(cont);
chart.draw(data, options);
}
}
Unfortunately, I still couldn't get it to work, yet. Now nothing renders on the screen, although my console.log's seem to output the right things...
Any explanations/suggestions?

The function that you are binding onload is overwriting the previous one.
Maybe you can store the values in another object and load them all at once in one function.

Ok the following has solved the problem:
google.load('visualization', '1', { packages: ['gauge'] });
google.setOnLoadCallback(drawChart);
function drawChart() {
var containers = $('.gaugeWrapper');
containers.each(function (index, elem) {
var id = $(elem).attr('id');
var name = $(elem).data('name');
var value = $(elem).data('value');
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
[name, value]
]);
var options = {
width: 400, height: 120,
redFrom: 90, redTo: 100,
yellowFrom: 75, yellowTo: 90,
minorTicks: 5
};
var cont = document.getElementById(id);
var chart = new google.visualization.Gauge(cont);
chart.draw(data, options);
});
I do not know how it differs from the code in the updated section of the question except for the fact that I am now using jQuery to grab the values I'm looking for...

Related

Pass the array to the google chart dataset in flask application

I have flask app with
#app.route('/dash_data')
def dash_data():
***
return jsonify({'key' : dashDetails})
which return nice json string
{"key":[["Food",50],["Health",10],["Restaurants",2],["Sports",20],["Taxi",5]]}
My google chart script expects
[
['Food', 50],
['Health', 10],
['Restaurants', 2],
['Sports', 20],
['Taxi', 5]
]
and it has the following way to pass this data to the chart:
var testData = $.get('/dash_data');
var tm = test_data.done(function (resp){ return resp;})
var dataArray = tm.responseJSON.key
var data = google.visualization.arrayToDataTable(dataArray);
However, the console output is
Uncaught (in promise) TypeError: Cannot read property 'key' of undefined at drawChart
Where is my mistake?
The full google-chart js file:
google.charts.load("current", {packages:["corechart"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var test_data = $.get('/dash_data');
var tm = test_data.done(function (resp){return resp;})
var t = tm.responseJSON.key
var data = google.visualization.arrayToDataTable(t);
var options = {
title: 'Expences 12.2020',
pieHole: 0.4,
width: 380,
height: 200
};
var chart = new google.visualization.PieChart(document.getElementById('exp_month_chart'));
chart.draw(data, options);
}
Thank you.
$.get runs asynchronously,
so you need to wait for the done callback before trying to draw the chart...
google.charts.load("current", {
packages: ["corechart"]
}).then(function () {
$.get('/dash_data').done(function (resp) {
var data = google.visualization.arrayToDataTable(resp.key);
var options = {
title: 'Expences 12.2020',
pieHole: 0.4,
width: 380,
height: 200
};
var chart = new google.visualization.PieChart(document.getElementById('exp_month_chart'));
chart.draw(data, options);
});
});
in the post above,
var t = tm.responseJSON.key
runs before
var tm = test_data.done(function (resp){return resp;})
is finished...

Photoshop scripting - trying to reference an UI element created in a funtion that is in a different file

I'm creating a script where a user must select from several dropdown lists in order to name their image. Since each row in the ui is the same and function the same (It contains a dropdown list, a selection box to enable editing the list, an edit text box and add/edit/remove buttons), I've created a function to add these ui elements instead of writing each one out. This function is also in a separate file. Everything works up until this point. What I now need to do is take whatever the user selected in the dropdown lists and create a file name from it. However, I cannot access the list. I guess because the function is last bit that is called.
I created a function that constructs the file name. I then tried to call the dropdown list in different locations and in different ways.
var dlg = new Window('dialog', 'Material to Library');
//Material category panel, used to select which material dialogue option appears
dlg.grp0 = dlg.add('group');
dlg.grp0.addCatPnl = dlg.grp0.add('panel', [10, 110, 705, 170], 'Assign a type of material');
panelOption(0, dlg.grp0.addCatPnl.matCatTitle, 'Material Category:', dlg.grp0.addCatPnl.matCat, dlg.grp0.addCatPnl.matCatAddBox, dlg.grp0.addCatPnl.matCatNew, dlg.grp0.addCatPnl.matCatEditText, dlg.grp0.addCatPnl.matCatAddButt, dlg.grp0.addCatPnl.matCatEdtButt, dlg.grp0.addCatPnl.matCatRmvButt, dlg.grp0.addCatPnl, 15, 40, false);
//2)Create a panel for the option
dlg.swap = dlg.add ('group');
dlg.swap.visible = true;
dlg.swap.orientation = 'stack';
//panel swaps in
dlg.swap.asgnPnlV = dlg.swap.add('panel', [10, 200, 705, 450], 'Assign material name');
dlg.swap.asgnPnlV.visible = false;
var ddlName = dlg.swap.asgnPnlV.veneerMfg;
panelOption(1, dlg.swap.asgnPnlV.veneerMfgTitle, 'Manufacturer:', dlg.swap.asgnPnlV.veneerMfg, dlg.swap.asgnPnlV.veneerMfgAddBox, dlg.swap.asgnPnlV.veneerMfgNew, dlg.swap.asgnPnlV.veneerMfgEditText, dlg.swap.asgnPnlV.veneerMfgAddButt, dlg.swap.asgnPnlV.veneerMfgEdtButt, dlg.swap.asgnPnlV.veneerMfgRmvButt, dlg.swap.asgnPnlV, 15, 40, true);
panelOption(1, dlg.swap.asgnPnlV.veneerSpeciesTitle, 'Species', dlg.swap.asgnPnlV.veneerSpecies, dlg.swap.asgnPnlV.veneerSpeciesAddBox, dlg.swap.asgnPnlV.veneerSpeciesNew, dlg.swap.asgnPnlV.veneerSpeciesEditText, dlg.swap.asgnPnlV.veneerSpeciesAddButt, dlg.swap.asgnPnlV.veneerSpeciesEdtButt, dlg.swap.asgnPnlV.veneerSpeciesRmvButt, dlg.swap.asgnPnlV, 50, 75, true);
panelOption(1, dlg.swap.asgnPnlV.veneerCutTitle, 'Cut', dlg.swap.asgnPnlV.veneerCut, dlg.swap.asgnPnlV.veneerCutsAddBox, dlg.swap.asgnPnlV.veneerCutNew, dlg.swap.asgnPnlV.veneerCutEditText, dlg.swap.asgnPnlV.veneerCutAddButt, dlg.swap.asgnPnlV.veneerCutEdtButt, dlg.swap.asgnPnlV.veneerCutRmvButt, dlg.swap.asgnPnlV, 85, 110, true);
panelOption(1, dlg.swap.asgnPnlV.veneerStainTitle, 'Stain', dlg.swap.asgnPnlV.veneerStain, dlg.swap.asgnPnlV.veneerStainAddBox, dlg.swap.asgnPnlV.veneerStainNew, dlg.swap.asgnPnlV.veneerStainEditText, dlg.swap.asgnPnlV.veneerStainAddButt, dlg.swap.asgnPnlV.veneerStainEdtButt, dlg.swap.asgnPnlV.veneerStainRmvButt, dlg.swap.asgnPnlV, 120, 145, false);
panelOption(1, dlg.swap.asgnPnlV.veneerMapTitle, 'Map', dlg.swap.asgnPnlV.veneerMap, dlg.swap.asgnPnlV.veneerMapAddBox, dlg.swap.asgnPnlV.veneerMapNew, dlg.swap.asgnPnlV.veneerMapEditText, dlg.swap.asgnPnlV.veneerMapAddButt, dlg.swap.asgnPnlV.veneerMapEdtButt, dlg.swap.asgnPnlV.veneerMapRmvButt, dlg.swap.asgnPnlV, 155, 180, false);
//This one works(below), but this is not what I want
//materialName(dlg.swap.asgnPnlV.veneerMatName, dlg.swap.asgnPnlV, 'sel1', 'sel2', 'sel3', 'sel4', 'sel5');
//This one (below) is what I actually want Error 21, undefined is not an object line 25....
materialName(dlg.swap.asgnPnlV.veneerMatName, dlg.swap.asgnPnlV, dlg.swap.asgnPnlV.veneerMfg.selection, dlg.swap.asgnPnlV.veneerSpecies.selection, dlg.swap.asgnPnlV.veneerCut.selection, dlg.swap.asgnPnlV.veneerStain.selection, dlg.swap.asgnPnlV.veneerMap.selection);
//Other panels to swap in
////
dlg.show();
////
function panelOption(panelPosition, title, titleText, name, nameAddbox, nameNew, nameEditText, nameAddButt, nameEdtButt, nameRmvButt, pnlNm, x1, x2, isUpperCase){
var listArray = ['test1', 'test2', 'test3', 'veneer'];
title = pnlNm.add('StaticText', [15, x1, 110, x2], titleText.toString());
name = pnlNm.add('DropDownList', [120, x1, 230, x2], listArray);
nameAddbox = pnlNm.add('checkbox', [240, x1, 260, x2], 'Edit List');
nameAddbox.value = false;
nameNew = pnlNm.add('StaticText', [330, x1, 360, x2], 'New:');
nameEditText = pnlNm.add('EditText', [360, x1, 480, x2],);
nameEditText.enabled = false;
nameAddButt = pnlNm.add('button', [490, x1, 545, x2], ' add ');
nameAddButt.enabled = false;
nameEdtButt = pnlNm.add('button', [555, x1, 610, x2], 'edit');
nameEdtButt.enabled = false;
nameRmvButt = pnlNm.add('button', [620, x1, 675, x2], 'remove');
nameRmvButt.enabled = false;
if (panelPosition == 0){
name.onChange = function(){
var x = name.selection;
switch (x.toString()){
case 'veneer':
dlg.swap.asgnPnlV.visible = true;
dlg.swap.asgnPnlSS.visible = false;
break;
default:
dlg.swap.asgnPnlV.visible = false;
}
}
}
}
function materialName(matName, pnlNm, nom1, nom2, nom3, nom4, nom5){
var temp = 'material name here build as it goes';
var nomenclature1 = nom1;
var nomenclature2 = nom2;
var nomenclature3 = nom3;
var nomenclature4 = nom4;
var nomenclature5 = nom5;
var temp = nomenclature1 + '_' + nomenclature2 + '_' + nomenclature3 + '_' + nomenclature4 + '_' + nomenclature5;
matName = pnlNm.add('StaticText', [15, 205, 310, 230], temp);
if (pnlNm.visible == true){
dlg.findElement ('veneerMfg').onChange = function() {
//var nomenclature1 = Nom1.selection;
//return nomenclature1;
alert ('found element');
}
}
}
What I need is the the name.selection to be one of the values in the dropdown list that was selected by the user.
Whenever I call the name.selection element that was created with the function it gives be 'undefined'. I tried dlg.findElement ('veneerMfg') this gave me 'null', which I am guessing it means it found the element, but I need to be able to find it when the user changes the selection in the dropdown box.
I think the easiest way to access the dropdowns would be via children of your dlg.swap.asgnPnlV panel, something like this:
var myButton = dlg.add('button',undefined,'Test');
myButton.onClick = function() {
var k = 1; // there're 40 children, each 8th is the dropdown, starting from index 1
var names = [];
for (var i = 0; i < dlg.swap.asgnPnlV.children.length; i++) {
// add only every 8th element
if ((i-k)%8 == 0) {
names.push(dlg.swap.asgnPnlV.children[i].selection)
}
}
alert(names);
}
So your function would look something like this:
materialName(dlg.swap.asgnPnlV.veneerMatName, dlg.swap.asgnPnlV, dlg.swap.asgnPnlV.children[1].selection, dlg.swap.asgnPnlV.children[9].selection, dlg.swap.asgnPnlV.children[17].selection, dlg.swap.asgnPnlV.children[25].selection, dlg.swap.asgnPnlV.children[33].selection);
This is however not very versatile. Even better would be adding all those panel in a loop, this way it's possible to modify my names object on the fly without asking for all the children. Note the closure I used to make sure that all dropdowns will change names properly.
var panelData = [
{
name: 'Manufacturer',
list: ['m1', 'm2', 'm3'],
coords: [15, 40]
},
{
name: 'Species',
list: ['s1', 's2'],
coords: [50, 75]
},
{
name: 'Cut',
list: ['cut1', 'cut2', 'cut3', 'cut4', 'cut5'],
coords: [85, 110]
},
{
name: 'Stain',
list: ['stain1', 'stain2', 'stain3', 'stain4'],
coords: [120, 145]
},
{
name: 'Map',
list: ['map1', 'map2', 'map3', 'map4', 'map5', 'map6'],
coords: [155, 180]
}];
var mygroup;
var names = {};
for (var i = 0; i < panelData.length; i++)
{
(function(item)
{
var mygroup = dlg.swap.asgnPnlV.add('group', [15, item.coords[0], 410, item.coords[1]]);
mygroup.add('statictext', [0, 0, 110, 20], item.name);
var myDropDown = mygroup.add('DropDownList', [110, 0, 230, 20], item.list);
myDropDown.onChange = function()
{
names[item.name] = String(this.selection)
}
})(panelData[i])
}
var myB = dlg.add('button', undefined, 'Test');
myB.onClick = function()
{
alert(JSON.stringify(names));
}
so your function would look like this:
materialName(dlg.swap.asgnPnlV.veneerMatName, dlg.swap.asgnPnlV, names['Manufacturer'], names['Species'], names['Cut'], names['Stain'], names['Map']);

tricky part of google charts Column with drill down functionality?

i am creating google charts and I already implement top 5 user column charts after that if you select first user column than displaying first user page history data from other variables(eachuser_data) its easy implement function in high charts! but in google charts, I don't know about add events.addListener work or not in this problem. let me know google charts provide click event on each column and display other graphs in same graph draw function. ? thank you in advance
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var charts = {};
var options = {
Column: {
chartArea: {
height: '100%',
width: '100%',
top: 24,
left: 64,
right: 32,
bottom: 48,
},
'vAxis': {
title: 'Cost in USD ($)', format:'$#',
},
height: '100%',
legend: {
position: 'bottom'
},
width: '100%'
}
};
// columns charts data
//top 5 user data with total click
var jsonData = [["johan",69],["jack",23],["scott",24],["x",5],["y",10]];
loadData(jsonData, '1', 'Column');
//specifc user data
var user1 = [["report1",45],["report2",40],["index.html",50]];
var user2 = [["report1",4],["report2",3],["index.html",5]];
var user3 = [["report1",4],["report2",3],["index.html",5]];
var user4 = [["report1",4],["report2",3],["index.html",5]];
var user5 = [["report1",4],["report2",3],["index.html",5]];
// load json data
function loadData(jsonData, id, chartType) {
// create data table
var dataTable = new google.visualization.DataTable();
// add date column
dataTable.addColumn('string', 'Total numbe of click');
var rowIndex = dataTable.addRow();
dataTable.setValue(rowIndex, 0, dataTable.getColumnLabel(0));
$.each(jsonData, function(productIndex, product) {
var colIndex = dataTable.addColumn('number', product[0]);
// add product data
dataTable.setValue(rowIndex, colIndex, product[1]);
});
// draw chart
$(window).resize(function () {
drawChart(id, dataTable);
});
drawChart(id, dataTable);
}
function drawChart(id, dataTable) {
if (!charts.hasOwnProperty(id)) {
charts[id] = new google.visualization.ChartWrapper({
chartType: 'ColumnChart',
containerId: 'chart-' + id,
options: {
vAxis: {
title: 'Cost in USD ($)',
format: '$#',
},
width: '100%',
height: '100%',
legend: {
position: 'bottom'
},
},
});
}
charts[id].setDataTable(dataTable);
charts[id].draw();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart-1"></div>
to know which column has been clicked / selected,
listen for the 'select' event
google.visualization.events.addListener(chart, 'select', chartSelection);
then use chart method getSelection() to get the row and column index of the column selected
getSelection will return an array of objects
[{row: 0, column: 1}]
the select event will fire both when a column is selected and un-selected
be sure to check the length of the array return by getSelection()
before trying to access the array contents
for column charts, only one column can be selected at a time
so the values of the selection will always be the first element in the array
function chartSelection() {
var selection = chart.getSelection();
if (selection.length > 0) {
var row = selection[0].row;
var col = selection[0].column;
var xValue = data.getValue(row, 0);
var yValue = data.getValue(row, col);
console.log('selection: ' + xValue + ' = ' + yValue);
} else {
console.log('nothing selected');
}
}
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['x', 'y0', 'y1'],
['A', 6, 7],
['B', 7, 9],
['C', 8, 11],
['D', 9, 11],
['E', 5, 6]
]);
var options = {
legend: {
alignment: 'end',
position: 'top'
}
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'select', chartSelection);
function chartSelection() {
var selection = chart.getSelection();
if (selection.length > 0) {
var row = selection[0].row;
var col = selection[0].column;
var xValue = data.getValue(row, 0);
var yValue = data.getValue(row, col);
console.log('selection: ' + xValue + ' = ' + yValue);
} else {
console.log('nothing selected');
}
}
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Javascript: setInterval for multiple chart updates

I'm trying to display three Google Chart Gauges on a page to represent data from three temperature sensors. I have a JS function GetCurrentTemperature that returns the three temperature values in an array. I want the gauges to update at regular intervals. I've had this working fine with a single gauge, but when I try and use setInterval for the three charts, they're not updating. The code I'm using is listed below.
function drawTemperatureGauges() {
var currentTemp = GetCurrentTemperature();
var gaugeCount = currentTemp.length;
var options = {
width: 200,
height: 200,
redFrom: 65,
redTo: 80,
yellowFrom: 50,
yellowTo: 65,
minorTicks: 5,
max: 80
};
for(var i=0; i<gaugeCount; i++) {
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
['Temp', currentTemp[i] ]
]);
var divName = 'gauge'.concat(i+1).concat('_div');
var chart = new google.visualization.Gauge(document.getElementById(divName));
chart.draw(data, options);
setInterval(function() {
var cTemp = GetCurrentTemperature();
data.setValue(0, 1, cTemp[i]);
chart.draw(data, options);
}, 2000);
}
}
I assume it's because I'm using i inside the anonymous setInterval function. I've looked at posts related to closures and also ones that specify the use of let rather than var but I still can't work out what syntax I need.
Any pointers greatly appreciated
Bbz
did you try locking in a closure like this?
for(var i=0; i<gaugeCount; i++) {
drawGauge(i);
}
function drawGauge(i) {
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
['Temp', currentTemp[i] ]
]);
var divName = 'gauge'.concat(i+1).concat('_div');
var chart = new google.visualization.Gauge(document.getElementById(divName));
chart.draw(data, options);
setInterval(function() {
var cTemp = GetCurrentTemperature();
data.setValue(0, 1, cTemp[i]);
chart.draw(data, options);
}, 2000);
}

Multiple Instances of Google Visualizations Chart Inside Separate Divs

I'm trying to show several Google Gauge charts in separate divs on the same screen. I also need to handle the click event on those divs (consequently the charts). I tried to do that dynamically but I had some issues. But anyway, even when I tried do this statically (which worked), I still couldn't get the chart area to be clickable. What happened is that the whole div is clickable except for the chart area.
Anyway, here's my (messy - test) code:
<div id="gaugePlaceHolder" class="gaugeWrapper"></div>
<div id="gaugePlaceHolder2" class="gaugeWrapper"></div>
document.getElementsByClassName = function (cl) {
var retnode = [];
var myclass = new RegExp('\\b' + cl + '\\b');
var elem = this.getElementsByTagName('*');
for (var i = 0; i < elem.length; i++) {
var classes = elem[i].className;
if (myclass.test(classes)) retnode.push(elem[i]);
}
return retnode;
};
google.load('visualization', '1', {packages:['gauge']});
google.setOnLoadCallback(function () {
drawChart1();
drawChart2();
});
function drawChart1() {
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
['Memory', 80]
]);
var options = {
width: 400, height: 120,
redFrom: 90, redTo: 100,
yellowFrom:75, yellowTo: 90,
minorTicks: 5
};
var chart = new google.visualization.Gauge(document.getElementById('gaugePlaceHolder'));
chart.draw(data, options);
}
function drawChart2() {
var data = google.visualization.arrayToDataTable([
['Label', 'Value'],
['Another', 30]
]);
var options = {
width: 400, height: 120,
redFrom: 90, redTo: 100,
yellowFrom: 75, yellowTo: 90,
minorTicks: 5
};
var chart = new google.visualization.Gauge(document.getElementById('gaugePlaceHolder2'));
chart.draw(data, options);
}
window.onload = function () {
var elements = $('.gaugeWrapper');
console.log(elements);
elements.click(function () {
alert("clicked");
});
}
Any explanations/suggestions?
The right way to add a listener to a Gauge is using google.visualization.events.addListener method, as shown in this example.
You could also try your code on Google Playground.

Categories

Resources