Loading Highcharts configuration externally - javascript

I have a lot of different charts that i'm rendering using Highcharts. Trying to find a good dynamic way of doing this. This is a proof of concept that I thought would be good. The thing is that the charts have different configurations (and data). So my initial thought is, in combination with mustasche.js, something like this:
HTML:
<div class="highchart"
data-some-other-variable=""
data-date-from=""
data-date-to=""
data-template="path/to/graph/graph.mustache"
>
Then a simple proof of concept in Javascript
var chart = $('.highchart');
$.get(chart.data('template'), function(options) {
// Do stuff
highcharts(options);
});
The thing is, the response I get is in pure text. And it can't be parsed into a javascript variable since it's not valid json. (the configuration object has a formater: function() {} inside it). Hence I cannot init highcharts with it. Eval isn't an option for this. Any ideas?

Give that code a try
var chart = $('.highchart');
$.ajax({
url: chart.data('template'),
type: "GET"
}, function(data) {
// Do stuff
var options = $.parseJSON(data);
highcharts(options);
});

Related

Highcharts not referenced error when using setOptions

We are currently using the DotNet.Highcharts API to incorporate Highcharts into our website. However, we are having some JavaScript reference issues when using the .SetOptions(GlobalOptions) call.
We are using a wrapper class to build our chart in the controller, then using a simple view to render it.
In our controller:
Chart chart = new HighCharts("chart-id");
chart.SetOptions(new GlobalOptions { Global = new Global { UseUTC = false; } });
chart.SetPlotOptions()...
In our view:
#model HighchartWrapper
#(Model.Chart)
The chart is rendered in the view as follows:
<script type="text/javascript">
Highcharts.setOptions({ global: { useUTC: false } });
var chart05;
$(document).ready(function() {
});
</script>
Chrome is raising an
"Uncaught ReferenceError: Highcharts is not defined"
error on the .setOptions() statement. Without this (and the corresponding .SetOptions() statement in .NET) the chart renders correctly. If I navigate off this page and then back again, the charts start to render correctly.
I've found that manipulating the ToHtmlString() return value to put the .setOptions call to inside the .ready() portion makes the charts display properly. Is this the approach that others have had to resort to?
Your suspicions are right, you need to run your relevant code within doc ready as follows.
$(document).ready(function() {
var chart = new Highcharts.Chart(options);
});
According to the docs, the examples are all executed once the dom is loaded, so it's likely safe to assume this is best practice and expect that within the javascipt libraries, code that is executing will depend on the .ready function. Surely digging through the source will answer any further questions you have, but this is totally fine.
Highcharts - How to set options

HTML5 Data attribute with complex JSON object and javascript

I came across a strange requirement (set by myself)...
I'm creating an easy to integrate ajax content loader plugin with lots of options and callbacks. Since the loader is a class and the developer can have multiple instances on a single page, I wanted to get rid of all the ugly code required for every single initialization and decided to use data attributes instead - they look awesome and proficient!
The question is: How to add functions and javascript in general inside a data attribute?
Example:
var url = "someurl/goes/here/";
var Template = new TemplateEngine('Name', {
onCreate: function(template, parts) {
// do something with template parts
template.ID += 1;
},
onRender: function(template, parts) {
template.addClass('flash');
}
});
var settings = {
container: DOM_ELEMENT|STRING,
template: Template,
disableDefaultRender: true,
// a bunch of hooks and callbacks like this:
onBeforeRequest: function(loader, data) {
new_data = data;
// modify request data somehow
loader.requestData = new_data;
},
onRender: function(loader, data) {
loader.renderData(data, function(part) {
// define specific rendering logic for different template parts
// in required
});
},
onAfterRequest: function(loader, data) {
},
onError: function(loader, data) {
}
// etc, etc
};
var THE_LOADER = new SuperFancyAjaxLoader(url, settings);
My original idea is to somehow put all of the above inside the said data attribute:
<div data-fancy-stuff="{all-or-most-of-the-above}">more stuff</div>
and make the script itself find all elements and initialize instances for each of them like so:
var elements = document.querySelector('[data-fancy-stuff]');
for(item in elements) {
try {
var data = elements[item].getAttribute('data-fancy-stuff');
var THE_LOADER = new SuperFancyAjaxLoader(data.url, data.settings);
} catch (ex) {
console.log('Someone messed with prototypes');
}
}
Is the idea of putting javascript functions inside an attribute idiotic? Or is there a way to actually put some js inside an attribute?
I understand that if there's so much javascript required, it's pointless to try and put it inside an attribute, but in real life cases (for this particular task), I will have 3-5 content loaders per page, most of them (or all) will use the same template and rendering logic, but they will all have to modify the request data differently by themselves.
p.s. Eval is Evil.
edit: I'm open to design proposals which do not involve third party MVC frameworks.
May be I don't understand well, but You want provide some JavaScipt modules/classes/objects through HTML5 attribute???
I think it's bad design. It's seems to be mixin of distinct layers.
So technically U have just ONE ability - to call eval, even after your PS because eval is the only point where JavaScript can get other JavaScript from String - ONLY.
But if U want dynamically load some complex javascript as reaction to data in some elements it's very good idea to learn and apply most ultimate thing for such scenarios - well-old-knownn require.js http://requirejs.org/. And if you want hardly bind DOM with some data and behavior you must to learn some of MVC JavaScript solutions - AngularJS, Backbome, Amber and so on.
By design u have to split your application to presentation layer where DOM will live and business layer were JavaScript will live. To bind them to each other you use string/JSON descriptors in DOM attribute and load JavaScript dynamically using on-fly head rewriting or by XHR+eval, such design is asynchronous, quick and is main choise of all solid network-based applications from gmail to all-other-cool-staff. To help build application with such model - require.js is best and most known helper.

