I tried to add charts to my ASP.net MVC project using Google Charts
I used the simplest way to understand how does it work !
On my Controller :
public JsonResult Index()
{
var data = new[]
{
new {Name = "China", Value = 1336718015},
new {Name = "India", Value = 1189172906},
new {Name = "United States", Value = 313232044},
new {Name = "Indonesia", Value = 245613043},
new {Name = "Brazil", Value = 203429773},
};
return Json(data,JsonRequestBehavior.AllowGet);
}
On my Web Page
</script><script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">// <![CDATA[
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(drawChart);
function drawChart() {
$.post('index', {},
function (data) {
var tdata = new google.visualization.DataTable();
tdata.addColumn('string', 'Country');
tdata.addColumn('number', 'Population');
for (var i = 0; i < data.length; i++) {
tdata.addRow([data[i].Name, data[i].Value]);
}
var options = {
title: "Top 5 Country's Population"
};
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
chart.draw(tdata, options);
});
}
// ]]></script>
<div id="chart_div"></div>
All what I have is a text on a page :
[{"Name":"China","Value":1336718015},{"Name":"India","Value":1189172906},{"Name":"United States","Value":313232044},{"Name":"Indonesia","Value":245613043},{"Name":"Brazil","Value":203429773}]
For the example you are trying to get working, you need two pages - one that displays the chart (the one with the HTML and Javascript) and the other one that serves the data (your MVC controller).
After that, you have to set the post line to your MVC page:
$.post('page/address', {},
You can use index too, but in this case you have to move the page with HTML to another file (for example chart.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 am trying to view Pie chart with JSON format data when I am trying to use Table chart is working fine
I think the problem is parsing the JSON but I do not know where it is exactly
I also checked questions on this topic but not using it just like that they are using PHP as server-side but not like that on the HTML Page with javascript
That is the code
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {
callback: drawChart,
packages:['table']
});
function drawChart() {
var jsonData = [
{"Car":23,"Bus":2,"Motorcycle":10,"Person":7},
{"Car":5,"Bus":6,"Motorcycle":9,"Person":8},
{"Car":10,"Bus":20,"Motorcycle":36,"Person":13}
];
var gglData = [];
if (jsonData.length > 0) {
// load column headings
var colHead = [];
Object.keys(jsonData[0]).forEach(function (key) {
colHead.push(key);
});
gglData.push(colHead);
// load data rows
jsonData.forEach(function (row) {
var gglRow = [];
Object.keys(row).forEach(function (key) {
gglRow.push(row[key]);
});
gglData.push(gglRow);
});
}
var data = google.visualization.arrayToDataTable(gglData);
var table = new google.visualization.Table(document.getElementById('table_div'));
table.draw(data);
}
</script>
</head>
<body>
<!--Div that will hold the pie chart-->
<div id="table_div"></div>
</body>
</html>
first, you have to include package --> 'corechart'
next, check the data format for a pie chart,
the data needs to be in rows vs columns...
see following working snippet,
each key / value pair is added as a separate row,
then the group() method is used to aggregate...
google.charts.load('current', {
callback: drawChart,
packages:['corechart', 'table']
});
function drawChart() {
var jsonData = [
{"Car":23,"Bus":2,"Motorcycle":10,"Person":7},
{"Car":5,"Bus":6,"Motorcycle":9,"Person":8},
{"Car":10,"Bus":20,"Motorcycle":36,"Person":13}
];
var gglData = [['Vehicle', 'Value']];
jsonData.forEach(function (row) {
Object.keys(row).forEach(function (key) {
gglData.push([key, row[key]]);
});
});
var data = google.visualization.arrayToDataTable(gglData);
var groupData = google.visualization.data.group(
data,
[0],
[{column: 1, aggregation: google.visualization.data.sum, type: 'number'}]
);
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
chart.draw(groupData);
var table = new google.visualization.Table(document.getElementById('table_div'));
table.draw(groupData);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
<div id="table_div"></div>
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 .
I am just beginning to try out Dojo for use with ESRI ArcGIS Server. I have tried a few tutorials and I'm having a problem with the Dojo FilteringSelect Dijit.
Relevant sections of my code:
<script>
dojo.require("esri.map");
dojo.require("esri.tasks.query");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.form.FilteringSelect");
var map;
function init() {
map = new esri.Map("mapDiv",{
basemap: "streets",
center: [-80.94, 33.646],
zoom: 8
});
dojo.connect(map, "onLoad", initFunctionality);
}
function initFunctionality(map) {
//build query task
var queryTask = new esri.tasks.QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/3");
//build query filter
var query = new esri.tasks.Query();
query.returnGeometry = true;
query.outFields = ["NAME", "POP2000", "POP2007", "POP00_SQMI", "POP07_SQMI"];
query.where = "STATE_NAME = 'South Carolina'";
query.outSpatialReference = {"wkid":102100};
var infoTemplate = new esri.InfoTemplate();
infoTemplate.setTitle("${NAME}");
infoTemplate.setContent( "<b>2000 Population: </b>${POP2000}<br/>"
+ "<b>2000 Population per Sq. Mi.: </b>${POP00_SQMI}<br/>"
+ "<b>2007 Population: </b>${POP2007}<br/>"
+ "<b>2007 Population per Sq. Mi.: </b>${POP07_SQMI}");
map.infoWindow.resize(245,105);
//Can listen for onComplete event to process results or can use the callback option in the queryTask.execute method.
dojo.connect(queryTask, "onComplete", function(featureSet) {
map.graphics.clear();
var symbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID, new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new dojo.Color([255,255,255,0.35]), 1),new dojo.Color([125,125,125,0.35]));
//QueryTask returns a featureSet. Loop through features in the featureSet and add them to the map.
dojo.forEach(featureSet.features,function(feature){
var graphic = feature;
graphic.setSymbol(symbol);
graphic.setInfoTemplate(infoTemplate);
map.graphics.add(graphic);
});
});
queryTask.execute(query);
}
function initLineID(features) {
var lineIdObjects = [];
dojo.forEach(features.features, function(feature) {
lineIdObjects.push({"name": feature.attributes.field_name});
});
//Build the appropriate data object for our data component
var data = {
"identifier": "name",
"items": lineIdObjects
}
//bind the data object to the datastore
var lineDataStore = new dojo.data.ItemFileReadStore({data: data});
//bind the data store to the FilteringSelect component
dijit.byId("lineid").store = lineDataStore;
}
dojo.ready(init);
</script>
as well as
<input dojoType="dijit.form.FilteringSelect"
id="lineid"
searchAttr="name"
name="widgetName"
onChange="doSomething(this.value)">
The challenge I face is that the resulting page only shows a basic
<input type="text">
box. Anyone know why this is? Thanks.
My guess is that you haven't configured a theme. You can do so by adding stylesheets and modifying the body element.
In this example, I am using the claro theme.
<link href="PATH_TO/dojo/resources/dojo.css" rel="stylesheet">
<link href="PATH_TO/dijit/themes/dijit.css" rel="stylesheet">
<link href="PATH_TO/dijit/themes/tundra/tundra.css" rel="stylesheet">
Add the theme name to the body element.
<body class="tundra">
parseOnLoad was set to false. For my future reference, why would I want to leave it at false?
I'm programming an ASP.Net MVC page and I'm using data from the server to create a Google chart. The x-axis is the date. The y-axis is the value. There are 2 lines of data being plotted to compare. Here is the relevant code:
#model IEnumerable<Tuple<DateTime,int,int>>
<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<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() {
var arr = [['Year', 'Sales', 'Expenses']];
//Using the Razor Model to create a Javascript array.
var arr2 = [
#foreach(var row in Model)
{
#:["#row.Item1.ToString("MMM d")", #row.Item2, #row.Item3],
}
];
for (var i = 0; i < arr2.length; i++)
{
arr.push(arr2[i]);
}
var data = google.visualization.arrayToDataTable(arr);
var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
chart.draw(data);
}
</script>
First of all, this code does actually work. Creating arr2 this way does turn a Razor model into something that I can use. However, my nose says code smell. It says that throwing together two languages razor and Javascript, which have somewhat similar C-based programming flow syntax could be confusing to the next person that comes along and tries to read it.
Is there a better way to write this?
However, my nose says code smell.
Oh yeah it stinks, I can feel it.
Is there a better way to write this?
Of course. Never build JSON manually as you did by mixing the 2 languages and writing loops and stuff. Use a JSON serializer:
#model IEnumerable<Tuple<DateTime,int,int>>
<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<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() {
var arr = #Html.Raw(
Json.Encode(
new object[] { new[] { "Year", "Sales", "Expenses" } }
.Concat(
Model.Select(x => new object[]
{
x.Item1.ToString("MMM d"),
x.Item2,
x.Item3
})
)
)
);
var data = google.visualization.arrayToDataTable(arr);
var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
chart.draw(data);
}
</script>
This will generate an equivalent code markup as yours but the whole model manipulation and encoding is done on the server. You could also write a custom HTML helper in order to simplify your code to this:
public static class ChartExtensions
{
public static IHtmlString ToChartData(
this IEnumerable<Tuple<DateTime, int, int>> model,
params string[] titles
)
{
return new HtmlString(
Json.Encode(
new object[] { titles }
.Concat(
model.Select(x => new object[]
{
x.Item1.ToString("MMM d"),
x.Item2,
x.Item3
})
)
)
);
}
}
and then in your view:
#model IEnumerable<Tuple<DateTime,int,int>>
<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<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() {
var arr = #Model.ToChartData("Year", "Sales", "Expenses");
var data = google.visualization.arrayToDataTable(arr);
var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
chart.draw(data);
}
</script>