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.
Related
I am currently trying to convert a lot of backend code to front end (to lighten the load on a small system).
The code at the moment calls a PHP function to return specific information. (e.g. image locations, strings, styling)
I am converting this code to its js equivalent, the content from Mysql was converted to JSON and stored in a read only file and I am accessing that file using this code:
<script>
function jsread(tag) {
$.getJSON("/strings.json", function(result){
document.write(result[tag]['value']);
});
}
</script>
I want the function to "print" where ever it is invoked. document write writes the value to the page but stops all other loading and write only the value.
Let me be very clear on this: I DO NOT want to use anything that needs extra calls or references out side of this function, that will take months of work so no getting elements by their IDs I have already view many questions on this subject and none are what I can work with. I need something that can be applied to every situation. Other wise I will just have to read the JSON using PHP as a middle compromise.
The problem here is, document.write()'s behaviour is crazy across all the browsers, because, it directly modifies the document object and messes up with the events attached. So it is always better to avoid this function as each browser defines it differently and has a different effect on the same code, with different browsers.
Is there a way to use them without a direct reference?
Solution
The wise thing is, as I said in the comments, it is better to use one of the jQuery functions safely, which create a textNode and insert it the right way, without affecting the others:
<script>
function jsread(tag) {
$.getJSON("/strings.json", function(result){
$("body").append(result[tag]['value']);
});
}
</script>
In case, if you wanna do something like having a placeholder and doing stuff, then you can try giving something like this:
$(function () {
var data = "Dummy Data, that would probably get returned from the getJSON";
// Inside the Success function, do this:
$("span.placeholder-of-the-json").replaceWith(data);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="placeholder-of-the-json">This gets replaced</span>
I have a Flask webapp running a HighCharts Dynamic html graph. Here's an example of the View.
http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/stock/demo/dynamic-update/
The dynamic Y variable uses a random function in this example. However, I want to replace that function with my Python function on the server side. If i try to replace the y variable with my Python function, {{call_me}}, i get a static variable from my server. I want the function to make a GET request and replay the function, so the Y variable becomes updated. I also want this all to update so that the graph function works without reloading the page, causing it to move dynamically like in the fiddle link.
Here's my example flask:
#app.route('/dynamic/', methods = ['GET'])
def dynamic ():
def call_me():
return myDynamicVar
return render_template('dynamic.html', call_me=call_me())
I know very little about Ajax, so please explain how this would work.
Thank you!
In my application, I need to provide an API (something like the Google Maps javascript API), through which I can send some custom javascript (with some session and request related information) as the response. The javascript is then used to plot some graphs on the UI. I'm using Express with Jade as my templating engine. The code that I'm currently using is:
app.use('/graph',function(req, res){
//send out graph data
var var_name = req.session.var_name //fetch something from session
var graphData = fetchGraphData(req.query.graph); //function that fetches graph data
res.contentType("text/javascript");
res.render(__dirname + '/views/graph.jade', {
title: "Title", queryStr: JSON.stringify({var_name: var_name, graphData: graphData })
});
});
And the jade file:
| some_var_name = {
| initGraph : function(divId){
| //some code here
| var graphData = !{graphData}
| // do something
As a workaround, I have started each line of the jade file with |, so that jade parses the text as plain text, and doesn't add any html tags! It works fine, but is there a cleaner way to do this? The solution may or may not use Jade!
You should look into underscore templates. I think that for generating arbitrary text output it would be somewhat cleaner. Jade is purpose-built for rendering HTML.
You could also try Mustache or Handlebars.
Based on your comment, I see you'd like to keep using res.render for rendering the template. consolodate.js adds support for all major template engines to Express. Including Underscore templates, Handlebars, Mustache and Dust, mentioned by #TheHippo.
You may try to define JavaScript functions you need to send to the browser in a separate module, outside of the template, which is probably more correct way from the "concerns separation" point of view. Also if functions are defined in a separate module they can be used both in the server and in the browser.
Then you can convert the functions to strings using its toString() method either in a function that invokes the template or right inside the template, if it supports plain JavaScript which is the case with underscore, EJS and doT templates (I tried both underscore and EJS and ended up using doT which is not only the fastest but very versatile - check it out):
JS code:
// if you send the same functions you may want to convert them to strings in advance
var data = {
funcStr: func.toString();
};
res.render(view, data);
Template (doT):
<script type="text/javascript">
func = {{= it.funcStr }};
// now you can call it here if you want but I would use
// separate JavaScript files
func();
</script>
I use it to send pre-compliled templates to the browser together with the page on the first page load, but I think it can be used in your case too.
As a side question, why can't you just bundle all these functions in a separate JavaScript module and load them as normal script file?
You can use https://www.npmjs.com/package/rendercustomjs package, it works fine but in ejs templating
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.
From an ATK4 page, i can call jqplot using a javascript helper file like this
on the page
$chart = $p->add('jqplot', null, 'chart1');
$chart->setSeries(array(10,20,15));
define a jqplot.php like this
class jqplot extends View {
function render()
{
$plot=$this->js(true)->univ()->jqplot($this->series, $this->opts);
parent::render();
return $this;
}
}
and in a js helper file, link the php call to the javascript
$.each({
jqplot: function(series, opts){
console.log('jqplot series',series);
console.log('jqplot options',opts);
$plot=$.jqplot(this.jquery.attr("id"), series, opts);
return $plot;
}
}
If i have one chart on a page and reload it with an ajaxec call, it works fine but if i have several charts next to each other, only the first one is ok and the one next to it completely disappears if i call reload.
What i really want to do is call the jqplot replot function on the chart and pass it new data from the page but how can i do this ? The $plot object in the jshelper holds a javascript object and i need this object to call replot on it.
I am thinking maybe i can store the object when first created in a javascript associative array and then when i call replot, lookup the id and if found, call replot on the object but not sure what this code looks like or whether i have the right approach so any help appreciated.
Thanks in advance for you assistance.
It probably would be quite difficult to achieve this. First you need to properly handle destruction of jqPlot. You'll need a proper jQuery UI widget capable of restoring everything through a de-constructor. Then you might get it to work.
As far as Agile Toolkit is concern, it destroys the element containing your jqPlot using JavaScript, re-loads HTML and re-executes JavaScript.