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>
Related
Re-worded objective:
I'm creating a web page using google apps script. I want to have multiple drop downs listed on the page. I know if I use <select><option>, I can create a list of hard coded options. What I would rather do is grab the options from a google sheet to display in the drop down, this way I can update it at anytime without modifying the HTML code.
The issue: While I was successful in creating a drop down selection containing Column A values from my sheet, I'm running into an issue where Apps Script will not let me create another drop down containing the values of Column B.
This is my sheet that contains names and dietary types. Each column contains the options for each drop down.
This is what it looks like on the front end. I'd like to have another drop down beside it that contains values from Column B (as seen above).
Here is my script:
var url = "google sheets URL";
function doGet(e){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Staff");
var list = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp = HtmlService.createTemplateFromFile("index");
tmp.list = list.map(function(r){ return r[0]; });
return tmp.evaluate();
}
This is the HTML for my selection list:
<select id="app" class="browser-default">
<option disabled selected>Select a teammate!</option>
<? for(var i=0;i<list.length;i++){ ?>
<option><?= list[i]; ?></option>
<? } ?>
</select>
It functions correctly at this point but when trying to replicate it so I can grab another column in Google Sheets and use that as another selection list,
I get: Referenceerror: "list" is not defined.
This is the script that's causing me to get the error.
var url = "google sheets URL";
function doGet(e){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Staff");
var list = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp = HtmlService.createTemplateFromFile("index");
tmp.list = list.map(function(r){ return r[0]; });
return tmp.evaluate();
}
function doGet(f){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Variables");
var list2 = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp2 = HtmlService.createTemplateFromFile("index");
tmp2.list2 = list2.map(function(r){ return r[0]; });
return tmp2.evaluate();
}
Here's a simple example of getting multiple list from a dialog.
Load the code and run launchDialog(). You could also add a doGet() and use return HtmlService.createHtmlOutputFromFile('aq4');
Code.gs:
function getList(n) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getRange(2,n,sh.getLastRow()-1,1);
return rg.getValues().map(function(r){return r[0]});
}
function launchDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('aq4');
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'My List');
}
aq4.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
input{margin:5px;}
td,th{border:1px solid black;}
</style>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(vA){
var select=document.getElementById('sel1');
select.options.length=0;
for(var i=0;i<vA.length;i++) {
select.options[i] = new Option(vA[i],vA[i]);
}
})
.getList(1);
});
function getAnotherList() {
google.script.run
.withSuccessHandler(function(vA){
var select=document.getElementById('sel1');
select.options.length=0;
for(var i=0;i<vA.length;i++) {
select.options[i] = new Option(vA[i],vA[i]);
}
})
.getList(Math.floor(Math.random()*23));
}
console.log("My Code");
</script>
</head>
<body>
<select id="sel1"></select>
<input type="button" value="Get Another List" onClick="getAnotherList();" />
</body>
</html>
Here's my List Spreadsheet:
I have a google sheet, I want to prompt a user to select ranges to get information from, store that into an array, and then create a chart in an html popup. I have read a bit about the google.script.run functionality, and understand that without the withSuccessHandler(HTMLFunction).FunctionToCall() syntax at the end, the HTML script moves onto the next line. I have a .gs file below, and an .html file, and I was able to get the graph to work when I just entered a static array in my .gs function. However, I seem to be struggling with how to return focus to the editor to get a range, and then to bring the HTML dialog box with the chart back up and get the right data to the function that plots the chart. I saw here that I could use the google.script.host to call the editor.focus() function so the user can now select cells, but I can't seem to get the focus back to the HTML popup without calling the HTML file all over again. Here is my .gs function:
function RetrieveData(){
var ss = SpreadsheetApp.getActive();
var sheets = ss.getSheets();
var s = sheets[1];
var UI = SpreadsheetApp.getUi();
var response = UI.prompt("Please enter the first cell in the category").getResponseText();
var ir = s.getRange(response);
var n= 0;
var stored = [];
stored.push(["Income Category", "Frequency"]);
while (ir.getValue()!= "") {
n = n +1;
ir = ir.offset(1, 0);
}
ir = ir.offset(-n,0)
for(i =0; i<n;i++) {
stored.push([ir.getValue(),ir.offset(n+2,0).getValue()]);
ir = ir.offset(1, 0);
}
return stored;
}
Here is my html that is within the body (Stack Overflow is a little strict, so I am not going to go through the trouble of showing all the HTML; this is just within the body and it is what is communicating with the .gs file):
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(getdata);
function getdata() {
google.script.run.withSuccessHandler(drawChart).RetrieveData();
google.script.host.editor.focus();
}
function drawChart(stored) {
//This apparently shows a log of the object
//console.log(stored);
var data = new google.visualization.arrayToDataTable(stored);
console.log(data);
var options = {'title':'Income',
'width':400,
'height':300,
'is3d':true};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
chart.draw(data, options);
google.script.run.withSuccessHandler(drawChart).RetrieveData();
}
One last thing I tried was to call the
SpreadsheetApp.getUi().showModalDialog(html, "Title") function one more time, but without calling the html file all over again, and creating an endless loop, I don't seem to have a way to do that. Any idea how to accomplish this?
Here's a simple example of picking a range with a modeless dialog. With just a few extra features thrown in for good measure.
Code.gs:
function selRange()//run this to get everything started. A dialog will be displayed that instructs you to select a range.
{
var output=HtmlService.createHtmlOutputFromFile('pickRange').setWidth(300).setHeight(200).setTitle('Select A Range');
SpreadsheetApp.getUi().showModelessDialog(output, 'Range Selector');
}
function selCurRng()
{
var sso=SpreadsheetApp.getActive();
var sh0=sso.getActiveSheet();
var rg0=sh0.getActiveRange();
var rng0A1=rg0.getA1Notation();
rg0.setBackground('#777700');
return rng0A1;
}
function clrRange(range)
{
var sso=SpreadsheetApp.getActive();
var sh0=sso.getActiveSheet();
var rg0=sh0.getRange(range);
rg0.setBackground('#ffffff');
}
pickRange.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
var grange='';
function selectRange()
{
$('#btn1').prop('disabled',true);
$('#btn2').prop('disabled',false);
google.script.run
.withSuccessHandler(setResponse)
.selCurRng();
}
function setResponse(r)
{
grange=r;
var msg='You have select the range ' + r;
$('#instr').css('display','none');
$('#rsp').text(msg);
}
function clearAndClose()
{
google.script.run.clrRange(grange);
google.script.host.close();
}
console.log('My Code');
</script>
</head>
<body>
<div id="rsp"></div>
<div id="instr">Please select your desired range.</div>
<input type="button" id="btn1" value="Range Selected" onClick="selectRange();" />
<br /><input type="button" id="btn2" value="close" onClick="clearAndClose();"; disabled="true" />
</body>
</html>
I am trying my had at the JavaScript API Tutorial that tableau provides. When I got to the filter portion I got a little lost and was hoping for some guidance. I have created a fiddle and trimmed down the code to the bare minimum:
var placeholderDiv = document.getElementById("tableauViz");
var url = "http://public.tableausoftware.com/views/WorldIndicators/GDPpercapita";
var options = {
hideTabs: true,
hideToolbar: true,
width: "800px",
height: "400px",
onFirstInteractive: function () {
workbook = viz.getWorkbook();
activeSheet = workbook.getActiveSheet();
}
};
viz = new tableauSoftware.Viz(placeholderDiv, url, options);
function filterSingleValue() {
activeSheet.applyFilterAsync(
"Region",
"The Americas",
tableauSoftware.FilterUpdateType.REPLACE);
}
When I run the consol debugger, I get the following error:
"Uncaught ReferenceError: filterSingleValue is not defined "
I am not sure what that means, but I am guessing that the filterSingleValue() function isn't actually passing any data. Is it because I don't have the first part wrapped in a function?
My goal is to just have my filter button filter on "The Americas" region. Appreciate any guidance or suggestions. Here is my fiddle
I encountered a similar issue (with filtering on marks) and was getting the same error. I worked around it by accessing the individual worksheets within the activeSheet object and running the clearSelectedMarksAsync function belonging to each worksheet. You should be able to run the applyFilterAsync in the same way. Hope that works!
filterReset = function() {
activeSheet.getWorksheets()[0].applyFilterAsync("Region", "The Americas", tableauSoftware.FilterUpdateType.REPLACE);
}
Here is a working version of what you are trying to do, save the code block as a html file an open it up in your web browser (only tested in ie11).
Note you can't call the initializeViz function until after the place holder is loaded. Also you need to ensure your global variables viz, workbook, activeSheet are global. (http://onlinehelp.tableau.com/samples/en-us/js_api/tutorial.js)
<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
// Change the region filter
function filterSingleValue(regionFilter) {
activeSheet.applyFilterAsync(
"Region",
regionFilter.value,
tableauSoftware.FilterUpdateType.REPLACE);
}
// Initialise the viz to hold the workbook
function initializeViz(){
//Get the region filter to be able to apply the filter on the initialisation of the viz
var regionFilter = document.getElementById("regionFilter");
var placeholderDiv = document.getElementById("tableauViz");
var url = "http://public.tableausoftware.com/views/WorldIndicators/GDPpercapita?Region=" + regionFilter.options[regionFilter.selectedIndex].text;
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);
}
</script>
</head>
<body>
<!-- Dropdown Menu, the value corresponds with those found in the "region" filter -->
<select id="regionFilter" onchange="filterSingleValue(this)">
<option value="Europe">Europe</option>
<option value="Middle East">Middle East</option>
<option value="The Americas">The Americas</option>
<option value="Oceania" selected="selected">Oceania</option>
<option value="Asia">Asia</option>
<option value="Africa">Africa</option>
</select>
<!-- 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>
I uploaded the following index.html file for a subdomain and it isn't properly loading. It only shows the title tag.
I want it to load when I go to xxx.myapp.com. The setup is ok on godaddy because I see the title, but the rest of the page doesn't render. Also I see the network requests on the server bit nothing...
Any thoughts?
<!DOCTYPE html>
<html>
<head>
<title>MI Testing title</title>
<!--Load the AJAX API-->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="http://www.myapp.com/JS/HelperFunctions.js"></script>
<script type="text/javascript" src="http://www.myapp.com/JS/Settings.js"></script>
<!-- zurb foundation-->
<link type="text/css" rel="stylesheet" href="http://www.myapp.com/foundation-4.3.1/CSS/foundation.css" />
<link type="text/css" rel="stylesheet" href="http://www.myapp.com/foundation-4.3.1/CSS/foundation.min.css" />
<link type="text/css" rel="stylesheet" href="http://www.myapp.com/foundation-4.3.1/CSS/normalize.css" />
<!--Local css -->
<link type="text/css" rel="stylesheet" href="http://www.myapp.com/CSS/AnalyticsIndex.css"/>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
// Load the Visualization API and the piechart package.
google.load('visualization', '1.0', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawCharts);
/*
Called when library loaded
*/
function drawCharts(){
drawDailyAverageSessionLength();
drawUsersGender();
}
/*
Draws the chart for average session length by day
*/
function drawDailyAverageSessionLength() {
//Apit to get the data from
var api = GET_AVG_SESSIONS_URL+"2013/0/0";
//Request data (using jquery/ajax)
$.getJSON(api,function(data){
//Start a days and seconds array
var days = [];
var seconds = [];
//Init google data array
var googData = new google.visualization.DataTable();
//Add X Y columns
googData.addColumn('string', 'days');
googData.addColumn('number', 'seconds');
//Init sort array
var sorted =[];
//Parse the results to get the dates
for (var key in data){
var date = new Date(key);
sorted.push(date);
}
//Sort the array
sorted.sort(sortDateArrayDescending);
//Split results
for (i=0;i<sorted.length;i++){
//Get the date object
var day = sorted[i];
//Add 1 to month
var month = day.getMonth()+1;
//Parse to string
var newKey = day.getFullYear()+'-'+month+'-'+day.getDate();
var short = month+'/'+day.getDate();
//Add date to days array
days.push(short);
//Add to integer array
seconds.push(parseInt(data[newKey]));
}
//Parse to google data
for (i=0; i<days.length;i++){
googData.addRow([days[i], seconds[i]]);
}
// Set chart options
var options = {'title':'Average session length (NOT ACCURATE since end of sessions aren\'t being tracked)',
'width':1200,
'height':400};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.LineChart(document.getElementById('averageSessionLengthChart'));
chart.draw(googData, options);
});
}
/*
Draws the chart for average session length by day
*/
function drawUsersGender() {
//Apit to get the data from
var api = GET_USERS_SEX;
//Request data (using jquery/ajax)
$.getJSON(api,function(data){
//Start a days and seconds array
var result = [['gender', 'number']];
//Iterate over the genders
for (var gender in data){
//Get the value pair and push
var entry = [gender, parseInt(data[gender])];
result.push(entry);
}
//Parse to google data
var data = google.visualization.arrayToDataTable(result);
//Display options
var options = {
title:'Gender for registered users',
'width':600,
'height':400
};
//Draw the chart
var chart = new google.visualization.PieChart(document.getElementById('genderChart'));
chart.draw(data, options);
});
}
</script>
</head>
<body>
<div class="row">
<div id = "averageSessionLengthChart" class="large-12 small-12 columns">
</div>
</div>
<div class="row">
<div id = "genderChart" class="large-12 small-12 columns">
</div>
</div>
</body>
</html>
Try a developper console from a PC outside your network to see what requests are sent (Press 'F12' on chrome or install the firebug extension on Firefox).
There should be a "Network" tab that shows what requests are made from the page.
Maybe it will help you understand what is happening.
this is my code that has geo-rss from google maps rss:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google AJAX Feed API - Simple Example</title>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("feeds", "1");
function initialize() {
var feed = new google.feeds.Feed("http://maps.google.com/maps/ms?ie=UTF8&hl=zh-CN&vps=1&jsv=259e&msa=0&output=georss&msid=109685068115364659392.00048b5b630141d82b83a");
feed.load(function(result) {
console.log(result.feed)
});
}
google.setOnLoadCallback(initialize);
</script>
</head>
<body>
<div id="feed"></div>
</body>
</html>
and i can't find the geo data on the firebug ,
so what should i do ..
thanks
updated:
the geo data(in the geo-rss) i want to get is like this:
<georss:point>
39.965015 116.362381
</georss:point>
and
<gml:LineString>
<gml:posList>
39.992191 116.417938
39.968254 116.466698
39.939568 116.451591
39.959045 116.411079
</gml:posList>
</gml:LineString>
this can not get using the google ajax api .
so what should i do ..
You need to load the XML feed instead of the JSON feed to be able to get the raw XML feed as it is. With the JSON feed, it removes all but the standard RSS and Atom tags and then translates to JSON.
Set the result format to XML as
var feed = new google.feeds.Feed("...");
feed.setResultFormat(google.feeds.Feed.XML_FORMAT);
Here is a modified load function to print XML namespaced elements such as georss:point and gml:LineString.
feed.load(function(result) {
var georssNS = "http://www.georss.org/georss";
var gmlNS = "http://www.opengis.net/gml";
var items = result.xmlDocument.getElementsByTagName("item");
for(var i = 0; i < items.length; i++) {
// get <georss:point>
var georss = google.feeds.getElementsByTagNameNS(items[i], georssNS, "point")[0];
if(georss) {
console.log(georss)
}
// get <gml:LineString>
var lineString = google.feeds.getElementsByTagNameNS(items[i], gmlNS, "LineString")[0];
if(lineString) {
console.log(lineString);
}
}
});
From an example in the Google AJAX API Code Playground that works
http://code.google.com/apis/ajax/playground/?exp=feeds#load_feed
/*
* How to load a feed via the Feeds API.
*/
google.load("feeds", "1");
// Our callback function, for when a feed is loaded.
function feedLoaded(result) {
if (!result.error) {
// Grab the container we will put the results into
var container = document.getElementById("content");
container.innerHTML = '';
// Loop through the feeds, putting the titles onto the page.
// Check out the result object for a list of properties returned in each entry.
// http://code.google.com/apis/ajaxfeeds/documentation/reference.html#JSON
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var div = document.createElement("div");
div.appendChild(document.createTextNode(entry.title));
container.appendChild(div);
}
}
}
function OnLoad() {
// Create a feed instance that will grab Digg's feed.
var feed = new google.feeds.Feed("http://maps.google.com/maps/ms?ie=UTF8&hl=zh-CN&vps=1&jsv=259e&msa=0&output=georss&msid=109685068115364659392.00048b5b630141d82b83a");
// Calling load sends the request off. It requires a callback function.
feed.load(feedLoaded);
}
google.setOnLoadCallback(OnLoad);