The other day someone asked, How can I get a near real time view of streaming data in Salesforce? They had a good reason, which I won’t go into here, but the question led me to Smoothie Charts. And I gotta say, I kinda love ’em. So I’m going to show you a simple example, then I’m going to show you that example in Salesforce1 and a neat trick I noticed.
You can see Smoothie Charts in action by clicking this fabulous link.
In plain old HTML, you can do something like this:
<html> <head> <title>SmoothieCharts Demo</title> </head> <body> <p>Time Series One</p> <canvas id="timeSeriesOneChart" width="600" height="200"></canvas> <p>Time Series Two</p> <canvas id="timeSeriesTwoChart" width="600" height="200"></canvas> <script src="smoothie.js"></script> <script type="text/javascript"> //two artificial series of streaming data var timeSeriesOne = new TimeSeries(); var timeSeriesOneData = [50,70,100,90,75,55,10,50,50,50]; var timeSeriesOneCounter = 0; var timeSeriesTwo = new TimeSeries(); var timeSeriesTwoData = [50,100,50,0,50,50,50,50,50,50]; var timeSeriesTwoCounter = 0; setInterval(function() { console.log(timeSeriesOneData[timeSeriesOneCounter]); timeSeriesOne.append(new Date().getTime(), timeSeriesOneData[timeSeriesOneCounter]); timeSeriesOneCounter++; if (timeSeriesOneCounter == timeSeriesOneData.length) { timeSeriesOneCounter = 0; } },500); setInterval(function() { console.log(timeSeriesTwoData[timeSeriesTwoCounter]); timeSeriesTwo.append(new Date().getTime(), timeSeriesTwoData[timeSeriesTwoCounter]); timeSeriesTwoCounter++; if (timeSeriesTwoCounter == timeSeriesTwoData.length) { timeSeriesTwoCounter = 0; } },500); function createTimelineOne() { var chart = new SmoothieChart(); chart.addTimeSeries(timeSeriesOne, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 }); chart.streamTo(document.getElementById("timeSeriesOneChart"), 500); } function createTimelineTwo() { var chart = new SmoothieChart(); chart.addTimeSeries(timeSeriesTwo, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 }); chart.streamTo(document.getElementById("timeSeriesTwoChart"), 500); } createTimelineOne(); createTimelineTwo(); </script> </body> </html>
Which results in animated version of this:
This is pretty much what you would expect given their site, so not that interesting yet. Note the calls to “setInterval”. These loop through an array and just add data to the Smoothie Charts provided “TimeSeries” object every half second or so.
Now, let’s say you do this as a Visualforce page in Salesforce (note that you’ll need to upload smoothie.js as a static resource first). The code looks pretty similar:
<apex:page docType="html-5.0" standardController="Contact"> <p>Time Series One</p> <canvas id="timeSeriesOneChart" width="600" height="200"></canvas> <p>Time Series Two</p> <canvas id="timeSeriesTwoChart" width="600" height="200"></canvas> <script src="{!$Resource.LAB_SmoothieCharts}"></script> <script type="text/javascript"> //two artificial series of streaming data var timeSeriesOne = new TimeSeries(); var timeSeriesOneData = [50,70,100,90,75,55,10,50,50,50]; var timeSeriesOneCounter = 0; var timeSeriesTwo = new TimeSeries(); var timeSeriesTwoData = [50,100,50,0,50,50,50,50,50,50]; var timeSeriesTwoCounter = 0; setInterval(function() { console.log(timeSeriesOneData[timeSeriesOneCounter]); timeSeriesOne.append(new Date().getTime(), timeSeriesOneData[timeSeriesOneCounter]); timeSeriesOneCounter++; if (timeSeriesOneCounter == timeSeriesOneData.length) { timeSeriesOneCounter = 0; } },500); setInterval(function() { console.log(timeSeriesTwoData[timeSeriesTwoCounter]); timeSeriesTwo.append(new Date().getTime(), timeSeriesTwoData[timeSeriesTwoCounter]); timeSeriesTwoCounter++; if (timeSeriesTwoCounter == timeSeriesTwoData.length) { timeSeriesTwoCounter = 0; } },500); function createTimelineOne() { var chart = new SmoothieChart(); chart.addTimeSeries(timeSeriesOne, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 }); chart.streamTo(document.getElementById("timeSeriesOneChart"), 500); } function createTimelineTwo() { var chart = new SmoothieChart(); chart.addTimeSeries(timeSeriesTwo, { strokeStyle: 'rgba(0, 255, 0, 1)', fillStyle: 'rgba(0, 255, 0, 0.2)', lineWidth: 4 }); chart.streamTo(document.getElementById("timeSeriesTwoChart"), 500); } createTimelineOne(); createTimelineTwo(); </script> </apex:page>
And the basic outcome is predictable as well (note that this is not delivered by Sites, so you see the standard Salesforce UI unlike the fabulous link I mentioned above):
However, the astute among you will notice that I declared a “standardController” of “Contact” which means I can now get a page like this with just a few clicks (here’s a more detailed explanation).
So yeah that’s awesome but so ~2009. Check out this action on Salesforce1. Here’s that same Contact page in the Salesforce1 mobile app, iOS edition (iTunes).
And then the really fun part is that when you tap on the smoothie chart you get the full sized version of the page.
Which I think is pretty sweet. And, yes, for the curious, this works great on the Android version of Salesforce1 as well.
The code is available in an unmanaged package suitable for the Developer Edition of your choice.
Great Article Reid. I just stumble into this article after trying ( unsuccessfully) to use visualforce charting.