Trouble with Google charts, queries and javascript - javascript

I'm trying to create a very simple website consisting of a Google bubble chart overlaid on a background image. The bubble chart is supposed to be generated based on data from a set of Google spreadsheets, and the user can select, through a simple text form, the names of the data points that are to be published. In the code below I have obfuscated the spreadsheet URL, because I don't want to make the data public yet (note however that the viewing privileges of the document are set to public, as required).
The code doesn't work, as no chart is drawn. Obviously there is something fishy going on, because there is never an alert from the collectData method, which handles the query response.
Also, initially I didn't have the draw method and its code was instead in the sendAndDraw method, sans the setTimeout. In that version, I got a Data table not found error on the webpage, but again, the collectData method didn't seem to be called, as it didn't raise an error.
Any suggestions as to what might be the issue? I should add that I am completely new to both javascript and Google developers tools.
EDIT: Following kfa's comment, the form was changed to include a post method. Now I get the Data Table not defined problem once again.
<!DOCTYPE html>
<html>
<head>
<style>
#container{
position: relative;
width: 200px;
height: 200px;
}
#background{
width: 100%;
height: 100%;
}
#bubbleChart{
width: 100%;
height: 100%;
}
</style>
<title>gRNA</title>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages' : ['corechart']});
google.charts.setOnLoadCallback(function() {initialize()});
var dataSourceUrls = [
'https://docs.google.com/spreadsheets/d/foo/edit#gid=0&headers=1'
];
var nSheets = dataSourceUrls.length;
var query;
var bubbleDataArray = [];
var bubbleData;
function initialize() {
//Currently not doing anything here
}
//Takes a list of names as a comma separated list.
function sendQueries(nameString) {
var queryString = generateQuery(nameString);
for(i = 0; i < nSheets; i++) {
query = new google.visualization.Query(dataSourceUrls[i]);
query.setQuery(queryString);
query.send(collectData);
query.abort();
}
}
//Generates the query string for the selected names.
function generateQuery(nameString) {
nameString = nameString.split(",");
var nNames = nameString.length;
var queryString = [];
queryString.push("select F, D, E, B ");
queryString.push("where F matches ");
queryString.push(nameString[0]);
for(i = 1; i < nNames; i++) {
queryString.push("or ");
queryString.push(nameString[i]);
}
return queryString.join("");
}
//Collect and manage the query responses.
function collectData(response) {
alert('Hi!');
if(response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' +
response.getDetailedMessage());
return;
}
var data = response.getDataTable();
if(data.getNumberOfRows() > 0) {
bubbleDataArray.push(data);
}
}
function sendAndDraw(nameString) {
bubbleDataArray = [];
sendQueries(nameString);
setTimeout(draw,2000);
}
function draw() {
bubbleData = bubbleDataArray[0];
var nTables = bubbleDataArray.length;
for(i = 1; i < nTables; i++) {
bubbleData = google.visualization.data.join(bubbleData,
bubbleDataArray[i], 'full', [[0,0]],
[1,2,3], [1,2,3]);
}
var container = document.getElementById('bubbleChart');
var bubbleChart = new google.visualization.BubbleChart(container);
var options = {
'backgroundColor':'transparent'
}
bubbleChart.draw(bubbleData,options);
}
function plot() {
sendAndDraw(document.getElementById('nameSel').value);
}
</script>
</head>
<body>
<form onsubmit="plot(); return false;" method="post">
<input type="text" id="nameSel"/>
<input type="submit" value="Plot"/>
</form>
<br />
<div id="container">
<div id="background"></div>
<div id="bubbleChart"></div>
</div>
</body>
</html>

Related

Searching from JSON

