graphael charts with dynamic data from database using ASP.NET MVC3 - asp.net-mvc-3

I would like to understand how to get data from a database and show that data inside a graphael chart.
I'm working in ASP.NET MVC3 and I really don't understand how to make the things working.
Any help is really appreciated.

great I solved the problem!
I think that could be useful for someone else too:
here it is the Controller code:
public ActionResult ActionName()
{
List<String> data = new List<String>();
data.Add("legend0_4");
data.Add("legend1_5");
data.Add("legend2_8");
data.Add("legend3_12");
data.Add("legend4_8");
return Json(data, JsonRequestBehavior.AllowGet);
}
here it is the ajax side that also creates the pie chart:
jQuery.getJSON("/ControllerName/ActionName", function (data) {
// # Init
var levels = [], values = [];
for (var id in data) {
var compo = data[id].split("_");
levels.push("%%.%% " + compo[0]);
values.push(parseInt(compo[1]));
}
var r = Raphael("accountCharts"),
pie = r.piechart(320, 240, 100, values, { legend: levels, legendpos: "west", href: ["http://raphaeljs.com", "http://g.raphaeljs.com"] });
r.text(320, 100, "Interactive Pie Chart").attr({ font: "20px sans-serif" });
pie.hover(function () {
this.sector.stop();
this.sector.scale(1.1, 1.1, this.cx, this.cy);
if (this.label) {
this.label[0].stop();
this.label[0].attr({ r: 7.5 });
this.label[1].attr({ "font-weight": 800 });
}
}, function () {
this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
if (this.label) {
this.label[0].animate({ r: 5 }, 500, "bounce");
this.label[1].attr({ "font-weight": 400 });
}
});
});

Related

How can I generate a real-time highchart from my database data?

