I have been working on a front end to my App Engine app kind of based on this codelab. I have a text box where you enter in a stock ticker symbol (like say, AMZN or GOOG), which it uses as criteria to run a query to Google BigQuery in the background and then it's supposed display the tweet count over several days in a Google Visualization API line chart.
But, based on what I've seen in the source code from the page, it's not pulling the query results from BigQuery into the data template variable {{ data }}. Here's my HTML code (called index1.html), which for the most part is like the codelab's:
<!--
You are free to copy and use this sample in accordance with the terms of the
Apache license (http://www.apache.org/licenses/LICENSE-2.0.html)
-->
<!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>
Jibdan Analytics
</title>
<script type="text/javascript" src="//www.google.com/jsapi"></script>
<script type="text/javascript">
google.load('visualization', '1', {packages: ['corechart']});
</script>
<script type="text/javascript">
countdata = {{ data }}
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.DataTable(query_response);
// Create and draw the visualization.
var chart = new google.visualization.LineChart(document.getElementById('visualization'));
chart.draw(data, {title: "Tweets by Day by Ticker",
curveType: "function",
width: 800, height: 600}
);
}
google.setOnLoadCallback(drawVisualization);
</script>
</head>
<body style="font-family: Arial;border: 0 none;">
<div id="visualization" style="width: 800px; height: 640px;"></div>
</body>
</html>
I messed with the html and Javascript after looking at the Google Code Playground code for the line chart, but really it seems like the issue is with that template variable.
Here's the pertinent Python code, too. The first method is supposed to take the BigQuery response and put it in a format that should be ingested by the Visualization API to produce a line chart. The get method has the query string to run and renders the results into the template, index1.html. Pretty much like the codelab:
class QueryHandler(webapp2.RequestHandler):
def _bq2line(self, bqdata):
logging.info(bqdata)
columnNameDate = bqdata['schema']['fields'][0]['name']
columnNameVal = bqdata['schema']['fields'][1]['name']
logging.info("Column Names=%s, %s" % (columnNameDate, columnNameVal))
countdata = { 'cols': ({'id':columnNameDate, 'label':columnNameDate, 'type':'string'},
{'id':columnNameVal, 'label':columnNameVal, 'type':'number'})}
countdata['rows'] = [];
logging.info(countdata)
for row in bqdata['rows']:
newrow = ({'c':[]})
newrow['c'].append({'v': row['f'][0]['v']})
newrow['c'].append({'v':row['f'][1]['v']})
countdata['rows'].append(newrow)
logging.info('FINAL COUNTDATA---')
logging.info(countdata)
self.response.out.write(countdata)
return json.dumps(countdata)
def get(self):
QUERY = """
SELECT STRFTIME_UTC_USEC(querydate, "%Y-%m-%d") AS tweet_date, COUNT(tweet_id) AS tweet_count
FROM [jibdantweetstream.tweetdata_09_21_2013]
WHERE gcs_load = true AND (REGEXP_MATCH(ticker, '""" + self.request.get('stock') + """'))
GROUP BY tweet_date
ORDER BY tweet_date
"""
try:
query_request = bigquery_service.jobs()
query = {'data': self._bq2line(bq.Query(QUERY, BILLING_ID)),
'query': QUERY}
query_response = query_request.query(projectId=BILLING_ID,
body=query).execute()
template = os.path.join(os.path.dirname(__file__), 'result1.html')
self.response.out.write(render(template, query_response))
finally:
self.response.out.write('Click here to go back to the Search page. ')
So, that's what I have. You'll see I have a couple of self.response.out.write statements in there, because I wanted to see if I was getting a response back with query results. I am getting results, so I know it's not an OAuth2 issue. I just don't know what else it could be.
Many Thanks in Advance.
"View Source", coupled with something (Firebug, Chrome Developer Tools) that'll tell you about syntax errors in your JavaScript are your friends. Looking at what you've provided, I'm guessing that expanding {{ data }} will result in a syntax error. And as asgallant noted, you're setting up countdata but referencing query_response instead.
Since your data is a date and a count, you're not going to run into entity escaping issues, but if you expand the query to include something else, you'll run into the problem of {{ data }} being escaped for HTML, where it needs to be escaping for JS. To handle that, you'll need the escapejs filter.
In your code:
countdata = { 'cols': ({'id':columnNameDate, 'label':columnNameDate, 'type':'string'},
{'id':columnNameVal, 'label':columnNameVal, 'type':'number'})}
It should be:
countdata = { 'cols': [{'id':columnNameDate, 'label':columnNameDate, 'type':'string'},
{'id':columnNameVal, 'label':columnNameVal, 'type':'number'}]}
Related
I'm using Google Charts API inside my C# project (using the WebBrowser from CefSharp), and it works with the data hard coded to it, but I'm running into a problem whenever I try to dinamically populate it using data.addRows(). I need to have something simple, like a external csv/json, so it's possible to run inside C# (WebBrowser is really limited and sometimes buggy), but every solution tells me to do that via php server or something more "complex" like that. So, is there a way to populate that chart just using JavaScript and an external file (or something different but viable)?
Thats the code, if useful:
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {packages:["orgchart"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('string', 'Manager');
data.addColumn('string', 'ToolTip');
// For each orgchart box, provide the name, manager, and tooltip to show.
data.addRows([['Alice', 'Mike', ''],['Bob', 'Jim', 'Bob Sponge'],['Carol', 'Bob', '']]);
// Create the chart.
var chart = new google.visualization.OrgChart(document.getElementById('chart_div'));
// Draw the chart, setting the allowHtml option to true for the tooltips.
chart.draw(data, {'allowHtml':true});
}
</script>
</head>
<body>
<div id="chart_div"></div>
</body>
</html>
Obs: CefSharp WebBrowser just calls this code above as a normal HTML file and runs it inside the C# application.
Thank you!
I've done something similar, but I've been using the arrayToDataTable() method of the google charts API, but I'm sure this can be done similarly. You will need to lean on C# though. I wanted to be thorough so this is a big one. More than happy to clarify because I'm sure that at least some of this will be poorly worded/confusing.
TLDR
Create a Class to hold your data points and a way to represent them as a list.
In Your Controller class, Create a method that returns a list containing your data points represented as lists. Turn that List of lists into JSON and Post it to a URL endpoint
Read your json data from that endpoint using ajax in the cshtml
Put the read json into your AddRows method.
Party
This will touch on : the .cshtml, the Home Controller, and Wherever you are pulling your data.
Wherever you are pulling your Data
My case is a bit complicated. But basically I have a method that just returns a list of type object that holds these objects, represented as lists. (eg. [<Timestamp>,<Temperature>,<Humidity>])
public class WeatherEntry
{
public string Timestamp { get; set; }
public double TEMPERATURE { get; set; }
public double Humidity { get; set; }
... Other stuff ...
}
If you can generate a list of data points, represented as lists on the c# side, you are in business.
The Controller class
Assuming you are using ASP.net MVC, you'll have a backing controller class that holds your Controlling C#
Once you have a list of type object containing your data points represented as lists, you can make it into JSON pretty easily using Newtonsoft.Json's JSonConvert.SerializeObject() Method as such:
public ActionResult GetChartData_All_Week()
{
var dataPointsList = this.myDataSource.GetMyDataPointList();
var convertedJson = JsonConvert.SerializeObject(dataPointsList, new JsonSerializerSettings()
{
//Not Needed, I just like to throw out any values that are null.
NullValueHandling = NullValueHandling.Ignore
});
return Content(convertedJson);
IMPORTANT We'll be using ajax in the next step, so we need the magical [Route] Attribute above this action result method as such:
Route("Index/Path/To/Post/To")]
public ActionResult GetChartData_All_Week()
All the hard stuff is done now. If you launch your App and visit the route you defined above (For me it is /Index/Charts/All/Week) You should see your JSON data there similar to this:
[["07/04/2020",25.5,44.0],["07/05/2020",25.600000381469727,45.0],["07/06/2020",25.5,44.0],...["07/08/2020",25.5,43.0]]
If You don't then this next part isn't going to work out. Always remember that it is a List of Lists we will need!
The .cshtml, AJAX, and jQuery magic
Most of your chart is already there. If It's working with the sample data as you posted, this will be super ez. inside your drawChart() method, you'll add the following before the data.AddRows() call.
var jsonData = $.ajax({
url:'Index/Path/To/Post/To',
dataType:"json",
async:false,
type:"GET"
}).responseText;
var asJson = JSON.parse(jsonData);
data.AddRows(asJson);
And now you should have it! The page and datasource will require a refresh to chart any new data that is added, but this should give you a dynamically sized list of points. The hardest part that I had was formatting my data as a List of Lists. I would recommend adding a CS List<object> ToList() method to whatever your equivalent to my Weather_Entry class is to put all of the important Data points into a list in order*. That makes it nice and easy to do something like:
public List<object> GetMyDataPointList(){
var myDataPointList = new List<MyDataPointType>();
myDataPointList = this.GetMyListOfDataPoints();
List<object> turnMeIntoJSON = new List<object>();
myDataPointList.ForEach(dp => turnMeIntoJSON.Add(dp.ToList));
return turnMeIntoJSON;
}
Best of Luck!
I hope the title of my question is descriptive and helpful for you to understand the issue that I am facing. I am new to programming, and I am aware that the issue I am facing is something that only a beginner would have issues with. Please do help me out. Please bear with me as this is quite a long description. I am aware that most of you who are a part of this community are very experienced programmers and wouldn't require such detailed methodology but it isn't my intention to waste your time and I believe that by giving such a detailed description, you would be able to help me better. Now about the issue, I am trying to build a grid using jQuery:
https://www.jqwidgets.com/jquery-widgets-demo/demos/jqxgrid/index.htm#demos/jqxgrid/defaultfunctionality.htm
I have used the source code from the link above to build the gird, but when I run the program, the data doesn't get displayed. I am sure that the issue lies in the jQuery because I have run my web service separately and it connects to SQL Server and displays the output in the form of a JSON array.
I have broken the solution into three projects on Visual Studio 2019:
PracticeValidation project - contains 3 .aspx c# web forms. One for the homepage, another for the recipe form and a third for the employee form.
WebServicesFunctionality project - Contains one .asmx Webservice file which holds 2 web methods(one for the recipe form, the other for the employee form) to serialise the data coming in the form of a list into a JSON array. Please find the code for the web service attached below.
[System.Web.Script.Services.ScriptService]
public class WebService1 : System.Web.Services.WebService
{
[WebMethod]
public string GetRecipe()
{
JavaScriptSerializer js = new JavaScriptSerializer();
string recipeList = String.Empty;
List<FormGeneratorClass.FormGeneratorVar.RecipeVar> recipeCatcher = new List<FormGeneratorClass.FormGeneratorVar.RecipeVar>();
recipeCatcher = FormGeneratorClass.FormGeneratorVar.ExecuteRecipeList();
if (recipeCatcher != null && recipeCatcher.Count > 0)
{
recipeList = js.Serialize(recipeCatcher);
}
else
recipeList = js.Serialize("No recipes!");
return recipeList;
}
[WebMethod]
public string GetEmp()
{
JavaScriptSerializer js = new JavaScriptSerializer();
string EmployeeList = String.Empty;
List<FormGeneratorClass.FormGeneratorVar.EmpVar> employeeCatcher = new List<FormGeneratorClass.FormGeneratorVar.EmpVar>();
employeeCatcher = FormGeneratorClass.FormGeneratorVar.ExecuteEmployeeList();
if (employeeCatcher != null && employeeCatcher.Count > 0)
{
EmployeeList = js.Serialize(employeeCatcher);
}
else
EmployeeList = js.Serialize("No recipes!");
return EmployeeList;
}
}
FormGeneratorClass project: This project holds a c# class file which is responsible for interacting with SQL Server. I am attaching the code inside this file below.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FormGeneratorClass
{
public class FormGeneratorVar
{
public class RecipeVar
{
public int Recipe_Id { get; set; }
public string Recipe_Name { get; set; }
}
public class EmpVar
{
public int Emp_Id { get; set; }
public string Emp_FirstName { get; set; }
public string Emp_LastName { get; set; }
}
public static List<RecipeVar> ExecuteRecipeList()
{
List<RecipeVar> listRecipe = new List<RecipeVar>();
string strConnString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(strConnString))
{
string sqlSelectAllQuery = "SELECT * FROM Tbl_Recipe";
SqlCommand cmd = new SqlCommand(sqlSelectAllQuery, con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
RecipeVar recipe = new RecipeVar();
recipe.Recipe_Id = !(rdr["recipe_id"] == DBNull.Value) ? Convert.ToInt32(rdr["recipe_id"]) : 0;
recipe.Recipe_Name = !(rdr["recipe_name"] == DBNull.Value) ? Convert.ToString(rdr["recipe_name"]) : string.Empty;
listRecipe.Add(recipe);
}
con.Close();
}
return listRecipe;
}
public static List<EmpVar> ExecuteEmployeeList()
{
List<EmpVar> listEmployee = new List<EmpVar>();
string strConnString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using (SqlConnection con = new SqlConnection(strConnString))
{
string sqlSelectAllQuery = "SELECT * FROM Tbl_Emp";
SqlCommand cmd = new SqlCommand(sqlSelectAllQuery, con);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
EmpVar employee = new EmpVar();
employee.Emp_Id = !(rdr["emp_id"] == DBNull.Value) ? Convert.ToInt32(rdr["emp_id"]) : 0;
employee.Emp_FirstName = !(rdr["emp_firstName"] == DBNull.Value) ? Convert.ToString(rdr["emp_firstName"]) : string.Empty;
employee.Emp_LastName = !(rdr["emp_lastName"] == DBNull.Value) ? Convert.ToString(rdr["emp_lastName"]) : string.Empty;
listEmployee.Add(employee);
}
con.Close();
}
return listEmployee;
}
}
}
I will set the WebServicesFunctionality project(pt.2) as the startup project and take a screenshot of the result I get for your reference
The web service is loaded on my local browser
The output after the employee web method gets invoked
The output after the recipe web method gets invoked
Now I'm sure all those reading this post will have a clearer idea about what I'm trying to do. So now I'll attach the code for employee .aspx page.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="EmployeeRecord.aspx.cs" Inherits="PracticeValidation.WebForm2" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Record of employees</title>
<meta name="description" content="JavaScript Grid with rich support for Data Filtering, Paging, Editing, Sorting and Grouping" />
<link href="Scripts/jqx.base.css" rel="stylesheet" type="text/css"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1 minimum-scale=1" />
<script src="Scripts/jquery-1.11.1.min.js"></script>
<script src="Scripts/jqxcore.js"></script>
<script src="Scripts/jqxdata.js"></script>
<script src="Scripts/jqxbuttons.js"></script>
<script src="Scripts/jqxscrollbar.js"></script>
<script src="Scripts/Menu.js"></script>
<script src="Scripts/jqxcheckbox.js"></script>
<script src="Scripts/jqxlistbox.js"></script>
<script src="Scripts/jqxdropdownlist.js"></script>
<script src="Scripts/jqxgrid.js"></script>
<script src="Scripts/jqxgrid.sort.js"></script>
<script src="Scripts/jqxgrid.pager.js"></script>
<script src="Scripts/jqxgrid.selection.js"></script>
<script src="Scripts/jqxgrid.edit.js"></script>
<script src="Scripts/demos.js"></script>
<script type="text/javascript">
$(document).ready(function () {
//Getting the source data with ajax GET request
source = {
datatype: "json",
datafields: [
{ name: 'EmpID' },
{ name: 'EmpLastName' },
{ name: 'EmpFirstName' }
],
async: false,
record: 'Table',
url: 'WebService1.asmx/GetEmp',
};
var dataAdapter = new $.jqx.dataAdapter(source,
{ contentType: 'application/json; charset=utf-8' }
);
$("#grid").jqxGrid({
source: dataAdapter,
theme: 'classic',
width: '100%',
columns: [
{ text: 'EmpID', dataField: 'EmpID', width: 250, hidden: false },
{ text: 'EmpLastName', dataField: 'EmpLastName', width: 150 },
{ text: 'EmpFirstName', dataField: 'EmpFirstName', width: 180 },
]
});
});
</script>
</head>
<body class ='default'>
<form id="form1" runat="server">
<div>
<h1>Welcome to the record of employees page</h1>
<h4>Click here to go back to the main login page</h4>
</div>
<div id="grid">
</div>
</form>
</body>
</html>
I will finally attach the screenshot of the output that I get.
Output of the employee record .aspx page:
Thank you to all those who have read the whole post and stay safe!
I figured out where I was going wrong. There were a lot of reasons why the data wasn't getting bound to the grid the way I wanted. I hope this is helpful for anybody else with the same issue.
The first problem you need to solve if you're creating a solution the way I did is that you need to create a proxy web service that gets the data from the web service. This is because, the web service project and the web form project run on a different port. So, even if everything is right, you would get a 404 error when making an AJAX request. So create a web service proxy from the web form project or else just ditch the idea of creating a separate web service project.
The next problem I ran into was a doozy and took me a really long time to figure out. Irrespective of serializing the data coming from SQL into JSON, the data that actually gets sent back from the web service is XML. This is because web services use SOAP and hence by default the method of data transfer is XML. If you run your web service and observe the data, you will see a JSON string with an XML wrapper. If you open Chrome dev tools and look at the content-type, you would see that its returning XML and no matter what you do, you cannot stop a web service from returning XML through an AJAX 'GET' request. So how do you solve the problem? Two ways:
Method 1: Use a Web API instead of a Web service. This will allow you to use REST services.
Method 2: If you're adamant about using a web service, then the following link will be super helpful.
https://www.one-tab.com/page/IjhbYbayRlWka-8ruNI87w
After I got my AJAX returning JSON from a web service successfully. The next issue was getting the data bound to the grid plug in. This is pretty simple and straightforward. Find the demo of the method you want to use and copy paste the entire thing into the success call back function of the AJAX request. You could even pass the data you are receiving by calling a user defined function INSIDE the success call back function of the AJAX request.
Sometimes you might run into an issue when consuming a web service using an AJAX request which says that "there was an error during the serializing or deserializing. Maximum JSON length property exceeded". If you do face this try initializing the javascriptSerializer object to a maxInt value.
If you are using Newtonsoft .JSON, then please check to see if your data gets serialized using the regular javascriptSerializer class. The reason why I say this is because AJAX requests serialize data using javascriptSerializer and Newtonsoft .JSON tends to overlook circular reference errors. As a result, the Ajax function might throw a "there was an error during serializing or deserializing. Maximum JSON length property exceeded" error when in actuality your web service is running into a circular reference error. Consider making changes to the SP or query that you are using if this is the case.
I've already checked out some similar questions here: How can I pass data from Flask to JavaScript in a template?
I'm trying to create a google chart using this example , where my data is coming from a database query, which is then parsed and formatted to be passed through Google's datasource python library, then converted into a json string. From there it is supposed to be passed through a JavaScript function in my HTML file in order to populate the Google Charts table. This last part seems to be causing me trouble. Here is the meat of my function in flask:
description = {"count": ("number", "Count"),"type": ("string", "Type")}
data=[]
rows = cur.fetchall()
# formats MySQL data for JSON string
for row in rows:
js = {'type': row[0], 'count': row[1]}
data.append(js)
# Loading it into gviz_api.DataTable (from Google Charts template)
data_table = gviz_api.DataTable(description)
data_table.LoadData(data)
# Create a JSON string. (from google charts template)
json= data_table.ToJSon(columns_order=("type", "count"), order_by="count")
render_template('myhtml.html' , json=json)
at the end of my function in flask, which is then passed through the following JavaScript function in my html file here:
<script>
...
// taken from the Google Charts example
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
var json = '{{ json }}'
function drawTable(json) {
var json_table = new google.visualization.Table(document.getElementById('table_div_json'));
var json_data = new google.visualization.DataTable((json), 0.6);
json_table.draw(json_data, {showRowNumber: true});
}
The chart is not drawn and I am left with only the header for the chart. I don't believe this is an issue with the format of my data or the HTML portion (which I've left out), for it is taken straight from the Google Charts template, so I've been trying to find problems in my JavaScript (which I am fairly new to). Can this string not be passed to the JavaScript function in this fashion? What alternative ways can it be done? Any pointers and suggestions would be great.
Probably have to escape it, something like:
{{json | safe}}
I'm new to Google Apps Script so am just exploring if what I want to achieve is possible.
From a Google form, I need to retrieve and display on a separate document the chart created from data on each individual form submission. I know this can be done.
The problem I have is that the chart type I want does not seem to be available here.
The chart needs to show a category and two values. This could be done with a bar chart, height is one value and colour the other value - this looks as though it might be possible but I am not sure if the colour of the whole bar can be changed.
An alternative is the bubble chart, X axis for category, Y axis for one value and size for the other value - but this type of chart does not seem to be supported.
You can display any of the 25+ chart types provided by the Google Visualization API within the Google Apps Script HTML Service.
Below is a modified version of the Bubble Chart example. Instead of fixed data, we'll pull data from a spreadsheet. The chart will be displayed in a modal dialog, as an add-on within that spreadsheet.
The source data:
A B C D E
ID Life Expectancy Fertility Rate Region Population
CAN 80.66 1.67 North America 33739900
DEU 79.84 1.36 Europe 81902307
DNK 78.6 1.84 Europe 5523095
EGY 72.73 2.78 Middle East 79716203
GBR 80.05 2 Europe 61801570
IRN 72.49 1.7 Middle East 73137148
IRQ 68.09 4.77 Middle East 31090763
ISR 81.55 2.96 Middle East 7485600
RUS 68.6 1.54 Europe 141850000
USA 78.09 2.05 North America 307007000
Client Side
The rest of the design is pretty straight-forward, but for Apps Script programmers who aren't used to javascript use in the HTML service, especially the behaviour of asynchronous function calls & call-backs, it's what's happening in the client side code that's most interesting. Here's the basic flow.
Present html page with a placeholder for the visualization.
Load external JavaScript libraries. We'll be using jQuery (for easy manipulation of DOM) and of course Google's JavaScript API, aka jsapi, for the visualization objects.
When the page loads, request a callback. We'll call that sendQuery(), as it will retrieve our spreadsheet data. This is a different approach than the original example that simply displayed a chart, because we're not just using hard-coded data.
When the jsapi finishes loading, sendQuery() is called. It requests our data, and passes the asynchronous response to another callback, drawSeriesChart().
Once data is received by drawSeriesChart(), draw the chart.
Options for retrieving data from spreadsheet
Since our visualization will be run in a browser (aka client-side), we need to get the info from the spreadsheet (aka server-side). Depending upon your specific needs, there are a few ways to retrieve that data.
Query via visualization API.
For a published spreadsheet, this is a very flexible way to retrieve data. Your client-side js can specify the range of data you're interested in, and you can utilize the Query Language to manipulate the data you'll display without modifying the source spreadsheet.
function sendQuery() {
var opts = {sendMethod: 'auto'};
var sheetId = "--- your sheet ID ---";
var dataSourceUrl = 'https://spreadsheets.google.com/tq?key=%KEY%&pub=1'
.replace("%KEY%",sheetId);
var query = new google.visualization.Query(dataSourceUrl, opts);
// Specify query string, if desired.
// Send the query with a callback function.
query.send(drawSeriesChart);
}
Handy for situations where you don't own the source data, for example
Create a web service that will feed the spreadsheet data. This approach keeps the spreadsheet itself private.
Use direct communication between the server & client side scripts, via google.script.run. This way, the spreadsheet remains private. This example is very simple, as it gleans the entire spreadsheet, but you could extend it to manipulate your data by filtering, sorting, or adding further metadata for formatting.
function sendQuery() {
// Send the query with a callback function.
google.script.run
.withSuccessHandler(drawSeriesChart)
.getSpreadsheetData();
}
This requires that function getSpreadsheetData() be implemented on the server side to return the desired data. That's shown in the actual code that follows.
Code.gs
Other than the usual yada-yada for menu creation, this file implements a getSpreadsheetData() function that we'll use to retrieve all the data from a sheet.
/**
* Adds a custom menu with items to show the sidebar and dialog.
*
* #param {Object} e The event parameter for a simple onOpen trigger.
*/
function onOpen(e) {
SpreadsheetApp.getUi()
.createAddonMenu()
.addItem('Bubble Chart Example', 'showBubbleEx')
.addToUi();
}
/**
* Runs when the add-on is installed; calls onOpen() to ensure menu creation and
* any other initializion work is done immediately.
*
* #param {Object} e The event parameter for a simple onInstall trigger.
*/
function onInstall(e) {
onOpen(e);
}
/**
* Opens a dialog for a visualization.
*/
function showBubbleEx() {
var ui = HtmlService.createTemplateFromFile('BubbleEx')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(450)
.setHeight(350);
SpreadsheetApp.getUi().showModalDialog(ui, "Bubble Chart Example");
}
/**
* Return all data from first spreadsheet as an array. Can be used
* via google.script.run to get data without requiring publication
* of spreadsheet.
*
* Returns null if spreadsheet does not contain more than one row.
*/
function getSpreadsheetData() {
var data = SpreadsheetApp.getActive().getSheets()[0].getDataRange().getValues();
return (data.length > 1) ? data : null;
}
BubbleEx.html
This was adapted from the "Sheets add-on" template, and refers to the Stylesheet.html file included there.
<!-- Use a templated HTML printing scriptlet to import common stylesheet. -->
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').getContent(); ?>
<!-- Below is the HTML code that defines the dialog element structure. -->
<div>
<div id="series_chart_div" style="width: 400px; height: 300px;"></div>
<div class="block" id="dialog-button-bar">
<button id="dialog-cancel-button" onclick="google.script.host.close()">Cancel</button>
</div>
</div>
<!-- Use a templated HTML printing scriptlet to import JavaScript. -->
<?!= HtmlService.createHtmlOutputFromFile('BubbleExJavaScript').getContent(); ?>
BubbleExJavaScript.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script>
// Load the Visualization API and desired package(s).
google.load('visualization', '1.0', {'packages':['corechart']});
/**
* Run initializations on dialog load.
*/
$(function() {
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(sendQuery);
// Assign handler functions to dialog elements here, if needed.
// Call the server here to retrieve any information needed to build
// the dialog, if necessary.
});
/**
* Issue asynchronous request for spreadsheet data.
*/
function sendQuery() {
google.script.run
.withSuccessHandler(drawSeriesChart)
.getSpreadsheetData();
}
/**
* Callback function to generate visualization using data in response parameter.
*/
function drawSeriesChart(response) {
if (response == null) {
alert('Error: Invalid source data.')
return;
}
else {
var data = google.visualization.arrayToDataTable(response,false);
var options = {
title: 'Correlation between life expectancy, fertility rate and population of some world countries (2010)',
hAxis: {title: data.getColumnLabel(1)}, // 'Life Expectancy'
vAxis: {title: data.getColumnLabel(2)}, // 'Fertility Rate'
bubble: {textStyle: {fontSize: 11}}
};
var chart = new google.visualization.BubbleChart(document.getElementById('series_chart_div'));
chart.draw(data, options);
}
}
</script>
I am very new to AngularJS and want to get started retrieving data from a remote source.
My app.js file looks like this:
var app = angular.module('footy', []);
app.controller('ClubController', ['$http', function($http){
var club = this;
club.team = [];
$http.get('http://api.football-data.org/teams/19').success(function(data){
club.team = data;
});
}]);
and my html looks like this:
<!doctype html>
<html ng-app="footy">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller = "ClubController as data">
{{data.club.team.name}}
</div>
</body>
</html>
When I view the page in a browser all that is returned is a blank page and in the source I can see "{{data.club.team.name}}" between the div.
When I view the data source Url in a browser I see this:
{
"id": 19,
"name": "Eintracht Frankfurt",
"shortName": "Eintr. Frankfurt",
"crestUrl": "http://upload.wikimedia.org/wikipedia/commons/0/04/Eintracht_Frankfurt_Logo.svg"
}
I have gone through and completed this course: http://campus.codeschool.com/courses/shaping-up-with-angular-js/ and was trying to apply the instructions from chapter 5 about services to this but I am having no luck.
Can someone help? Thanks in advance.
I see two problems here:
http://api.football-data.org/teams/19 doesn't allow CORS. You can't load the data. Update: The OP pointed out that s/he has an API key that allows CORS. This is not the issue then, but might be for other people trying to reproduce this.
{{data.club.team.name}} should be {{data.team.name}} because you said var club = this;. In other words, the controller you aliased data is the same as the club object. The club object is not a property of the controller data, it IS the controller's data.
See this plnkr for a demo. The commented out code doesn't work because of the api key, the locally hosted file does: http://plnkr.co/edit/ItD96Zfk0g1cLyGrrgBy?p=preview