I am trying to build an AJAX search form for a dataset that queries an open data API and displays search results below the form. I want to include one or more inputs that correspond to fields within my selected JSON dataset.
When the form is submitted I want to use the form data to query the Open Data API. I want to allow users to find a subset of records from a specific dataset.
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="data.js"></script>
<title></title>
</head>
<body>
<div class="container">
<form>
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>
</body>
</html>
JavaScript:
let x = document.getElementById('input');
let obj;
fetch('https://data.winnipeg.ca/resource/f58p-2ju3.json')
.then(response => {
return response.json()
})
.then(data => {
console.log(data)
obj = JSON.parse(data)
})
This is my code. I want the user to search by the location name and then the whole information will be displayed meaning observationid, observationtime, thingid and locationname in the form of a table. Only for the specific locationname entered by the user.
In case you need for searching in the loaded JSON data, you'll need 3 methods a least.
init(). This method loads the whole JSON from the API for the first time.
search(inputValue). This method takes the inputValue value in order to be used to filter the whole JSON data.
For instance, in my search() method, I'm using the toLowerCase() to turn in lowercase the current string, and indexOf() string method to find records of a location name by the first occurrence of any given character, so you could search by inserting any character of a particular location name.
search(inputValue) {
const results = this.data.filter(x => x.locationname.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
return this.renderTable(results);
}
renderTable(results). This method has the responsibility to render the filtered data given from the search(inputValue) method.
Here is a small demo where you may see it in action.
class SearchJSON {
data = null;
constructor() {
this.init();
}
init() {
fetch('https://data.winnipeg.ca/resource/f58p-2ju3.json')
.then(response => {
return response.json();
})
.then(data => {
this.data = data;
});
}
search(inputValue) {
const results = this.data.filter(x => x.locationname.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
return this.renderTable(results);
}
renderTable(results) {
let html = "<table><thead><tr><th>Observation Id</th><th>Observation Time</th><th>Thing Id</th><th>Location Name</th></tr></thead></table><table class=\"result-table\"><tbody>";
let result = null;
for (let i = 0; i < results.length; i++) {
result = results[i];
html += `<tr><td>${result.observationid}</td>
<td>${result.observationtime}</td>
<td>${result.thingid}</td>
<td>${result.locationname}</td>
<tr>`;
}
html += "</tbody></table>";
return html;
}
}
window.onload = () => {
const form = document.querySelector("form");
const input = document.getElementById("input");
const searchJSON = new SearchJSON();
form.onsubmit = (e) => {
e.preventDefault();
const result = searchJSON.search(input.value);
const formDiv = document.querySelector("form div");
const div = document.createElement("div");
div.innerHTML = result;
if (formDiv !== null) {
formDiv.parentElement.removeChild(formDiv);
form.appendChild(div);
}
form.appendChild(div);
};
};
.container {
font-size: 0.8rem;
}
.container table {
border-collapse: collapse;
}
.container table th,
.container table td {
border: #ccc solid 1px;
padding: 0.2rem 0.4rem;
width: 100px;
}
.result-table {
display: inline-block;
height: 230px;
overflow: auto;
}
<div class="container">
<form>
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>
I'm not sure about the capability of the endpoint but I think that It may be good to investigate if the https://data.winnipeg.ca/resource/f58p-2ju3.json endpoint can receive parameters, therefore you can send parameters to the endpoint with the given form data to return the required data so, you might no need to use the init() method from my example to load the whole JSON data at the first time.
Update: Endpoint with query string parameters:
I've found a way to use query string parameters as you needed. You may use it in this way:
`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`
Where:
locationname is the location name to search.
$limit is the max quantity of records to retrieve by the endpoint with the current parameters.
And also, you may use async/await fetch requests like this:
async search(inputValue, limit) {
const response = await fetch(`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`);
const results = await response.json();
return this.renderTable(results);
}
You may see it in action here:
class SearchJSON {
data = null;
async search(inputValue, limit) {
const response = await fetch(`https://data.winnipeg.ca/resource/f58p-2ju3.json?locationname=${inputValue}&$limit=${limit}`);
const results = await response.json();
return this.renderTable(results);
}
renderTable(results) {
let html = "<table><thead><tr><th>Observation Id</th><th>Observation Time</th><th>Thing Id</th><th>Location Name</th></tr></thead></table><table class=\"result-table\"><tbody>";
let result = null;
for (let i = 0; i < results.length; i++) {
result = results[i];
html += `<tr><td>${result.observationid}</td>
<td>${result.observationtime}</td>
<td>${result.thingid}</td>
<td>${result.locationname}</td>
<tr>`;
}
html += "</tbody></table>";
return html;
}
}
window.onload = () => {
const form = document.querySelector("form");
const txtLimit = document.getElementById("txtLimit");
const input = document.getElementById("input");
const searchJSON = new SearchJSON();
form.onsubmit = async(e) => {
e.preventDefault();
const result = await searchJSON.search(input.value, txtLimit.value);
const formDiv = document.querySelector("form div");
const div = document.createElement("div");
div.innerHTML = result;
if (formDiv !== null) {
formDiv.parentElement.removeChild(formDiv);
form.appendChild(div);
}
form.appendChild(div);
};
};
.container {
font-size: 0.8rem;
}
.container table {
border-collapse: collapse;
}
.container table th,
.container table td {
border: #ccc solid 1px;
padding: 0.2rem 0.4rem;
width: 100px;
}
.result-table {
display: inline-block;
height: 230px;
overflow: auto;
}
<div class="container">
<form>
<input type="number" id="txtLimit" step="10" max="1000" min="0" value="10">
<input type="text" id="input" placeholder="what you are looking for?">
<button type="submit">Search</button>
</form>
</div>
assign JSON to variable and use $.getJSON
use below code with your search button,
$("#search").on('keypress keyup change input', function() {
var arrival = $(this).val().toLowerCase();
$('#matches').text(!arrival.length ? '' :
dataArr.filter(function(place) {
// look for the entry with a matching `code` value
return (place.title.toLowerCase().indexOf(arrival) !== -1);
}).map(function(place) {
// get titles of matches
return place.title;
}).join('\n')); // create one text with a line per matched title
});

Detecting a click after a new element is loaded after the DOM

I am trying to create a simple Chrome Plugin - however I have come to an issue.
I am trying to detect a click on a div using a simple getElementById - however as the api call happens after the DOM is loaded the JS cannot 'find' any div's and gives an error and doesn't do anything after I click on the element.
How do I detect the click, after the data from the API has loaded? I have included some of my code below:
Thanks
document.addEventListener('DOMContentLoaded', function () {
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function () {
inputBox = document.getElementById("postcodeInput").value
console.log(inputBox)
let xml = new XMLHttpRequest();
xml.open('get', "https://api.getaddress.io/find/" + inputBox + "/?api-key=SECRET&expand=true", false);
xml.send(null);
var data = xml
var arr = xml.responseText
var data = JSON.parse(arr)
var postcode = data.postcode
var addresses = data.addresses
console.log(addresses)
document.getElementById("postcode").innerHTML = postcode;
var text = "";
var i;
for (i = 0; i < addresses.length; i++) {
text += "<div id='addressClick' name=" + i + ">" + addresses[i].line_1 + "</div>" + "<br>";
}
document.getElementById("data").innerHTML = text;
clickFunc()
}, false);
}, false);
function clickFunc() {
var rowBox = document.getElementById("addressClick");
rowBox.addEventListener('click', function () {
console.log('asd');
}, true);
}
HTML
<!doctype html>
<html>
<head>
<title>Address Search</title>
<script src="popup.js"></script>
</head>
<body>
<h3>Address Search</h3>
<input type="text" id='postcodeInput' name="postcodeInput" value="KW1 4YT">
<button id="checkPage">Search</button>
<div class='results'>
<h3>Results - <span id='postcode'></span></h3>
<p id='data'></p>
</div>
</body>
</html>
<style>
body {
width: 200px
}
#addressClick:hover {
color: blue;
cursor: pointer
}
</style>
You can attach an EventListener to all the body and, at every click, detect if the clicked element is the desired one:
document.body.addEventListener('click', event => window.alert(event.target.innerText));
This can sound like an aggressive solution, but it's way less invasive than a MutationObserver

