Write JSON to HTTP response in JSF - javascript

I want to create dynamically charts with de JavaScript Library "D3.js" in JSF. I mean with dynamically that a java methods takes data from the DB and writes a corresponding JSON. This method I have already programmed and it works. Then the JavaScript should access to the JSON and create the chart.
My question is: How to pass the JSON sting/file to the JavaScript. I think the best way is to write it in the HTTP response. But how to do that in JSF? Or do you have other suggestions?

Just let JSF print out the JSON string as if it's a JS variable.
<script>var data = #{bean.json};</script>

Step by step:
Create a backing bean method for updating a String attribute (jsonData) with the numerical array string JSON representation. You will have to:
Call to an EJB bean or JAX-RS service that returns the data.
Read the data in a List.
Convert the List collection to a JSON object and then stringify it, for example using the javax.json package from the standard.
Update the jsonData model attribute.
In the JSF page you have to include an outputLabel component binded to the jsonData attribute. You can hide it with CSS.
In the JSF page write Javascript code to put the component value in a Javascript variable. You can achieve it with a jQuery selector or in plain Javascript with getElementById function.
Finally you use the Javascript variable in the D3 library.
Note: The use of D3 libraty has been copied from here.
The code in the JSF page would be similar to this:
<h:form id="myForm">
<h:outputLabel id="myLink" value="#{yourBackingBean.jsonData}"
style="display:none;" styleClass="myLink"/>
<div class="someclass">
<h2>Create A Bar Chart With D3 JavaScript</h2>
<div id="bar-chart">
</div>
</div>
<script>
var chartdata = eval(document.getElementById("myForm:myLink").innerHTML);
// you can use jQuery too: $(".myLink").html()
// the size of the overall svg element
var height = 200,
width = 720,
// the width of each bar and the offset between each bar
barWidth = 40,
barOffset = 20;
d3.select('#bar-chart').append('svg')
.attr('width', width)
.attr('height', height)
.style('background', '#dff0d8')
.selectAll('rect').data(chartdata)
.enter().append('rect')
.style({'fill': '#3c763d', 'stroke': '#d6e9c6', 'stroke-width': '5'})
.attr('width', barWidth)
.attr('height', function (data) {
return data;
})
.attr('x', function (data, i) {
return i * (barWidth + barOffset);
})
.attr('y', function (data) {
return height - data;
});
</script>
</h:form>

Related

How to incorporate D3 svg elements into mithril.js?

I have been working with a framework using mithril.js and wish to add this script that draws an interactive tree into a component on my website.
So far, I just placed the JS code inside the script tags into my application, and I know that normally, the DOM object generated is obtained by calling svg.node(). But I know I can't return that from my object's view method, and instead need to return something of the form m(...). I tried looking at sources like this one but couldn't find a solution to my problem in them. Is there a known way for incorporating D3 SVG graphics into mihtril.js?
If you wanted to look at the code I have right now:
export default class TreeModal extends Modal {
content() {
var treeData = ... // some data
... // Other code; irrelevant to this question
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom).append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Some other code adding more functionality
// At this point, svg contains the SVG object which I wish to display
return svg.node(); // Raises ILLEGAL CONSTRUCTOR error; how to adapt this for Mithril.js?
}
}
Thanks in advance for all the help!
UPDATE: It seems like what a Modal is is crucial to my problem, because the API I use actually asks me to implement the content() method in any subclasses of Modal. I looked in Modal.js, and the relevant bit is:
export default class Modal {
view() {
return (
<div>
...
{this.content()}
...
</div>
)
}
}
Ideally, the solution would not have to override the view() method of Modal and only would include changes to content() in TreeModal.
It is really hard to write correct code without knowing the implementation or documentation of the base Modal class. To work around the API you have mentioned we can render a div in content with a random id that we use later to find it after it is placed into the DOM tree. Then inject the SVG into that div and use D3 as you normally would. I'm not sure if D3 needs cleanup but that would be done in onremove, again make sure to call the parents onremove if necessary.
I use JSX in content but I wasn't able to test its format.
export default class TreeModal extends Modal {
constructor() {
// #TODO: Not sure what Modal's constructor looks like.
super();
// Create a random DOM id we share between content()
//and oncreate().
this.svgContainerId = 'svg_container_' +
Math.floor(Math.random()*1000000000) + 1;
}
oncreate(vnode) {
// #TODO: Check if Modal implements this method or not.
// super.oncreate(vnode);
var treeData = {} // some data
... // Other code; irrelevant to this question
// USE our predefined id to lookup the div rendered in content
// and inject our SVG into that container.
var svg = d3.select(document.getElementById(this.svgContainerId)).append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom).append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Some other code adding more functionality
}
content() {
// render a plain DIV with our predefined id that never changes.
// this JSX is untested
return (<div id="{this.svgContainerId}"/>);
}
}