Assigning json to variable

Ok, some explanation. Even though I don't think it has anything to do with the problem itself. I have a small django project that maps some data using leaflet. On a mouseover some ajax functionality is added, using the dajax(which is a "lightweight library to implement AJAX inside django projects") framework. The call itself looks like this:
dajax.add_data(simplejson.dumps(series), 'my_test_flot')
My js function receives json data which looks like this (using alert)
[{"color": "#dddd00",
"data": [[-0.5, -20.5]],
"label": "Tweede Zandlaag"}]
The object has more data to it but the problem is not with the object. When I copy/paste the data directly into the function var series = [] the behaviour is as aspected. As aspected means, the graph I'm drawing with flot is actually being drawn. Otherwise the graph remains empty.
function my_test_flot(dat) {
function MyFormatter(v, xaxis) {
return " ";
}
$(function () {
alert(dat)
var series = dat; // here lies the problem, but why?
...
Can anyone help?
Ok, problem solved. Apparently you have to use JSON.parse(). How it's done is explained here.
This does not copy the data - it just makes series a reference to the same object as dat. Therefore, if you later modify the object, all users retaining references to it see the changes. This is probably what causes your trouble.

Get json data in d3 from lift snippet

I am currently implementing a graph using d3 and a json object for data. A working example of such a graph can be found here: http://bl.ocks.org/950642. If you know d3 you can certainly directly jump to the My Problem part.
Quick overview of code
So, basically in my template I have the minimum code necessary:
<div id="main" class="lift:surround?with=default;at=content">
<div class="lift:GraphVisual.showGraph"></div>
</div>
Then in my snippet (GraphVisual.scala) I have the following:
def showGraph = {
<h3>Bachelor 5/6 - Communication Systems</h3> ++
<div id="graph-container" width="500px" height="100px">
<script type="text/javascript" src="static/scripts/graph_script.js></script>
</div>
In the javascript file defining the graph (graph_script) I mainly have the following
var vis = d3.select("#graph-container").append("svg:svg")
d3.json("static/scripts/jsonData.json", function(json) {
//Do stuff
}
My Problem
If I store my json data in a file, as shown above, everything works fine. Now I would like to generate my json object with lift. So I would like to have a function returning the json object representation of the graph in Lift and to use it in the script (which should be static).
What I tried
Suppose I have the following value defining my graph:
val graph = JObject(JField("nodes", JArray(List(...))), JField("links", JArray(List)))
I tried to define this graph as a variable in a script above the d3 script:
<script>{JsCrVar("myGraph", graph).toJsCmd}</script>
With this method I have the variable myGraph which is well defined but I don't know how to access it in graph_script.js
You could move your json data retrieval to REST API.
object MyGraphApi extends RestHelper {
serve {
case Req("graph" :: Nil, _, GetRequest) => {
JsonResponse(myGraphObject)
}
}
}
Then you can ajax pull it inside page using /graph url.
The solution of Lukasz, using the REST API works totally fine. During my research I also found out that my problem was just a javascript issue: I didn't need to use d3.json(...).
So I just had to remove the function d3.json(...) and put all that was inside outside of it. Then just call mygraph.nodes for example instead of json.nodes.

Adding a function to jqGrid to add local JSON

I"m trying to create an additional function for jqGrid to simplify adding JSON data to a grid that uses local data. The following attempt works except for one line, which causes it to fail.
$.fn.myrows = function(data) {
$(this).clearGridData();
$(this).jqGrid("setGridParam", {datatype: "json", loadonce: true});
$("#" + (this).selector)[0].addJSONData(data); // PROBLEMATIC LINE
$(this).jqGrid("setGridParam", {datatype: "local", loadonce: true});
};
This function is then called as $("#myGrid").myrows(jsonDataObject);.
Note that these lines work when they are not inside this function.
Any ideas? Thanks!
In the answer on your previous question I tried to explain how to extend jqGrid to support new method. To tell the truth, I don't see what real advantage you will have from the jqGrid extending. Why not just define your function as
var myrows = function(grid,data) {
// function body
};
and use it in the way:
myrows($("#myGrid"),jsonDataObject);
Much more important thing in my opinion is the implementation of what you need. Your current code have many disadvantages. If the grid has datatype: "local" then the local data paging can be used. The method addJSONData add all the data in the grid, so the page size can be broken.
If you have datatype: "local" and want to fill it with the data then the most effective way will be to set data parameter of the grid with respect of setGridParam and then just call $("#myGrid").trigger('reloadGrid',[{page:1}]) (see here).
Alternative you can use many other existing methods like addRowData which allows to add many rows of data at one method call.
If you get the JSON data from the server and thy to fill the grid with addJSONData method it will be really bad way. The versions of jqGrid starting with the version 3.5 has rich ways to customize the ajax request used. So it is strictly recommended that you don't use addJSONData in the case. See here for details.
You have a typo, do this on the line:
$("#" + $(this).selector)[0].addJSONData(data); // PROBLEMATIC LINE
The $ was missing before this

Categories