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!
Related
I am new to Javascript, MVC, and making websites. I am making a company internal site that is used as a GUI for controlling hardware. The computer (Server) is connected to another machine taking measurements (data points). I have successfully created a page that plots this data using the plotly library, but instead of sending all of the data points from the server to the client after all the measurements have been taken, they want one point to be plotted at a time. This means making calls back to the server for each point. The class (object) that contains the functionality to do this is called the MeasurementManager. My first though was to pass this class into the view by ViewBag, then call the function that takes the measurement in a loop using razor syntax. For testing we can say we are taking 10 measurements. Then call the Javascript function that updates the plotly plot also inside of the loop and pass the data from the MeasurementManager to the plotly update function. I'm just not sure how to do this. For argument sake if it is possible to call the MeasurmentManager function inside of a javascript loop, that would be fine as well.
Controller: (Just created the MeasurementManager object and checks if it was created without error. I didn't want to do this error checking in the view with razor syntax. I tried passing the model strictly but I couldn't figure out how to get access to the same instance of the object, so ViewBag was the way to go. This function runs after clicking a button in another view)
[HttpPost]
public IActionResult Calibrate()
{
MeasurementManager measurementManager;
try
{
measurementManager = new MeasurementManager(ref ErrorMessage);
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
return RedirectToAction("ErrorView");
}
ViewBag.MeasurementManager = measurementManager;
return View();
View: (This is the Calibrate view returned from the above function. It is enough code to get a plotly plot with no points plotted, and it contains an update plot function)
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div class="row">
<div class="col-12">
<div id="CalibrationPlot" style="width:100%; height:1000px" class="js-plotly-plot"></div>
<div class="plot-container plotly"></div>
</div>
</div>
<script>
// Define Layout
var layout = {
xaxis: {range: [0, 256], title: "Mux"},
yaxis: {range: [0, 5*Math.pow(10, -10)], title: "Capacitance"},
title: "Capacitance vs Mux"
};
// Display using Plotly
Plotly.newPlot("CalibrationPlot", data, layout);
</script>
<script>
function updatePlot(capacitancePoint, elementPoint)
{
var update = [{
x: elementPoint,
y: capacitancePoint
}];
Plotly.update("CalibrationPlot", update, 0)
}
</script>
What I would like to add to the view is something like this which will measure a point and plot that value on the plotly plot. Hopefully this would achieve making a live plot on the webpage.
#{
for(int i = 0; i < 10; i++)
{
double MeasuredValue = ViewBag.measurementManager.MeasureValue(); // Y Coordinate
int xValue = i; // X Coordinate
// Make a call to Plotly.update (updatePlot function) and pass in these two values here
}
}
Feel free to add any solution that you think would work. I'm new to all of this and this is my first stackoverflow question.
Please use SignalR to update the frontend graph in real-time. OR Try to Blazor maybe that helps also.
I'm trying to build a UI with two dropdowns. The first one is "category", the second one is "sub-category". I can build a static list of "category" in my razor view. I want the "sub-category" item list to be dynamically updated when "category" is changed. I'm trying to pass all category information from server side to client side since the list is not big and there is no security issue. But I cannot find a good way to format my data and transfer it to client side. I can generate a json object with all of my category trees using the following code:
ExpandoObject catToSubcatMap = new ExpandoObject();
foreach (var cat in repository.Categories)
{
var subcats = repository.SubCategories.Where(s => s.ParentID == cat.CategoryID);
List<Object> subcatNameList = new List<object>();
foreach(var subcat in subcats)
{
subcatNameList.Add(new { Name = subcat.Name });
}
AddProperty(catToSubcatMap, cat.Name, subcatNameList);
}
Session["CatToSubcatMap"] = JsonConvert.SerializeObject(catToSubcatMap, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
The json itself looks perfect. But when I tried to read the value from my jQuery function, it failed:
var sss = '#Session["CatToSubcatMap"]';
It seems like there are too many special characters in the json string. My generic question is: how should I format and pass complicated data between server and client. Using Viewbag or Session, which one is preferred?
Thanks
Chris
You can do what you are trying to do; what you have should be formatted correctly, but you just have to include Html.Raw.
var sss = #(
Html.Raw(Session["CatToSubcatMap"].ToString())
);
Raw will essentially write out directly to the response without encoding the contents, which is what likely was happening.
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 revamping a site that allows booking of events that run each year. Each event has its own page which is currently entirely static: each page consists of a heading, description, and a list of dates sorted by venue. Each year when new dates become available, someone has to go in and manually change the HTML for each one, an obviously laborious task.
I'd like to automate the process somewhat by having, say, a CSV file that stores the dates (which could be added to piecemeal), and then the page grabs the relevant dates from there when it loads. I have no experience with server-side stuff, but I have a little knowledge of jQuery, and I have a feeling I should be able to do this with AJAX or the like - but how?
I think that ivoszz's idea is the best in your case. If you want to get a database and PHP, you will need a way to get your data into the database itself, which opens a whole new can of worms. Sure, database + server-side frontend is the industry standard, but I feel that it is oversized for your requirements.
It is easier to learn how to display JSON with jQuery, when reading it from a simple text file. You only need to write this code once.
Then, whenever there is a change, you can use a simple workflow: You use Excel to enter the events, using a prerecorded format. Then you export the Excel file as .csv. Use a small program to read the CSV and serialize it to JSON. Copy the ouput to a predetermined location on the server. Everything is ready.
Should somebody else have to update the site in your absence, all they need is Excel, the conversion tool (which is tiny), and the server password. I am posting the code for the conversion tool at the end of this answer.
Alternatively, you can use the code to create an ASP .NET WebForms project. Instead of serializing the objects created by the code, you can make an .aspx page and display the data on it using server-side code. However, this has some disadvantages.
.net webforms has a steeper learning curve than JavaScript, and the resulting code will probably use server-side controls, which are hard to style with CSS unless you know how to do it right.
if you are using an inexpensive hosting package instead of your own server, you have to make sure that the provider has the needed .net version on their server. Also, it will probably eat up more space because of all the libraries normally included in .net web projects.
if the structure of the input data never changes, you can compile the converter once and start treating it as a black box. Any changes to the way stuff is displayed can be made in the JSON-reading code and debugged directly in your browser. For a .net solution, you have to keep an installation of Visual Studio around.
.net webforms is not a future-proof skill. Microsoft has created a new, more convenient Web technology, .NET MVC, and I wouldn't suggest starting learning the older tech now. On the other hand, it is not a good idea to make this project MVC if you already have a bunch of existing static pages, because MVC cannot mix static and dynamic pages easily. You'll probably be able to use the content, but will have to rewrite the whole routing system and replace every single internal link.
Here is the code for a C# appliaction which will convert the csv to JSON. When compiled, place the .exe in the same directory as your csv, called DataSource.csv. Doubleclick it. It will produce a new file called autoOutput.json in the same directory. Each line in the .csv file has to be built in the event name; venue; date; cost; format. You can add comments or similar in the Excel to the right of cost, they will be discarded. The order of the lines does not matter. As long as an event name is unique, all venues and dates which start with it will be interpreted as belonging to that event. As long as the combination of event name and venue is unique, all dates will be interpreted as being about that event at that venue.
I'm not doing anything with the information about which lines could not be read because they were too short. You can append it to the file, or exchange its contents with the warning. Then the person who does the conversion will have to fiddle with the csv until there are no warnings, or you can try to leave it in the file, but ignore it when loading for dispalying.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web.Script.Serialization;
namespace ElendilEvents2JSON
{
public class Event
{
public String Name { get; set; }
public List<EventInVenue> venues { get; set; }
}
public class EventInVenue
{
public String VenueName { get; set; }
public List<EventInstance> Dates { get; set; }
}
public class EventInstance
{
public String When { get; set; }
public String Cost { get; set; }
}
class Program
{
static void Main(String[] args)
{
//read the file
List<int> unreadable;
List<Event> events = readFile(#".\SourceData.csv", out unreadable);
//write the file using the normal JSON serializer. Will output just everything as a single line. If the data structure is changed, it will output in the new structure.
string autoOutput;
JavaScriptSerializer serializer = new JavaScriptSerializer();
autoOutput = serializer.Serialize(events);
File.WriteAllText(#".\autoOutput.json", autoOutput);
}
public static List<Event> readFile(string path, out List<int> unreadableLines)
{
//get the contents out of the file
var lines = System.IO.File.ReadLines(path);
// split each line into an array of strings
var csv = lines
.Select(line => line.Split(';'))
.ToArray();
//will hold all events
List<Event> events = new List<Event>();
//will hold the numbers of all lines which were OK
List<int> unreadable = new List<int>();
//read each line, if you want to skip header lines, change the zero
for (int lineCounter = 0; lineCounter < csv.Length; lineCounter++)
{
string[] line = csv[lineCounter];
if (line.Length >= 4)
{
string eventName = line[0];
Event currentEvent;
//if we haven't yet created the event, create it now and add it to the dictionary
if (!events.Select(ev => ev.Name).Contains(eventName))
{
currentEvent = new Event { Name = eventName };
//the venues of the new event are still empty
currentEvent.venues = new List<EventInVenue>();
events.Add(currentEvent);
}
else currentEvent = events.Where(ev => ev.Name == eventName).Single();
// the same as above: we have the event now, if the current venue isn't yet on its list, enter it, else use the old one
string venueName = line[1];
EventInVenue currentVenue;
if (!currentEvent.venues.Select(ven => ven.VenueName).Contains(venueName))
{
currentVenue = new EventInVenue { VenueName = venueName };
currentVenue.Dates = new List<EventInstance>();
currentEvent.venues.Add(currentVenue);
}
else currentVenue = currentEvent.venues.Where(ven => ven.VenueName == venueName).Single();
string date = line[2];
string cost = line[3];
EventInstance currentEventInstance = new EventInstance { When = date, Cost = cost };
currentVenue.Dates.Add(currentEventInstance);
}
else
//if the line was too short
unreadable.Add(lineCounter + 1);
}
unreadableLines = unreadable;
return events;
}
}
}
The easiest way to do this would be with PHP and a mySQL database. You can add / overwrite the database with a CSV file, but in the long run, you would be better off developing a simple input form to update the database, rather than going through the task of overwriting / updating the mysql database manually, with the CSV file.
You don't need to start learning PHP to do such simple thing. If you know jQuery a little bit, simply add JSON file to your server, eg. events.json with this structure:
[
{
"event": "Event name",
"description": "description of the event",
"dates": [
{
"date": "20131028",
"place": "Dublin"
}, {
"date": "20131030",
"place": "London"
}
]
}, {
... another event here with the same structure...
}
]
load it with jquery.get, use some templating library (eg. underscore) and make simple template inside the page to display events and details. Finally you will have only 2 pages (or maybe only one), home.html for displaying the list of events and event.html to display details about the event.
Now editing events.json adds and changes events on the home page and details pages. This is just a rough example, it needs to be customized according to your requirements.
Ajax is a technology that alow a conversation between a client side script and a server side one. So in order to use it you will have to study some server side stuff. JQuery is a client-side script which means it only runs on the client machine at the browser.
I would recommend you to start with php it is simplier to learn and to use. And just to read a file is way to easy to learn as you want.
First off, I am fairly new to MVC and jQuery. I apologize if my question or terminology is incorrect.
I currently have a view in my MVC application that displays a list of addresses. On the same page, I also have a map where I wish to map these locations.
I am trying to find the 'proper' way of getting the list of address objects to the javascript in the view so that it may be iterated through and mapped.
I have seen some solutions which require a getJSON call to the controller from the javascript code. I wish to avoid this solution since it requires another trip to the database and webserver. All of the information that I need to render the addresses on the map is already being presented to the View via ViewData.
I have also seen a solution in which the javascript could access the data passed into the view via ViewModel.Data, however this example was working on a single object, as opposed to a list.
I would appreciate it if anyone had any tips or resources available.
Thanks
Just render the data into your javascript. Say you have a list of address objects like this:
public class Address
{
public string Line1 { get; set; }
public string City { get; set; }
}
// in your controller code
ViewData["Addresses"] = new List<Address>(new Address[] { new Address() { Line1="bla", City="somewhere"}, new Address() {Line1="foo", City="somewhereelse"}});
Render it into your javascript like this:
<script type="text/javascript">
var addresses = new Array(
<% for (int i = 0; i < ViewData["Addresses"].Count; i++) { %>
<%= i > 0 : "," : "" %>({line1:"<%= addr.Line1 %>", city:"<%= addr.City %>"})
<% } %>);
</script>
This basically creates a JSON formatted array with your address objects in javascript.
UPDATE: If you want to do this automatically using the framework code instead of writing your own code to serialize to JSON, take a look at the JavaScriptSerializer. Here's a howto from the great ScottGu on doing this: Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5
Technically, ViewData is not render to output HTMl thus will not be sent to client browser. The only way you can access to ViewData is render it to an object in the HTML like array or something like:
var cityList = new Array();
function addCity(cityId, cityName) {
var city = new Object();
city.CityID = cityId;
city.CityName = cityName
cityList .push(city);
}
<% foreach (Something.DB.City item in ViewData["Cities"] as List<City>)
{ %>
addCity(item.Id, item.Name);
<% } %>
This's the way I usually do when I need to render data for javascript
You could format the data in JSON on the server (as a string). Assign this to your your ViewData. In your View then, assign the ViewData to a javascript variable.
<script type="text/javascrpt">
var objList = <%= ViewData["objectListJSON"]; %>;
for (var obj in objList)
{
...
}
</script>