How to update d3 graphics if I load from external data?

So I haven't been able to find an example. I'm loading this huge dataset from an external file with D3, and I'm making some graph with some part of the data. When a slider is moved, I wish make the same graph with some other part of the data.
I used d3.json() for loading the data. The trouble that I'm facing is: since the d3.json() is asynchronous, it loads the dataset for once, and then I can't access it from other functions. I can't have another update function that makes a new graph since I simply can't access the data anymore.
Other people's suggestions have been "only using the data in the d3.json() function: How to store a JSON object loaded from a file?
d3.json("temp.json", function(data){
//use data here
})
// do not use data anymore
I tried to use a variable to store the data (the second answer from the same question link above):
var DATASET; // global
d3.json("file.json", function(data) {
DATASET = data;
//any other functions that depend on data
});
d3.selectAll().data(DATASET).......
And I couldn't get it to work at all, since (I assume this is the reason) the data hasn't been loaded to DATASET when the last line of d3.data calls DATASET
Is there any way I could update external data in d3? Should I just load the data again in my update function? Will it affect performance since the dataset is huge?
Thank you so much for answering!
If you want to read all the data once, you should store it in your global DATASET variable, just as you show in your code fragment. But instead of placing the update logic in your main script flow, put it inside an update(...) function, and call that from the d3.json callback, too... something like this (untested):
var DATASET; // global
d3.json("file.json", function(data) {
// data is loaded, save the full set
DATASET = data;
// filter the initial subset
var subdata = data.filter(...);
// now update the graph
updateGraph(subdata);
});
function updateGraph(data) {
// when invoked without any data, use the full dataset
var newdata = (data == null ? DATASET : data);
// data rendering logic starts here...
d3.selectAll().data(newdata);
...
}
This saves the full dataset in a global variable, draws the inital graph with a filtered subset of the data, and also allows you to change which data is shown by using a different filter before calling the function again.

I can't access my current element

This is an instance of Rappid Toolkit which uses jointJS for building visual tools as for web development. http://i.stack.imgur.com/6XSis.png
In this toolkit you can make a graph which can become a website.
My problem is the following one:
In every single element of this graph there is a box below it with:x,y,width,height,angle.
I want to change this information of this boxcontent and to display some info from this element but the code in which I have to add my snippet is the following(var Halo is the var for my element in the graph):
var halo = new joint.ui.Halo({
cellView: cellView,
boxContent: function(cellView) {
return"Here I want to display my box content info instead of x,y,width,height, angle";
}
}).render();
If I try to add my code inside it to access in JSON format my current element info my full code is:
var halo = new joint.ui.Halo({
cellView: cellView,
boxContent: function(cellView) {
// Drawing
var selectedObjectDataText = JSON.stringify(this.cellView.toJSON());
var selectedObjectDataJSON = JSON.parse(selectedObjectDataText);
return(selectedObjectDataJSON[0].wi_name);
}
}).render();
where wi_name is the name of my element but in the first line I can't access the specific element of my graph.
var selectedObjectDataText = JSON.stringify(this.cellView.toJSON());
Is there any global way to access my halo(my graph element) since this.cellView.toJSON() doesn't work?
I tried this.model.toJSON() this.cellView.model.toJSON() etc with no result
Note that JointJS links and elements are Backbone Models (linkView and elementView are Backbone Views).
To get the current value of an attribute use get() method.
boxContent: function(cellView) {
return cellView.model.get('wi_name');
}
Alternatively you can use prop(), that can return also nested properties of a model.
boxContent: function(cellView) {
return cellView.model.prop('wi_name');
}
It worked for var selectedObjectDataText = JSON.stringify(cellView.model.toJSON());
Thank you all for your support.

