I am relatively new to google charts and was trying to run a basic chart for demo and further development.
Its a pretty basic script and was working well till yesterday and now it doesnt load anything.
I am loading this script in script editor webpart of Sharepoint and trying to load it. Not sure if its my case or just that Google Charts has a problem.
Pl help. Am I missing something conceptual here? Its a pretty basic code that I got from http://www.evoketechnologies.com/blog/visualizing-sharepoint-google-charts/ and modified it a bit for my use.
I have run window.alert and it is extracting all values in the enumerator correctly. I think then something happens and the chart doesnt load.
javascript alerts also pop up after the barChart.draw(data, options), and lineChart.draw(data, options) code part, so the code has execute fully.
Thank you
Niraj
*<html>
<head>
<script src="https://www.gstatic.com/charts/loader.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices.min.js" type="text/javascript"></script>
<script language="javascript">
var returnedItems = null;
function loadGoogleLibAndDraw(){
google.charts.load('current', {'packages':['bar','line']});
google.charts.setOnLoadCallback(visualizeData);
}
function visualizeData() {
var context = new SP.ClientContext();
var list = context.get_web().get_lists().getByTitle(document.getElementById('customListName').value);
var caml = new SP.CamlQuery();
caml.set_viewXml("<View></View>");
returnedItems = list.getItems(caml);
context.load(returnedItems);
context.executeQueryAsync(onSucceededCallback, onFailedCallback);
}
function onSucceededCallback(sender, args) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Part No');
data.addColumn('number', 'Volume');
var enumerator = returnedItems.getEnumerator();
var markup = '';
while (enumerator.moveNext()) {
var row = [];
var listItem = enumerator.get_current();
row.push(listItem.get_item('Part_x0020_No'));
row.push(listItem.get_item('Volume'));
data.addRow(row);
}
var options = {
chart: {
title: 'KPIs',
},
bars: 'vertical'
};
var barChart = new google.charts.Bar(document.getElementById('BarChart'));
barChart.draw(data, options);
var lineChart = new google.charts.Line(document.getElementById('LineChart'));
lineChart.draw(data, options);
}
function onFailedCallback(sender, args) {
var markup = '<p>The request failed: <br>';
markup += 'Message: ' + args.get_message() + '<br>';
displayDiv.innerHTML = markup;
}
</script>
</head>
<body onload="loadGoogleLibAndDraw()">
<form name="metricsform" id="metricsform">
<input id="customListName" name="customListName" value="Projects" type="hidden"/>
</form>
<div>
<div id="displayDiv"></div>
<div id="BarChart" style="width: 300px; height: 200px;"></div>
<div id="LineChart" style="width: 300px; height: 200px;"></div>
</div>
</body>
</html>*
recommend not using inline tag events --> <body onload="...
especially if there are multiple editor web parts / <body> tags
also, it isn't necessary since the loader...
will wait for the document to finish loading before calling the callback
to be absolute sure, place all <script> tags at the bottom, just before the </body> end tag
recommend following setup...
<html>
<body>
<form name="metricsform" id="metricsform">
<input id="customListName" name="customListName" value="Projects" type="hidden"/>
</form>
<div>
<div id="displayDiv"></div>
<div id="BarChart" style="width: 300px; height: 200px;"></div>
<div id="LineChart" style="width: 300px; height: 200px;"></div>
</div>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2014.02/jquery.SPServices.min.js"></script>
<script>
var returnedItems = null;
function visualizeData() {
var context = new SP.ClientContext();
var list = context.get_web().get_lists().getByTitle(document.getElementById('customListName').value);
var caml = new SP.CamlQuery();
caml.set_viewXml("<View></View>");
returnedItems = list.getItems(caml);
context.load(returnedItems);
context.executeQueryAsync(onSucceededCallback, onFailedCallback);
}
function onSucceededCallback(sender, args) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Part No');
data.addColumn('number', 'Volume');
var enumerator = returnedItems.getEnumerator();
var markup = '';
while (enumerator.moveNext()) {
var row = [];
var listItem = enumerator.get_current();
row.push(listItem.get_item('Part_x0020_No'));
row.push(listItem.get_item('Volume'));
data.addRow(row);
}
var options = {
chart: {
title: 'KPIs',
},
bars: 'vertical'
};
var barChart = new google.charts.Bar(document.getElementById('BarChart'));
barChart.draw(data, options);
var lineChart = new google.charts.Line(document.getElementById('LineChart'));
lineChart.draw(data, options);
}
function onFailedCallback(sender, args) {
var markup = '<p>The request failed: <br>';
markup += 'Message: ' + args.get_message() + '<br>';
displayDiv.innerHTML = markup;
}
google.charts.load('current', {
callback: visualizeData,
packages: ['bar', 'line']
});
</script>
</body>
</html>
Related
I am currently querying a table from Google sheet which has a Date column. The date column in my dashboard has time info included, which I want to remove; also the starting date in my code is 12/18/2018 but my dashboard starts with one day earlier. 12/17/2018 16.00
My Data source looks like this:
My Dashboard looks like this:
My Code Looks like this.
Code.gs:
function doGet(e) {
return HtmlService
.createTemplateFromFile("Line Chart multiple Table")
.evaluate()
.setTitle("Google Spreadsheet Chart")
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getSpreadsheetData() {
var ssID = "1jxWPxxmLHP-eUcVyKAdf5pSMW6_KtBtxZO7s15eAUag";
var sheet = SpreadsheetApp.openById(ssID).getSheets()[1];
var data1 = sheet.getRange('A2:F9').getValues();
var data2 = sheet.getRange('A2:F9').getValues();
var rows = {data1: data1, data2: data2};
var r = JSON.stringify(rows);
return r;
}
Line Chart multiple Table.html
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<div id="linechartweekly"></div>
<div id="table2"></div>
<div class = "block" id="message" style="color:red;">
<script>
google.charts.load('current', {'packages':['table']});
google.charts.load('current', {packages: ['corechart', 'line']});
google.charts.setOnLoadCallback(getSpreadsheetData);
function display_msg(msg) {
console.log("display_msg():"+msg);
document.getElementById("message").style.display = "block"; // Style of display
var div = document.getElementById('message');
div.innerHTML = msg;
}
function getSpreadsheetData() {
google.script.run.withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(r) {
// Parse back to an object
var rows = JSON.parse(r);
console.log("rows:"+rows);
var data1 = google.visualization.arrayToDataTable(rows.data1, false);
var data2 = google.visualization.arrayToDataTable(rows.data2, false);
var options1 = {
title: 'SPC Chart weekly',
legend: ['USL', 'UCL', 'Data', 'LCL', 'LSL'],
colors: ['Red', 'Orange', 'blue', 'Orange', 'Red'],
pointSize: 4,
};
var chart1 = new google.visualization.LineChart(document.getElementById("linechartweekly"));
chart1.draw(data1, options1);
var table2 = new google.visualization.Table(document.getElementById("table2"));
table2.draw(data2, {showRowNumber: false, width: '50%', height: '100%'});
}
function failure_callback(error) {
display_msg("ERROR: " + error.message);
console.log('failure_callback() entered. WTF'+error.message);
}
</script>
</body>
</html>
May I know how to change my date to the right format removing the time and also ensure the correct starting date
Any help is much appreciated.
The actual problem has me stumped, but I do have a workaround; see modified code example below, with some additional error handling.
I've extensively tested the server-side function, and from its perspective there is absolutely no difference in the row object that is created whether the range starts at column 'I' or 'J'.
The problem manifests itself in the client-side success handler which, when column 'I' is included is essentially passed a null argument, note the whole object, not just the row.data1 part, is null.
The row object that is being passed from the server to the client is quite complicated (an object with 3 key value pairs, where the values are fairly long arrays). I can't see anything in the GAS documentation that disallows this: Legal parameters and return values are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects and arrays. So this could be a bug?
The workaround, illustrated in the code examples below is to stringify the object in the server-side function, and then parsing it back to an object in the client.
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<div id="table1"></div>
<div id="linechartweekly"></div>
<div id="table2"></div>
<div class = "block" id="message" style="color:red;">
<script>
google.charts.load('current', {'packages':['table']});
google.charts.load('current', {packages: ['corechart', 'line']});
google.charts.setOnLoadCallback(getSpreadsheetData);
function display_msg(msg) {
console.log("display_msg():"+msg);
document.getElementById("message").style.display = "block"; // Style of display
var div = document.getElementById('message');
div.innerHTML = msg;
}
function getSpreadsheetData() {
google.script.run.withFailureHandler(failure_callback).withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(r) {
// Parse back to an object
var rows = JSON.parse(r);
console.log("rows:"+rows);
var data1 = google.visualization.arrayToDataTable(rows.data1, false);
var data2 = google.visualization.arrayToDataTable(rows.data2, false);
var data3 = google.visualization.arrayToDataTable(rows.data3, false);
var options1 = {
title: 'SPC Chart weekly',
legend: ['USL', 'UCL', 'Data', 'LCL', 'LSL'],
colors: ['Red', 'Orange', 'blue', 'Orange', 'Red'],
pointSize: 4,
};
var table1 = new google.visualization.Table(document.getElementById("table1"));
table1.draw(data1, {showRowNumber: false, width: '50%', height: '100%'});
var chart1 = new google.visualization.LineChart(document.getElementById("linechartweekly"));
chart1.draw(data2, options1);
var table2 = new google.visualization.Table(document.getElementById("table2"));
table2.draw(data3, {showRowNumber: false, width: '50%', height: '100%'});
}
function failure_callback(error) {
display_msg("ERROR: " + error.message);
console.log('failure_callback() entered. WTF'+error.message);
}
</script>
</body>
</html>
Code
function doGet(e) {
return HtmlService
.createTemplateFromFile("Line Chart multiple Table")
.evaluate()
.setTitle("Google Spreadsheet Chart")
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getSpreadsheetData() {
var ssID = "1jxWPxxmLHP-eUcVyKAdf5pSMW6_KtBtxZO7s15eAUag";
var sheet = SpreadsheetApp.openById(ssID).getSheets()[0];
//var firstrow = 6; //11th row
//var range = sheet.getRange(firstrow, 1, sheet.getLastRow() - firstrow + 1, 6);
//var data1 = range.getValues();
var d1 = sheet.getRange('A1:B5').getValues();
var d2 = sheet.getRange('I2:O4').getValues();
var d3 = sheet.getRange('I2:O4').getValues();
var rows = {data1: d1, data2: d2, data3: d3};
// Stringify the object
var r = JSON.stringify(rows);
return r;
}
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>
When I press the button named calendar I am directed to the controller where it is called the main view which contains two DatePickers. After I select two dates and click on the submit button , I am directed in the controller in the method called "Plotting" that would open a new view that display a chart .My question is , can i display the chart view to be seen under the main view with the two DatePickers because if i want to select other dates to not have to press the back button?
Here is my code from the controller which leads me to the main view :
public ActionResult Calendar()
{
ChartDate objdatemodel = new ChartDate();
return View("Calendar",objdatemodel);
}
Here is the code for the main view named Calendar
#model Plotting.Models.ChartDate
#{
ViewBag.Title = "jQuery UI Datepicker Calender Control In Asp.Net Mvc Application";
}
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script src="http://code.jquery.com/ui/1.11.0/jquery-ui.js"></script>
<h2>
Datepicker Calender
</h2>
<div id="yourviewDiv"></div>
#using (Html.BeginForm("Plotting", "Greenhouse"))
{
<table>
<tr>
<td>
<p>
Start Date: #Html.TextBoxFor(m => m.StartDate, new { #id = "txtdatepicker1", #style = "width:200px;" })
</p>
</td>
<td>
<p>
End Date: #Html.TextBoxFor(m => m.EndDate, new { #id = "txtdatepicker2", #style = "width:200px;" })
</p>
</td>
</tr>
</table>
<input type="submit" value="Submit" />
}
<script>
$(function () {
$("#txtdatepicker1").datepicker();
});
$(function () {
$("#txtdatepicker2").datepicker();
});
</script>
Here is the code from the controller for the view plotting:
public ActionResult Plotting(ChartDate objdatemodel)
{
ChartDate cDate = new ChartDate();
cDate.DateData = new DateChart();
cDate.DateTitle = "Day";
cDate.HumidityTitle1 = "Senzor 1";
cDate.HumidityTitlle2 = "Senzor 2";
List<Greenhouse> greenhouse = dal.FindDatesByInterval(objdatemodel.StartDate , objdatemodel.EndDate);
String day = "";
String humidity1 = "";
String humidity2 = "";
for (int i = 0; i < greenhouse.Count - 1; i++)
{
day += greenhouse[i].DateTime.Day + ",";
humidity1 += greenhouse[i].Humidity1 + ",";
humidity2 += greenhouse[i].Humidity2 + ",";
}
day += greenhouse[greenhouse.Count - 1].DateTime.Day;
humidity1 += greenhouse[greenhouse.Count - 1].Humidity1;
humidity2 += greenhouse[greenhouse.Count - 1].Humidity2;
DateChart obj = new DateChart();
/*Get the data from databse and prepare the chart record data in string form.*/
obj.Date = day;
obj.Humidity1 = humidity1;
obj.Humidity2 = humidity2;
cDate.DateData = obj;
return View("Plotting",cDate);
}
And here is the code for the Plotting view :
#model Plotting.Models.ChartDate
#{
ViewBag.Title = "How To Create Dynamic Google Column Chart In an Asp.Net MVC Application Using C# ";
}
<fieldset>
<legend><strong>Greenhouse</strong></legend>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(drawChart);
function drawChart() {
// Create and populate the data table.
var days = [#Model.DateData.Date];
var humidities1 = [#Model.DateData.Humidity1];
var humidities2 = [#Model.DateData.Humidity2];
var data = new google.visualization.DataTable();
data.addColumn('string', '#Model.DateTitle');
data.addColumn('number', '#Model.HumidityTitle1');
data.addColumn('number', '#Model.HumidityTitlle2');
for (i = 0; i < days.length; i++) {
data.addRow([days[i].toString(), humidities1[i], humidities2[i]]);
}
var options = {
title: 'Humidities Values',
hAxis: { title: '#Model.DateTitle', titleTextStyle: { color: 'black' } }
};
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
<div id="chart_div" style="width: auto; height: auto;">
</div>
</fieldset>
Currently the code works , but if I want to put another chart I have to press the back button . You can put the two sites view the same view ? Thank you .
Below is my view. I have modified it to display how I would like it to work. I have a graph that is drawn when the page is loaded. My model contains the data it needs. I have to transform it to JSON (right?) and then pass it into the generateGraph, but the setOnLoadCallBack is my problem. I can't figure out how to create the JSON string before the setOnLoadCallBack is called.
#model IEnumerable<FHWebUserInterface.Models.Weight>
<div id="visualization" style="width: 600px; height: 400px;"></div>
<div id="linechart_material"></div>
#{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var weightsAsJsonString = serializer.Serialize(Enumerable.Select(Model, weight =>
new
{
date = weight.Date,
value = weight.Value
}));
}
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load('visualization', '1.1', { packages: ['corechart', 'imagelinechart'] });
</script>
<script type="text/javascript">
function drawVisualization(weightsAsJsonString) {
// Removed the body
chart.draw(data, options);
}
google.setOnLoadCallback(drawVisualization(weightsAsJsonString));
</script>
You just need to render your json somewhere in JavaScript block like this (i've added it before google.setOnLoadCallback call):
#model IEnumerable<FHWebUserInterface.Models.Weight>
<div id="visualization" style="width: 600px; height: 400px;"></div>
<div id="linechart_material"></div>
#{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var weightsAsJsonString = serializer.Serialize(Enumerable.Select(Model, weight =>
new
{
date = weight.Date,
value = weight.Value
}));
}
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load('visualization', '1.1', { packages: ['corechart', 'imagelinechart'] });
</script>
<script type="text/javascript">
function drawVisualization(weightsAsJsonString) {
// Removed the body
chart.draw(data, options);
}
// here is my change
var weightsAsJsonString = #weightsAsJsonString; // so it will render your C# object from server side here on client side
google.setOnLoadCallback(drawVisualization(weightsAsJsonString));
</script>
I'm trying increase the number of items that appear on this webpage using the Google Feed API to 25 instead of 4 items:
http://ewh.ieee.org/reg/1/sac/news.php
Here is the JS:
<script src="//www.google.com/jsapi?key=AIzaSyA5m1Nc8ws2BbmPRwKu5gFradvD_hgq6G0" type="text/javascript"></script>
<script type="text/javascript">
google.load("feeds", "1");
function feedLoaded(result) {
if (!result.error) {
var container = document.getElementById("content");
container.innerHTML = '';
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(i + ': ' + entry.title));
container.appendChild(div);
}
}
}
function OnLoad() {
var feedControl = new google.feeds.FeedControl();
feedControl.addFeed("http://feeds.feedburner.com/IeeeSpectrum", "Latest IEEE Spectrum News:");
feedControl.draw(document.getElementById("content"));
feed.includeHistoricalEntries(); // tell the API we want to have old entries too
feed.setNumEntries(25); // we want a maximum of 25 entries, if they exist
feed.load(feedLoaded);
}
google.setOnLoadCallback(OnLoad);
</script>
And the HTML:
<body style="font-family: Arial;border: 0 none;">
<div id="content">Loading IEEE Spectrum News...</div>
</body>
I tried using feed.setNumEntries(), but it doesn't seem to work. I appreciate any help. Thank you.
I found the solution to my problem:
<script type="text/javascript">
google.load("feeds", "1");
function initialize() {
var control = new google.feeds.FeedControl();
control.setNumEntries(25);
control.addFeed("http://feeds.feedburner.com/IeeeSpectrum", "Latest IEEE Spectrum News:");
control.draw(document.getElementById("content"));
}
google.setOnLoadCallback(initialize);
</script>