I have looked at the following links Binding json result in highcharts for asp.net mvc 4 , highcharts with mvc C# and sql, HighChart Demo and many others. However, I couldn't find a working demo showing how to implement a highchart using data from a database.
Objective:
I want to generate a real time highchart line graph getting data from my database. What I want is very similar to the third link which provides a real-time highchart with randomly generated values. It is also similar by X-axis and Y-axis, for I want my x-axis to be "Time" (I have a DateTime column in my database) and y-axis to be an integer (I have a variable for that as well in my database).
Please I need help in sending the model data to my razor view.
Note that I am already using SignalR to display a realtime table. I also want to know if it can be used to automatically update the highchart as well.
Below is the code snippet of my script in the view. I have used the code provided in link 3 for generating the highchart. Please tell me where should I apply the changes on my code.
#section Scripts{
<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/SignalR/Hubs"></script>
<script type="text/javascript">
$(document).ready(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.dataHub;
//debugger;
// Create a function that the hub can call to broadcast messages.
notifications.client.updateMessages = function () {
getAllMessages()
};
// Start the connection.
$.connection.hub.start().done(function () {
alert("connection started")
getAllMessages();
}).fail(function (e) {
alert(e);
});
//Highchart
Highcharts.setOptions({
global: {
useUTC: false
}
});
//Fill chart
$('#container').highcharts({
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.random();
series.addPoint([x, y], true, true);
}, 1000);//300000
}
}
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: Math.random()
});
}
return data;
}())
}]
});
});
function getAllMessages() {
var tbl = $('#messagesTable');
var data = #Html.Raw(JsonConvert.SerializeObject(this.Model))
$.ajax({
url: '/home/GetMessages',
data: {
id: data.id,
},
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
$("#g_table").dataTable();
}).error(function (e) {
alert(e);
});
}
</script>
}
UPDATED CODE
//Highchart
Highcharts.setOptions({
global: {
useUTC: false }
});
//Fill chart
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'spline',
events: {
load: $.connection.hub.start().done(function () {
alert("Chart connection started")
var point = getAllMessagesforChart();
var series = this.series[0];
setInterval(function (point) {
// add the point
series.addPoint([point.date_time, point.my_value], true, true)
}, 1000);
}).fail(function (e) {
alert(e);
})
}
}
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
maxZoom: 20 * 1000
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Value',
margin: 80
}
},
series: [{
name: 'Random data',
data: []
}]
});
function getAllMessagesforChart() {
var data = #Html.Raw(JsonConvert.SerializeObject(this.Model))
$.ajax({
url: '/home/GetMessagesforChat',
data: {
id: data.id,
},
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (data) {
data = JSON.parse(data);
//data_graph = [].concat(data);
//$("#debug").html(data_graph);
}).error(function (e) {
alert(e);
});
return data;
//return data_graph;
}
There is an example that might help you:
http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/highcharts/demo/line-ajax/
it uses an ajax callback function.
Well, you can also have a look at my sample where I add dynamically series by clicking add button.
http://plnkr.co/edit/Sh71yN?p=preview
You only need to add data in the right structure.
Have a look at the function
$("#btnAdd").click(function()
of my code script.js
I hope it helps.
regards,
Luis

How do I resize a photo into multiple photo sizes before saving in Parse.Cloud.beforeSave

First off let me start by saying I got this code working perfectly to get a thumbnail (https://parse.com/docs/cloud_modules_guide#images)
My current code is:
var Image = require("parse-image");
var photoSizesArray = {};
photoSizesArray["Thumb"] = [40,40];
photoSizesArray["Normal"] = [180,180];
Parse.Cloud.beforeSave("_User", function(request, response) {
var user = request.object;
if (!user.get("profilePicture")) {
// response.error("Users must have a profile photo.");
// return;
response.success();
return;
} else {
if (!user.dirty("profilePicture")) {
// The profile photo isn't being modified.
response.success();
return;
}
for (var key in photoSizesArray) {
Parse.Cloud.httpRequest({
url: user.get("profilePicture").url()
}).then(function(response) {
var image = new Image();
return image.setData(response.buffer);
}).then(function(image) {
// Crop the image to the smaller of width or height.
var size = Math.min(image.width(), image.height());
return image.crop({
left: (image.width() - size) / 2,
top: (image.height() - size) / 2,
width: size,
height: size
});
}).then(function(image) {
// Resize the image to 40 x 40.
return image.scale({
width: photoSizesArray[key][0],
height: photoSizesArray[key][1]
});
}).then(function(image) {
// Make sure it's a JPEG to save disk space and bandwidth.
return image.setFormat("JPEG");
}).then(function(image) {
// Get the image data in a Buffer.
return image.data();
}).then(function(buffer) {
// Save the image into a new file.
var base64 = buffer.toString("base64");
var cropped = new Parse.File("profilePicture"+key+"_" + Parse.User.current().id + ".jpg", { base64: base64 });
return cropped.save();
}).then(function(cropped) {
// Attach the image file to the original object.
user.set("profilePicture" + key, cropped);
}).then(function(result) {
response.success();
}, function(error) {
response.error(error);
});
}
}
});
My question is how do I go about saving the same photo but a different size in the same _User table?
Currently I'm getting an error that says
"Can't call multiple success/error multiple times"
or sometimes if it does work, it'll only save one of the 2 photo sizes.
Any help would be appreciated on how I should move the success/error response around.
OR if I should be looking into a different approach on how to save an additional photo size.
thanks,
So after a whole bunch research and trial and error, I finally understand how Promises work along side with httpRequests.
I initially had problems getting 2 simultaneous httpRequests going parallel to each other as for some reason, it gets overwritten or just ignored.
Here's what you need to know.
Parse.Cloud.httpRequest returns a Parse.Promise object.
this means that it has a .then and error functions (read more here: enter link description here
you actually have to RETURN the object in a for loop. This meant me placing my Parse.Cloud.httpRequest object in a separate function outside the for loop that I can call within the for loop.
When you finally have all the Promise Objects collected in a promises array, you go through it using Parse.Promise.when(promises).then()...
The following is my code that gets an uploaded photo, processes 2 sizes of it and saves them in separate _User columns - profilePictureThumb and profilePictureNormal.
var Image = require("parse-image");
Parse.Cloud.beforeSave("_User", function(request, response) {
var user = request.object;
if (!user.get("profilePicture")) {
// response.error("Users must have a profile photo.");
// return;
response.success();
return;
} else {
if (!user.dirty("profilePicture")) {
// The profile photo isn't being modified.
response.success();
return;
}
var promises = [];
var sizes = {
Normal: { width: 180, height: 180 },
Thumb: { width: 80, height: 80 }
};
for(var key in sizes) {
promises.push(
ProcessUrls(user.get("profilePicture").url(), key, sizes[key])
);
}
return Parse.Promise
.when(promises)
.then(function () {
// results are passed as function arguments
// map processed photos to result
var photos = Array.prototype.slice.call(arguments);
var result = {};
console.log("promises:" + promises)
photos.forEach(function (photo) {
console.log("photo.key: " + photo.key)
result[photo.key] = photo;
user.set('profilePicture' + photo.key, photo.file);
});
response.success();
}, function(error) {
response.error("error: " + error);
});
} // Else
});
function ProcessUrls(fullUrl, key, size) {
/* debugging
console.log("fullUrl: " + fullUrl);
console.log("key: " + key);
console.log("width: " + size["width"]);
console.log("height: " + size["height"]);
*/
return Parse.Cloud.httpRequest({ url: fullUrl })
.then(function(response) {
var image = new Image();
return image.setData(response.buffer);
})
.then(function(image) {
// Crop the image to the smaller of width or height.
var size = Math.min(image.width(), image.height());
return image.crop({
left: (image.width() - size) / 2,
top: (image.height() - size) / 2,
width: size["width"],
height: size["height"]
})
})
.then(function(image) {
// Resize the image to 40 x 40.
return image.scale({
width: size["width"],
height: size["height"]
});
})
.then(function(image) {
// Make sure it's a JPEG to save disk space and bandwidth.
return image.setFormat("JPEG");
})
.then(function(image) {
// Get the image data in a Buffer.
return image.data();
}).then(function(buffer) {
// Save the image into a new file.
var base64 = buffer.toString("base64");
var cropped = new Parse.File("profilePicture"+key+"_" + Parse.User.current().id + ".jpg", { base64: base64 });
return cropped.save()
.then(function (file) {
// this object is passed to promise below
return {
key: key,
file: file
};
})
})
};
Thanks to #Andy and a whole bunch of other StachOverflow users where I cobbled up this code together.
First of all you don't have to load source image each time. Load it once, then resize it multiple times.
You can't reuse the same Image object, so you have to create as many as you need for each individual resize operation.
Roughly the flow looks as following:
var grandPromise = Parse.Cloud.httpRequest({ url: url })
.then(function (response) {
var buffer = response.buffer;
var promises = [];
var sizes = {
normal: { width: 300, height: 300 },
thumb: { width: 100, height: 100 }
};
for(var key in sizes) {
var size = sizes[key];
var image = new Image();
// create promise for resize operation
var promise = image.setData(buffer)
.then(function(image) {
// do whatever scaling you want
return image.scale({
width: size.width,
height: size.height
});
})
.then(function (scaledImage) {
return scaledImage.data();
})
.then(function (buffer) {
var base64 = buffer.toString('base64');
var name = key + '.jpg';
var file = new Parse.File(name, { base64: base64 });
return file.save()
.then(function (file) {
// this object is passed to promise below
return {
key: key,
size: size,
file: file
};
});
});
// save promise to array
promises.push(promise);
}
// create promise that waits for all promises
return Parse.Promise
.when(promises)
.then(function ( /* result1, result2, ... */ ) {
// results are passed as function arguments
// map processed photos to result
var photos = Array.prototype.slice.call(arguments);
var result = {};
photos.forEach(function (photo) {
result[photo.key] = photo;
});
return result;
});
});
grandPromise.then(function (result) {
var normalURL = result.normal.file.url();
var thumbURL = result.thumb.file.url();
// save URLs on user model
user.set('profilePictureNormal', normalURL);
user.set('profilePictureThumb', thumbURL);
console.log('Saved normal size photo at ' + normalURL);
console.log('Saved thumb size photo at ' + thumbURL);
response.success();
}, function (err) {
console.log('Got error ' + err.code + ' : ' + err.message);
response.error(err);
});

ember model find query with params doesn't display on pagination

2I have an Ember app which connects to an api from where it gets articles. I make use of pagination to get 10 articles per request. This works. But now I wanted to add sorting to the request. I implemented this by using the extra parameter in the store.find.
However, for some reason if I use the 'return this.store.find('article', params);' instead of 'return this.store.find('article');' new articles (still requested and added correctly to the store!) in the getMore function are not beiing displayed or rendered. But when i remove the params parameter from store.find in model, it does work. What could be the case here?
templates/articles.hbs
<script type="text/x-handlebars" data-template-name="articles">
{{#each itemController="article"}}
<div class="item">
//...
</div>
{{/each}}
</script>
routes/articles.js
import Ember from 'ember';
export default Ember.Route.extend(Ember.UserApp.ProtectedRouteMixin, {
model: function(params) {
var params2 = {page: 1, per_page: 10, sort: params.sort};
return this.store.find('article', params2);
},
setupController: function(controller, model) {
controller.set('content', model);
},
actions:{
//...
},
getMore: function() {
// don't load new data if we already are
//if (this.get('loadingMore')) return;
//this.set('loadingMore', true);
var meta = this.store.metadataFor("article");
if (meta.hasmore) {
var controller = this.get('controller'),
nextPage = controller.get('page') + 1,
perPage = controller.get('perPage'),
sorting = controller.get('sort'),
items;
var params = {page: nextPage, per_page: perPage, sort: sorting};
this.store.findQuery('article', params).then(function (articles) {
controller.set('page', controller.get('page') + 1);
//this.set('loadingMore', false);
});
}
else{
$('#pagination_spinner').hide();
}
},
queryParamsDidChange: function() {
this.refresh();
}
}
});
controllers/articles.js
import Ember from 'ember';
var ArticlesController = Ember.ArrayController.extend({
itemController: 'article',
queryParams: ['sort'],
sort: 'rating',
page: 1,
perPage: 10
});
export default ArticlesController;
views/articles.js
import Ember from 'ember';
export default Ember.View.extend({
didInsertElement: function(){
//this.scheduleMasonry();
this.applyMasonry();
// we want to make sure 'this' inside `didScroll` refers
// to the IndexView, so we use jquery's `proxy` method to bind it
//this.applyMasonry();
$(window).on('scroll', $.proxy(this.didScroll, this));
},
willDestroyElement: function(){
this.destroyMasonry();
// have to use the same argument to `off` that we did to `on`
$(window).off('scroll', $.proxy(this.didScroll, this));
},
// this is called every time we scroll
didScroll: function(){
if (this.isScrolledToBottom()) {
$('#pagination_spinner').addClass('active');
this.get('controller').send('getMore');
}
},
scheduleMasonry: (function(){
Ember.run.scheduleOnce('afterRender', this, this.applyMasonry);
}).observes('controller.model.#each'), //TODO check
applyMasonry: function(){
$('#pagination_spinner').removeClass('active');
var $galleryContainer = $('#galleryContainer');
$galleryContainer.imagesLoaded(function() {
// check if masonry is initialized
var msnry = $galleryContainer.data('masonry');
if ( msnry ) {
msnry.reloadItems();
// disable transition
var transitionDuration = msnry.options.transitionDuration;
msnry.options.transitionDuration = 0;
msnry.layout();
// reset transition
msnry.options.transitionDuration = transitionDuration;
} else {
// init masonry
$galleryContainer.masonry({
itemSelector: '.item',
columnWidth: 0,
"isFitWidth": true
});
}
});
},
destroyMasonry: function(){
$('#galleryContainer').masonry('destroy');
},
// we check if we are at the bottom of the page
isScrolledToBottom: function(){
var distanceToViewportTop = (
$(document).height() - $(window).height());
var viewPortTop = $(document).scrollTop();
if (viewPortTop === 0) {
// if we are at the top of the page, don't do
// the infinite scroll thing
return false;
}
return (viewPortTop - distanceToViewportTop === 0);
}
});
nothing smart coming to my mind, but maybe it's that...
You've got the line:
if (meta.hasmore) {
in your getMore() function. Is this the case that you've got this meta field in one response and forgot in the other?

MVVM binding to a Kendo Grid is VERY slow?

I am trying to bind a ViewModel to a Kendo DataSource which in turn is given to a Kendo Grid. Nothing too fancy at this point.
It sort of works but is VERY slow! I have an alert informing me that I have received my json data (700 rows) within 2 seconds but it then takes around 15 seconds to update the viewmodel.
What am I doing wrong?
Thanks
$(document).ready(function () {
// create the viewmodel we use as the source for the list
var viewModel = kendo.observable({
items: [],
total: function () {
return this.get("items").length;
}
});
var dataSource2 = new kendo.data.DataSource({
data: viewModel,
pageSize: 50
});
// create the grid
$("#grid").kendoGrid({
dataSource: dataSource2,
height: 500,
scrollable: {
virtual: true
},
columns: [
{ field: "ID_ORDER", title: "ID", width: 80 },
{ field: "CREATION_DATE", title: "Creation Date" },
{ field: "STATUS", title: "STATUS", width: 80 },
** more columns (around 10) **
]
});
// pass this on to initialise
APPS.View.Orders.Initialise(viewModel);
});
Then in my typescript I am handling the Initialise call where the viewModel is passed in:
module APP.View.Orders {
export var _Scope: string = "Orders";
var _viewModelOrders: any;
export var Initialise = function (viewModelOrders: any) {
_viewModelOrders = viewModelOrders;
var orderdetails = {
userid: APP.Core.userID,
context: "DEAL"
};
// retrieve all orders
$.getJSON("/api/omsapi/GetOrders", orderdetails, function (mydata) {
try {
alert("item count (1): " + mydata.length);
jQuery.each(mydata, function () {
var newItem = this;
_viewModelOrders.items.push(newItem);
});
alert("item count (2): " + _viewModelOrders.items.length);
}
catch (e) {
alert(e.message);
}
});
}
}
Try building the item array and then assign it into the model.
Something like:
// retrieve all orders
$.getJSON("/api/omsapi/GetOrders", orderdetails, function (mydata) {
try {
alert("item count (1): " + mydata.length);
var items = [];
jQuery.each(mydata, function () {
items.push(this);
});
_viewModelOrders.items = items;
alert("item count (2): " + _viewModelOrders.items.length);
}
catch (e) {
alert(e.message);
}
});
You can suspend the observable temporarily by doing the following:
$.getJSON("/api/omsapi/GetOrders", orderdetails, function (mydata) {
try {
var simpleArray = viewModel.items(); // get a reference to the underlying array instance of the observable
jQuery.each(mydata, function () {
items.push(this);
});
viewModel.items.valueHasMutated(); // let the observable know it's underlying data has been updated
}
catch (e) {
alert(e.message);
}
}
Doing the above technique dramatically improves loading times. I have testing this loading a few thousand rows in a reasonable time.
To explain further, this is due to the line:
_viewModelOrders.items.push(newItem);
Each time you push an item into the array, it triggers a change event, which the Grid sees and updates itself. So if you push 700 items in, you are really causing the grid to update the DOM 700 times.
It would be much better to aggregate all the items into an array, then assign the array to the DataSource, with something like:
$.getJSON("/api/omsapi/GetOrders", orderdetails, function (mydata) {
datasource2.data(mydata);

Loading google datatable using ajax/json

I can't figure out how to load a datable using ajax/json. Here is my json code in a remote file (pie.json)
{
cols: [{id: 'task', label: 'Task', type: 'string'},
{id: 'hours', label: 'Hours per Day', type: 'number'}],
rows: [{c:[{v: 'Work'}, {v: 11}]},
{c:[{v: 'Eat'}, {v: 2}]},
{c:[{v: 'Commute'}, {v: 2}]},
{c:[{v: 'Watch TV'}, {v:2}]},
{c:[{v: 'Sleep'}, {v:7, f:'7.000'}]}
]
}
This is what I have tried so far but it doesn't work.
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages:["piechart"]});
function ajaxjson() {
jsonreq=GetXmlHttpObject();
jsonreq.open("GET", "pie.json", true);
jsonreq.onreadystatechange = jsonHandler;
jsonreq.send(null);
}
function jsonHandler() {
if (jsonreq.readyState == 4)
{
var res = jsonreq.responseText;
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable(res, 0.6)
var chart = new google.visualization.PieChart(document.getElementByI('chart_div'));
chart.draw(data, {width: 400, height: 240, is3D: true});
} // end drawChart
} // end if
} // end jsonHandler
function GetXmlHttpObject()
{
var xmlHttp=null;
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
return xmlHttp;
}
Things work perfectly if I replace the 'res' variable with the actual code in pie.json.
Any help would be greatly appreciated.
Just a guess because I'm unfamiliar with the google objects you are using, but I'm pretty sure responseText just returns a string. JavaScript can't natively parse JSON (only XML to the best of my knowledge), so you'd have to eval("var res = " + jsonreq.responseText).
I hope this helps.
if I were in you, I'll use jQuery to save some code:
$.getJSON("pie.json", function(data) {
drawWanChart(data);
});
and here the function that draws your chart:
function drawWanChart(jsonData)
{
jsonData = jQuery.parseJSON(jsonData);
if(jsonData != null)
{
data = new google.visualization.DataTable(jsonData);
chart = new Google.visualization.Table(document.getElementById('chart_div'));
chart.draw(data, null);
}
}
Please refer to official documentation:
$.getJSON
PieChart on Google Playground

Resources