jQuery autocomplete + data attributes returned uiAutocomplete in the result

I am trying to use the autocomplete from jQueryUI. I need to do a data call to the backend, but other than the value from request.term there are other parameters i need to pass as well, so instead of using some other means if passing the additional data, i thought of using the data- attributes to do it.
var input = $(document.createElement("input"));
mydata.each(function() {
input.attr('data-'+this.nodeName, this.innerHTML);
});
So when i build my <input> i also put a brunch of data- attributes in there, the idea is that when i need to do the autocomplete call, i should be able to just do input.data() and grab everything i need.
However, i am getting some weird behavior from jQuery's .data() call.
input.autocomplete({
source: function(req, resp) {
$.ajax({
url: $(this.element).attr('action'),
dataType : 'json',
data: $(this.element).data(),
cache : false
}).done(function(resp) {
console.log(resp);
}).fail(function() {
alert("failed");
});
},
...
In the above code, when i do $(this.element).data(), it does indeed returned my all the attributes that i defined, but it also included something else, such as the uiAutocomplete object...
I thought the .data call is supposed to return only the items with prefix data-? What is the best way to grab the data- attributes?
This is because jQuery uses data attribute to store plugins namespace data in it, the more plugin used in it, the more data namespaces you will get. This is not weird, this is how jQuery works. As it is said here -
Calling jQuery.data( element ) retrieves all of the element's
associated values as a JavaScript object. Note that jQuery itself uses
this method to store data for internal use, such as event handlers, so
do not assume that it contains only data that your own code has
stored.
Reference:
https://api.jquery.com/jQuery.data/
As far as your solution, you should namespace your data if you wish to retrieve it later. Something like -
input.data('mydata', {name:'test'});
and then get it by -
var data = input.data('mydata');
In my observation the data method were returning the values set as below
1)setting the values
$(".autoCompleteInput").data("attribute1", "value1");
$(".autoCompleteInput").data("attribute2", "value2");
$(".autoCompleteInput").data("attribute3", 4);
$(".autoCompleteInput").data("attribute4", 5);
2) Getting the values
var datas = $(".autoCompleteInput").data();
3)assigning the values to other control
var stringData = JSON.stringify(datas);
$(".DataDisplay").val(stringData );
http://api.jquery.com/data/
Creating an Autocomplete sample
1)Created another page that returns a string of values used for autocomplete.
2)Made a Ajax request and retrieved the values into a local Array.
3)used the Array of values to Populating the AutoComplete.
4)incase of some widgets Make sure you are including the necessary jquery library and some related css into your page.
$(document).ready(function () {
$.ajax({
url: "AutoCompleteDataProvider.cshtml",
success: function (data) {
var autoCompleteValue = data.split(',');
$(".autoCompleteInput").autocomplete({
source: autoCompleteValue
});
},
error: function (textStatus) {
alert(textStatus);
}
});
});

D3 Hierarchy Links Don't Use Accessor Function

I have similar code to
D3.js Collapsible Force Layout: Links are not being generated
and I need to be able to use Hierarchy.links with an accessor function for the children of my data. IE.
links = d3.layout.tree()
.children(function(d) { return d.children() })
.links(nodes)
I have to use this accessor function because i'm dynamically getting the children in the function children().
Should I simply monkey patch d3 and change the d3_layout_hierarchyLinks(nodes); function to use an accessor?
?
I ended up just using a different method of calcuating the links. Given by mbostock on github
Calculate links by hand:
function computeLinks(nodes) {
return d3.merge(nodes.map(function(parent) {
return parent.computeChildren().map(function(child) {
return {source: parent, target: child};
});
}));
}

Categories