Interactive Charts and Jelly

Andrew Pishchul
Giga Expert

Hi Guys,
I'm working on some nice charts in ServiceNow, which are based on morris.js components (http://www.oesmith.co.uk/morris.js/). It's really simple and there are no problem if you just want to display a static chart, which loads data only once, before it's displayed.

But now I need to put some parameters and 'Refresh/Apply' button on a form and be able to refresh the chart correspondingly. I think it should be possible with some javascript, but I don't know how to 'link' that button with data retrieval and chart refresh.

Any ideas how to do that?

(How to put morris.js chart in SN: I use Dynamic block, retrieve data in

<g:evaluate>
and display a chart with javascript)

[flickr-photo:id=9155182158]
5 REPLIES 5

adiddigi
Tera Guru

Hello,

One thing is, You directly cannot use "javascript" to "refresh" jelly.

There is a way though :

Organize your javascript function in such a way that, it takes an argument, (which is a JSON, like we did in your last post), and this function is responsible for "repainting" your chart.

Now have a link


<a onclick = "__refresh()">Refresh me</a>


now in refresh, you will need to get the data using Script Include in JSON format(using Glide Ajax). Here is a Script Include that i wrote which can retrieve Glide Records in JSON format ready to be used. Here is the link : http://servicenowdiary.com/wp-content/uploads/2013/04/SendJSON2v0.1.html


"No, I don't want to do it through Glide Ajax, show me a Jelly way"...

I'm no Jelly Jedi , I might be wrong - But I feel you cannot refresh only the chart with Jelly, You need to completely re-render the page, Which is done by submitting the same page again with parameters in your URL. Let me know if you need more help.

May the force be with you...


Thanks for the idea! but I didn't get how to use your script, sorry.. Could you give more details on that?

Below is a simple code for "Dynamic Content" block, which I'm experimenting with.. feel free to modify it, that would be really helpful!
Thanks in advance!



<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:include_script src="jquery-1.9.1.min.js.jsdbx"/> <!-- references to jQuery, Raphael and morris js -->
<g:include_script src="raphael.js.jsdbx"/> <!-- make sure that you put these js files as UI Scripts -->
<g:include_script src="morris.js.jsdbx"/> <!-- in ServiceNow -->

<script>
function my_refresh() {
// need to refresh data here ?
}
</script>

Log period (days) <input id="input1" type="text" value="7"/>
<input type="button" onclick="javascript:my_refresh();" value="Refresh"/>

<g:evaluate> // retrieve data for a chart
var gr = new GlideRecord("sys_trend");
var queryString = 'name=Open Incident Tickets^collectedRELATIVEGE@dayofweek@ago@3';
gr.addEncodedQuery(queryString);
gr.query();
var i=0;
var DD = [];
while (gr.next()){
DD<i> = {
day: gr.collected.getDisplayValue().toString(),
value: gr.value.toString()
};
i = i+1;
} // while
var json_v = new JSON(); // create new JSON object
var data_v = json_v.encode(DD); // encode data in order to pass to javascript
</g:evaluate>

<div id="my_chart" style="height: 350px;width:1570px;"></div>

<script>
var D = ${data_v}; // get data for a chart
new Morris.Line({
element: 'my_chart',
data: D,
xkey: 'day',
ykeys: ['value'],
lineWidth: 2,
labels: ['Open Incidents'],
xLabels: "hour",
hideHover: 'auto'
});
</script>

</j:jelly>


Hi,

That's a lot of Morris 🙂



However, I used a very simple example to drive the point home. The idea as I mentioned earlier is simple. Here is the code, I'm using GlideRecord's directly on Client side, change it to GlideAjax. I'm also hard coding some rows.

If you want to stream it continuously, use set-timeout and then keep streaming, instead of clicking a button.

Oh, and this gives you a pie chart.



<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<script></script>

<head>
<script></script>
<script></script>
<g:evaluate>

var fieldTrend = new KPILibrary().fieldTrend('priority','1,2,3');
JSUtil.logObject(fieldTrend);
gs.log(fieldTrend);
var text = fieldTrend['priority'];
var json = new JSON().encode(text);

</g:evaluate>

<title>Open Incidents By Severity:</title>
</head>

Enter the list of priorities, you want to fetch comma seperated<input type="text" id="daysPast"></input> <input type ="button" id="refresh" value ="Refresh" onclick ="refreshMe()"></input>


<div id="incident-donut"></div>





<script>
var a = ${json};
console.log(a);
Morris.Donut({
element: 'incident-donut',
data:a
});

</script>




</j:jelly>


Now here is the Client Script section of UI page:



//IDEA :

//Step one : When ever the refresh is hit, call the function refresh, and in refresh get all the parameters you need to create an encoded query.
//Step two: Call the Script Include that gives you something...I'm using GlideRecord directly, change it to Glide here for simplicity.

//Step three : Finally, the data returned will be passed to a function called repaint, which will literally re-paint your chart.




function refreshMe(){

var retArr = [];

//This function is responsible for repaintin your chart

var timeToBeShown = document.getElementById('daysPast').value; // You can either use jQuery/Protoype.js to do it, But i did it using Vanilla JS for simplicity
//Call the Script Include, that returns the Data in JSON format with the encoded Query you want to send.
var arr = timeToBeShown.split(',');
var i=0;
while(arr<i>){

var gr = new GlideRecord("incident");
gr.addQuery('priority',arr<i>);
gr.query();
var o = {};
var key = "Priority:"+arr<i>;
o['label'] = key;
o['value'] = 10;
retArr.push(o);

i++;
}

repaint(retArr);


}
function repaint(jSON){
var a = jSON;
Morris.Donut({
element: 'incident-donut',
data:a
});


}



Great, all is clear now.. but I got another problem: every time when I call 'repaint' function (click 'refresh' button) it creates an new/duplicated chart on the top. So it doesn't refresh, but creates a new chart with new data.

UPDATE: I found it.. we should use


.setData
method:


function repaint(jSON){
var a = jSON;
graph.setData(a); // graph is a variable - morris object

}