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...
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']);
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>
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);
}
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.