Displaying Charts with Titanium Appcelerator
The ti.charts module that you can find in the Appcelerator marketplace is for iOS only. I wanted a lightweight solution that could function on both Android and iOS and provide the most common of charts, bar, line, pie, etc. The simplest way to go about this was for me to use a charting javascript library within a web view.
My qualifications:
- Fast
- No jQuery dependency
- Animation on initial draw
- Good default styling
Now there are a lot of javascript charting libraries, but not a whole lot that meet all of the above qualifications. An inordinate amount rely on jQuery. I’ve messed around with a few that are dependent on jQuery before, and they typically have slow render times when there get to be too many data points, and by too many I mean not a whole lot. The webView is one of the most resource intensive components that you can use, so the more that can be done to keep things simple, the better.
I stumbled across a new library the other day after weeks of searching that does exactly what I want. ChartJS. Here is how to implement a chart into a webView, while also passing custom data points.
There are 3 files in this project, aside from automatically generated files
app.js
source/chart.html
source/chart.wvjs – this file contains the javascript from Chart.js located here
app.js var win = Titanium.UI.createWindow({ backgroundColor:'#fff' }); var chartView = Ti.UI.createWebView({ height: 200, width: 320, left: 0, top: 0, showScrollbars: false, touchEnabled: false, url: '/source/chart.html' }); win.add(chartView); var button = Ti.UI.createButton({ title: 'Regenerate', top: 220, }); win.add(button); button.addEventListener('click', function(){ var options = {}; options.data = new Array(Math.floor(Math.random()*1001), Math.floor(Math.random()*1001), Math.floor(Math.random()*1001), Math.floor(Math.random()*1001), Math.floor(Math.random()*1001)); Ti.App.fireEvent('renderChart', options); }); win.open();
We start off by creating our window, web view, and a button to redraw the graph with new data. When the button is clicked, we create an object called options. 5 random numbers between 1 and 1000 are generated and assigned to the options.data array.
Ti.App.fireEvent is then called with 2 arguments. renderChart is the 1st item passed, and this means that somewhere out there in our code, we need to have a corresponding event listener with the same name. The second item is the options object. Now, you may ask yourself why I didn’t pass an array directly……It won’t work, an object is expected. By attaching the array to the object, we can pass that data through to the event listener that will be located within our html file.
For the webView to communicate with Titanium itself, using the event handlers like this is necessary. Titanium and the webView need a way to open a line of communication, and that’s exactly what this does.
views/chart.html <html> <head> <title>Chart</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <script src="chart.wvjs" type="text/javascript" charset="utf-8"></script> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script> Ti.App.addEventListener('renderChart', function(options) { Ti.API.info('rendering chart'); var canvas = document.getElementById('myChart'); canvas.width = 310; canvas.height = 190; var data = { labels : ["Jan","Feb","Mar","Apr","May"], datasets : [ { fillColor : "rgba(220,220,220,0.5)", strokeColor : "rgba(220,220,220,1)", data : options.data } ] } var ctx = document.getElementById("myChart").getContext("2d"); new Chart(ctx).Bar(data); }); </script> </body> </html>
The default file extension of our charting library is .js. I have found there can be conflicts with Titanium when using a .js extension, so make sure that you rename your javascript files that are being called from a webView. My preference is .wvjs, but you can really use whatever.
You can see we have our charting javascript code within the eventListener for renderChart. This is executed when fireEvent is executed from our Titanium code. The width and height for the canvas are specified from javascript instead of adding the attributes to the HTML, this serves the purpose of clearing out what exists in the canvas when we regenerate a new chart with new data.