Parse a Google spreadsheet into a Javascript array

I have a Google spreadsheet (https://docs.google.com/spreadsheets/d/e/2PACX-1vRc8Lx0N-wf3f1xAAXkNFUqQjaWPFcde3YjK02gCBqGpUrULwHC6NC0sndeLJBvOyKkA88hvtH335pR/pubhtml) which I'd like to access in a webpage to use with Google Maps API.
As per Google API'S documentation, the script should look like this:
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script>
google.charts.load('current', { 'packages': ['map'] });
google.charts.setOnLoadCallback(drawMap);
function drawMap() {
var data = google.visualization.arrayToDataTable([
['Country', 'Population'],
['China', 'China: 1,363,800,000'],
['India', 'India: 1,242,620,000'],
['US', 'US: 317,842,000'],
['Indonesia', 'Indonesia: 247,424,598'],
['Brazil', 'Brazil: 201,032,714'],
['Pakistan', 'Pakistan: 186,134,000'],
['Nigeria', 'Nigeria: 173,615,000'],
['Bangladesh', 'Bangladesh: 152,518,015'],
['Russia', 'Russia: 146,019,512'],
['Japan', 'Japan: 127,120,000']
]);
var options = {
showTooltip: true,
showInfoWindow: true
};
var map = new google.visualization.Map(document.getElementById('chart_div'));
map.draw(data, options);
};
</script>
</head>
<body>
<div id="chart_div"></div>
</body>
</html>
So my goal is to dynamically replace:
[
['Country', 'Population'],
['China', '1,363,800,000'],
['India', '1,242,620,000'],
['US', '317,842,000'],
['Indonesia', '247,424,598'],
['Brazil', '201,032,714'],
['Pakistan', '186,134,000'],
['Nigeria', '173,615,000'],
['Bangladesh', '152,518,015'],
['Russia', 'Russia: 146,019,512'],
['Japan', 'Japan: 127,120,000']
]
...with the content of the Google Spreadsheet.
I am new at JS, and I'm struggling to properly convert an exported JSON from Google Spreadsheets into a JS array. Searching around, I stumbled upon an interresting script:
<!doctype html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
var spData = null;
function doData(json) {
spData = json.feed.entry;
}
function drawCell(tr, val) {
var td = $("<td/>");
tr.append(td);
td.append(val);
return td;
}
function drawRow(table, rowData) {
if (rowData == null) return null;
if (rowData.length == 0) return null;
var tr = $("<tr/>");
table.append(tr);
for(var c=0; c<rowData.length; c++) {
drawCell(tr, rowData[c]);
}
return tr;
}
function drawTable(parent) {
var table = $("<table/>");
parent.append(table);
//console.log(table);
return table;
}
function readData(parent) {
var data = spData;
var table = drawTable(parent);
var rowData = [];
for(var r=0; r<data.length; r++) {
var cell = data[r]["gs$cell"];
var val = cell["$t"];
if (cell.col == 1) {
drawRow(table, rowData);
rowData = [];
}
rowData.push(val);
}
drawRow(table, rowData);
}
$(document).ready(function(){
readData($("#data"));
});
</script>
<script src="https://spreadsheets.google.com/feeds/cells/1TTeG6mp2rb61Yxi5KO3GFmZ3qQ3RAMlB9bisLciuj-M/1/public/values?alt=json-in-script&callback=doData"></script>
<style type="text/css">
table {border-collapse: collapse; width: 100%;}
th, td {border: thin solid black; padding: 3px;}
tr.head th, tr.head td {background-color: #EDEDED; border-bottom: 4px double black;}
span.linetitle {font-weight: bold;}
div.lineclass {font-style: italic;}
.title, .result {width: 80%;}
.notes {width: 15%;}
h1 {text-align: center;}
body {margin: 12px; font-size: 12px;}
</style>
<style type="text/css" media="print">
form {display: none;}
</style>
</head>
<body>
<h1>Parse Google Spreadsheet with JavaScript</h1>
<div id="data"/>
</body>
</html>
...which fetches the spreadsheet, and turns it into a HTML table. However, I can't seem to find a way to build an array from this.
After this long context, here's my question: how can I fetch the Google spreadsheet to insert it as data in the drawMap function above?
The Google spreadsheet API you are using is supposed to be called with JSONP.
I used jQuery's simple implementation of JSONP via the $.ajax function.
You can see my solution with line by line explanations:
var spreadsheetUrl = 'https://spreadsheets.google.com/feeds/cells/1TTeG6mp2rb61Yxi5KO3GFmZ3qQ3RAMlB9bisLciuj-M/1/public/values?alt=json-in-script&callback=doData';
// The callback function the JSONP request will execute to load data from API
function doData(data) {
// Final results will be stored here
var results = [];
// Get all entries from spreadsheet
var entries = data.feed.entry;
// Set initial previous row, so we can check if the data in the current cell is from a new row
var previousRow = 0;
// Iterate all entries in the spreadsheet
for (var i = 0; i < entries.length; i++) {
// check what was the latest row we added to our result array, then load it to local variable
var latestRow = results[results.length - 1];
// get current cell
var cell = entries[i];
// get text from current cell
var text = cell.content.$t;
// get the current row
var row = cell.gs$cell.row;
// Determine if the current cell is in the latestRow or is a new row
if (row > previousRow) {
// this is a new row, create new array for this row
var newRow = [];
// add the cell text to this new row array
newRow.push(text);
// store the new row array in the final results array
results.push(newRow);
// Increment the previous row, since we added a new row to the final results array
previousRow++;
} else {
// This cell is in an existing row we already added to the results array, add text to this existing row
latestRow.push(text);
}
}
handleResults(results);
}
// Do what ever you please with the final array
function handleResults(spreadsheetArray) {
console.log(spreadsheetArray);
}
// Create JSONP Request to Google Docs API, then execute the callback function doData
$.ajax({
url: spreadsheetUrl,
jsonp: 'doData',
dataType: 'jsonp'
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Tableau JavaScript API - get Selected value

I am currently researching the possibility to grabbing data from the Tableau report(s) via the JavaScript API but the closet I can get to grabbing values from a graph after filtering is selecting the value via the selectSingleValue() method.
For example: JavaScript API Tutorial
In the API tutorial tab called 'Select'. One of the examples selects the row "Marcao Sao, China". Is it possible to extract that numerical value of $52.0k ?
I have tried looking into the Objects returned (via FireBug) but I cannot seem to locate the right object. My recent location was in getActiveSheets().
Any help would be appreciated.
In the JavaScript API tutorial tab 'Events' it shows you how to add an event listener to return the selected marks. You can then loop through the marks to get the values you want.
Copy the below code block into a file, save as html and open in your favourite web browser (tested on ie11).
<html>
<head>
<meta charset="utf-8">
<title>Tableau 8 Javascrip API</title>
<script type="text/javascript" src="http://public.tableausoftware.com/javascripts/api/tableau_v8.js"></script>
<script type="text/javascript">
/////////////////////
// Global variables
var viz, workbook, activeSheet
// function called by viz on marks being selected in the workbook
function onMarksSelection(marksEvent) {
return marksEvent.getMarksAsync().then(reportSelectedMarks);
}
function reportSelectedMarks(marks) {
for (var markIndex = 0; markIndex < marks.length; markIndex++) {
var pairs = marks[markIndex].getPairs();
for (var pairIndex = 0; pairIndex < pairs.length; pairIndex++) {
var pair = pairs[pairIndex];
if (pair.fieldName == "AVG(F: GDP per capita (curr $))") {
alert("You selected a country with an avg GPD per capita of " + pair.formattedValue);
}
}
}
}
// Initialise the viz to hold the workbook
function initializeViz(){
var placeholderDiv = document.getElementById("tableauViz");
var url = "http://public.tableausoftware.com/views/WorldIndicators/GDPpercapita?Region=";
var options = {
width: "800px", //width: placeholderDiv.offsetWidth,
height: "400px", //height: placeholderDiv.offsetHeight,
hideTabs: true,
hideToolbar: true,
onFirstInteractive: function () {
workbook = viz.getWorkbook();
activeSheet = workbook.getActiveSheet();
}
};
viz = new tableauSoftware.Viz(placeholderDiv, url, options);
// Add event listener
viz.addEventListener(tableauSoftware.TableauEventName.MARKS_SELECTION, onMarksSelection);
}
</script>
</head>
<body>
<!-- Tableau view goes here -->
<div id="tableauViz" style="height:1200px; width:1200px"\></div>
<script type='text/javascript'>
//Initialize the viz after the div is created
initializeViz();
</script>
</body>
</html>

Access data from LabelInput using Google Closure

I am trying to read input got through autocomplete and display it as a alert. After accessing i get undefined as alert instead of the value accessed through labelinput.
The ex.js file is as follows
goog.require('goog.dom');
goog.require('goog.ui.LabelInput');
goog.require('goog.ui.ac');
goog.require('goog.events.EventType');
function autoComplete() {
var jobj = [{"cityname":"Bangalore","cityid":"1"},
{"cityname":"Bellary","cityid":"2"},
{"cityname":"Belgaum","cityid":"3"},
{"cityname":"Bidar","cityid":"4"},
{"cityname":"Mumbai","cityid":"5"},
{"cityname":"Munnar","cityid":"6"},
{"cityname":"Delhi","cityid":"7"},
{"cityname":"Diu/Daman","cityid":"8"}];
var li1 = new goog.ui.LabelInput("Enter City Name");
li1.render(goog.dom.getElement('d1'));
var array1 = new Array();
for (var i=0;i<jobj.length; i++)
{
array1[i] = jobj[i].cityname;
}
var ac2 = goog.ui.ac.createSimpleAutoComplete(
array1, goog.dom.getElement('d1'), false);
goog.events.listen(ac2,
goog.ui.ac.AutoComplete.EventType.UPDATE,
function() { var val2 = (goog.dom.getElement('d1').value);
alert(val2);
});
}
The ex.html file is as follows
<html>
<head>
<script src="../closure-library/closure/goog/base.js"></script> <!--My Closure Library Location -->
<script src="ex.js"></script>
</head>
<body onload="autoComplete();">
<style>
.ac-renderer {
position: absolute;
width: 300px;
background-color: #fff;
border: 1px solid;
}
</style>
<div id="d1">City &nbsp </div><br><br>
</body>
</html>
"goog.dom.getElement('d1')" will return a div element, which will not have a value. The LabelInput renders a control inside of that element when you call
li1.render(goog.dom.getElement('d1'));
You should be using the getValue method of the LabelInput class itself
li1.getValue()
or if you want to access the Input element created during the LabelInput render method, call
li1.getElement().value
Source : http://docs.closure-library.googlecode.com/git/class_goog_ui_LabelInput.html

Categories