Building a realtime dashboard with Firebase and Chartjs
Firebase is the perfect back-end for rapid prototyping and for building realtime production apps and it’s realtime capabilities make it the ideal way for building dashboard apps. Building a dashboard app with charts that don’t cost you a bean is where ChartJS comes in.
ChartJS lets you include great looking charts in your app, from line graphs to bar and pie charts and lets you customise them really easily. Now if you want really, really, really spectacular 3D charts in your app then you’ll likely need to go and pay a hefty sum for a charting library but if it’s a prototype you’re building or you don’t wanna a spend any dough then ChartJS is more than capable of giving you glorious looking charts. Working together with Firebase, you can get charts updating in realtime from a cloud datastore enabling you to build a dynamic dashboard in less than a few of dozen lines of Javascript code. Sound cool? Let’s show you how it’s done.
Setup with a skeleton HTML page called index.html as shown below. We’ll be keeping things really simple in this tutorial, enabling Firebase and ChartJS to take centre stage so we’ll only be using jQuery rather than a major framework. I really like Semantic-UI as an alternative to Bootstrap as the CSS and layout library so we’ll use their CDN as well. Everything we need is shown below:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css”>
<link rel="stylesheet" href="firechart.css">
<body style=“margin:20px">
<script src="http://code.jquery.com/jquery-1.12.2.min.js" integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script>
<script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.j</script>
<script src="firechart.js"></script>
Notice we reference a firechart.css and a firechart.js. We’ll be creating these files shortly.
Before you get going you’ll need to set yourself up with a Firebase if you don’t have one already. It’s completely free and really easy to do. Just go to firebase.com and click Signup with Google, follow the destructions and once you’re setup you’ll get a unique Firebase Url ending in firebaseio.com. Copy that as you’ll need it in your code to reference your Firebase.
The first part of this tutorial will all be about getting our charts setup. We’ll then move on to getting the data from Firebase to turn those charts into a dynamic, realtime dashboard.
Let’s say our Dashboard is going to have 3 charts based on sales data for the past six months. This sales data contains the total of sales and gross profit for each month, each salesperson and region. In the first chart we’ll show a line graph of the total sales and gross profit for each of the six months. On the second graph we’ll show a bar chart telling us the breakdown of sales per salesperson for the whole period. The third area on our dashboard will show a pie or a doughnut chart for the whole period by region.
We’ll start but laying out our HTML page which is surprisingly simple thanks to ChartJS. We’ll use Semantic-UI’s grid layout to keep our charts in order and everything responsive. We’ll put a <h1> tag as our header and some <div> elements which implement Semantic’s grid system. We’ll be having a layout with 3 areas across, one for each chart. Semantic’s grid system uses 16 columns across so we’ll set each of our areas to be 5 columns wide for a total of 15 leaving one over for padding. We’ll create a div to surround everything and we can do all of this simply by specifying some Semantic classes on our <div> container elements. The HTML below should be placed immediately below the opening <body> tag :
<h1>Firechart Dashboard</h1>
<div class="five wide column">
<div class="five wide column">
<div class="five wide column">
Each of our charts will be placed within each grid column area. The actual charts will not be defined in HTML but in Javascript however we have to tell the page where they will be drawn. To do this we need to define <canvas> elements in our HTML and give each of them a specific id which we’ll use to reference them in our Javascript :
<div class="five wide column">
<canvas id=“salesByMonth”></canvas>
</div>
<div class="five wide column">
<canvas id=“salesByPerson”></canvas>
</div>
<div class="five wide column">
<canvas id=“salesByRegion”></canvas>
</div>
That’s pretty much all we need for the HTML. Next create a new file in the same folder as your index.html called firechart.css. This will include some important classes for styling our charts as shown below :
canvas{
width: 100% !important;
max-width: 1200px;
height: auto !important;
max-height:600px;
}
.chartLegend li span{
display: inline-block;
width: 12px;
height: 12px;
margin-right: 5px;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
.chartLegend li {
display: inline;
padding-right: 8px;
}
.bar-legend li {
list-style: none;
}
.line-legend li {
list-style: none;
}
.pie-legend li {
list-style: none;
}
.align-center {
text-align: center;
}
The classes above the squiggly line are really important to our chart layout and you should definitely add them. The classes beneath the line are more my preferences so you can leave them or change them if you want. We’ll explain what’s happening here later but we really want to get our charts working with our Javascript so we’ll come back to it later.
Create another new file in the same location called firechart.js. This is where we’ll be coding our logic.
As explained earlier we’ve only placed a canvas element on our page. To hook a chart up to a canvas element we’ll need to define a context to the canvas first. We’ll define a context for each of the 3 canvas elements and set them to 2d as that’s the type of display used by ChartJS. We’ll use jQuery selectors to reference each of the canvas elements :
var ctxSalesByMonth = $(“salesByMonth”).get(0).getContext(“2d”);
var ctxSalesByPerson = $(“salesByPerson”).get(0).getContext(“2d”);
var ctxSalesByRegion = $(“salesByRegion”).get(0).getContext(“2d”);
Now we can create our charts by simply newing up a Chart object and passing in the relevant context. We then chain on a function call to the type of chart that we want to create. In our example each of our charts uses a different graph type :
var chartSalesByMonth = new Chart(ctxSalesByMonth).Line();
var chartSalesByPerson = new Chart(ctxSalesByPerson).Bar();
var chartSalesByRegion = new Chart(ctxSalesByRegion).Pie();
We now need to actually pass the data that we want our chart to use along with display information such as labels and colours. To do this we need to setup an object containing everything we want to display. Let’s start with our Sales By Month line chart. Here’s the sample data object we use which we’ll insert above the chart declarations :
var dataSalesByMonth = {
labels : ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: [20000, 25000, 30000, 22000, 28000, 34000]
}
]};
The labels property of our data object is an array with each value representing a point in the line graph. In this example we’re using the first 6 months of the year. datasets is another array with each element an object containing information for a specific range of data. In this case, as it’s a line chart, each dataset will represent a line in the chart. In the example we only have a single dataset so it’s not too important at this point but we will be adding another one later.
Each dataset has a label property, useful for when displaying a legend for the chart. We have a series of colour settings which are not really important here but you can play around with these settings yourself to get them looking pretty. What we’re interested in is the data property which is our actual data for the chart. Each element in the data array corresponds to an element in the labels array. So in the example above Jan is 20000, Feb is 25000 and so on. Obviously later we’ll make this data dynamic by retrieving it from our Firebase instead but for now this static data will do to get us up and running.
To assign this data object to our chart we simply pass it as a parameter to our Line() function as below :
var chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
For the next graph, which will be a Bar chart, we’re going to display the total sales for each sales person. In this chart we want to display a bar for the total sales of each salesperson and alongside a bar for the gross profit for each salesperson. As we’re showing two pieces of data (bars) for each salesperson we’ll need two datasets :
var dataSalesByPerson = {
labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: [20000, 25000, 30000, 22000, 28000, 34000]
},
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: [10000, 12500, 15000, 11000, 14000, 17000]
}
]};
Again we’re defining a labels array which in this case is the names of the sales people. This time the datasets array has 2 objects, one for Total Sales and one for Gross Profit. For each dataset, which will represent a bar in the chart, we’ll assign different colour settings and of course difference data arrays. Each element in the data arrays represents a value for the corresponding salesperson in the labels array.
Now we’ve shown how to add a second dataset object to a chart, in this case by showing the Gross Profit for each salesperson, lets do the same by adding another dataset to the first chart’s data object (remember to put a comma immediately after the closing curly brace of the first dataset) :
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: [10000, 12500, 15000, 11000, 14000, 17000]
}
Finally we’ll define data for the pie chart. Pie charts operate on different types of data. For example a pie chart cannot have different datasets - each element in a pie represents a single value. The data for pie charts is not defined in an object but as an array of objects with each representing a “slice” or an element of the pie. Each object in the pie array has to have a label, a value, a colour and a highlight colour. Here’s the sample data for our pie chart :
var dataSalesByRegion = [
{
label: "West USA",
color: "rgba(67, 214, 92, 0.5)",
highlight: "rgba(67, 214, 92, 1)",
value: 20000
},
{
label: "UK",
color: "rgba(218, 233, 39, 0.5)",
highlight: "rgba(218, 233, 39, 1)",
value: 12500
},
{
label: "Asia",
color: "rgba(230, 150, 200, 0.5)",
highlight: "rgba(230, 150, 200, 1)",
value: 25000
},
{
label: "West Europe",
color: "rgba(200, 220, 50, 0.5)",
highlight: "rgba(200, 220, 50, 1)",
value: 18000
}
];
With our data defined for each of our charts we can pass them into the relevant chart functions to create our graphs :
chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
chartSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson);
chartSalesByRegion = new Chart(ctxSalesByRegion).Doughnut(dataSalesByRegion);
OK we’re ready to take a look at the static dashboard we’ve created. Open the index.html file in your browser and hey presto! there’s our dashboard. Have a quick play with the charts and notice all of the great functionality we get for “free” with ChartJS. For example the cool loading animation. We also get tooltips of each element’s data displayed as you mouse over a bar or a point in the line. For the pie chart, ChartJS picks the colour you declare in the highlight property along with a tooltip about the pie slice you’re hovering over.
One thing we’re missing here is a legend to let us know what’s going on. For example we have two lines on the first chart and two bars on the second yet there’s nothing to tell us what each one represents. Luckily ChartJS makes it super-simple to add a legend to our charts. Go back to your editor and open your index.html file. Add a new <div> tag underneath each <canvas> tag and give it an id that you can reference and give them all the class of chartLegend as below :
<div class="five wide column">
<canvas id="salesByMonth"></canvas>
<div id="legendSBM" class="chartLegend"></div>
</div>
<div class="five wide column">
<canvas id="salesByPerson"></canvas>
<div id="legendSBP" class="chartLegend"></div>
</div>
<div class="five wide column">
<canvas id="salesByRegion"></canvas>
<div id="legendSBR" class="chartLegend"></div>
</div>
In the firechart.js file we can define our legend simply by calling the generateLegend() function on each of the chart objects that we created. This returns the necessary html to display a legend for each chart and we assign them to the <div> elements we created above using jQuery selectors. The following code should be added immediately below the lines where we create our charts :
$("#legendSBM").html(lineSalesByMonth.generateLegend());
$("#legendSBP").html(barSalesByPerson.generateLegend());
$("#legendSBR").html(pieSalesByRegion.generateLegend());
Refresh your page in the browser and this time we have our legend. Pretty cool :)
Going back to our firechart.css, most of the classes are defined to style our legend nicely. For example the legend’s html is returned to display in a <ul> with each element displayed in a <li> tag. To avoid the bullet-points that normally come with <li>’s I set their display-type to none. Notice I had to define a class to do this for each type of chart I’m using - this shows how you can override ChartJS’ standard legend styles for each chart type individually. In the chartLegend li class I set it’s display to inline, this shows each element for the legend on the same line (with some padding added to space them out) rather than the default behaviour of showing each one on a different line. The chartLegend li span style is the most important related to the legend as it defines their layouts.
We’re done for the chart setup side of things. Next we’ll look at the trickier part - making our dashboard work with actual data from our Firebase and to make it dynamic and realtime. Before we crack on, here are the full listings for the index.html and firechart.js files so far. Notice I’ve added <h3> tags to each chart area in the index.html so that we have a header for each chart :
<title>Firechart Dashboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/semantic.min.css">
<link rel="stylesheet" href="firechart.css">
<body style="margin:25px">
<h1> Firechart Dashboard</h1>
<div class="five wide column">
<h3 class="align-center">Sales By Month</h3>
<canvas id="salesByMonth"></canvas>
<div id="legendSBM" class="chartLegend"></div>
<div class="five wide column">
<h3 class="align-center">Sales By Salesperson</h3>
<canvas id="salesByPerson"></canvas>
<div id="legendSBP" class="chartLegend"></div>
<div class="five wide column">
<h3 class="align-center">Sales By Region</h3>
<canvas id="salesByRegion"></canvas>
<div id="legendSBR" class="chartLegend"></div>
<script src="http://code.jquery.com/jquery-1.12.2.min.js" integrity="sha256-lZFHibXzMHo3GGeehn1hudTAP3Sc0uKXBXAzHX1sjtk=" crossorigin="anonymous"></script>
<script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js"></script>
<script src="firechart.js"></script>
var ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d");
var ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d");
var ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
var dataSalesByMonth = {
labels : ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: [20000, 25000, 30000, 22000, 28000, 34000]
},
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: [10000, 12500, 15000, 11000, 14000, 17000]
}
]};
var dataSalesByPerson = {
labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: [20000, 25000, 30000, 22000, 28000, 34000]
},
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: [10000, 12500, 15000, 11000, 14000, 17000]
}
]};
var dataSalesByRegion = [
{
label: "West USA",
color: "rgba(67, 214, 92, 0.5)",
highlight: "rgba(67, 214, 92, 1)",
value: 20000
},
{
label: "UK",
color: "rgba(218, 233, 39, 0.5)",
highlight: "rgba(218, 233, 39, 1)",
value: 12500
},
{
label: "Asia",
color: "rgba(230, 150, 200, 0.5)",
highlight: "rgba(230, 150, 200, 1)",
value: 25000
},
{
label: "West Europe",
color: "rgba(200, 220, 50, 0.5)",
highlight: "rgba(200, 220, 50, 1)",
value: 18000
}
];
lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson);
pieSalesByRegion = new Chart(ctxSalesByRegion).Pie(dataSalesByRegion);
$("#legendSBM").html(lineSalesByMonth.generateLegend());
$("#legendSBP").html(barSalesByPerson.generateLegend());
$("#legendSBR").html(pieSalesByRegion.generateLegend());
I’ve already setup all of the data needed to generate the sales dashboard. To avoid having to type all of this data in manually yourself just head over to my GitHub page for FireChart (https://github.com/MancDev/firechart), download the zip containing the files, head over to your Firebase dashboard, click the Import Data button, select the firechartdata.json file and there you have all of the necessary data you need for this dashboard app. If you really can’t be bothered with any of that then you can just point to my demo Firebase reference as there’s no authentication on it - but I recommend you do import the data to your own Firebase to be able to view the data in your own Firebase dashboard.
The data structure is quite important here. Remember we’re not dealing with a relational database with tables and columns and niceties like sum and join. We’re dealing with a NoSQL object store where the purpose is speed and scale and we have to work with it accordingly. You can see that I have a node for sales and that leads us to another level of nodes. If we open up one of these child nodes we can see it contains a value for the name of the month and child nodes which each hold a piece of sales data. If we open one of these up we’ll see it contains some sales info for the salesperson, the region and the sales and profit. Now this structure is perfect for our first chart, Sales By Month, but will require more work to populate our 2nd and 3rd charts. It’s quite common in NoSQL databases to set up different data structures to suit the requests and queries you want and to duplicate the data in several places to optimise speed. However in this case I’m just storing one set of data and we’ll do the work we need to do on the client to get the data calculated to populate each chart. In doing so we’ll be highlighting some useful techniques to query data in Firebase.
Let’s get started by adding the following lines at the top of your firechart.js file, just below the opening (function() { :
var firebaseRef;
var salesRef;
Add all subsequent code we include from hereon in at the very bottom of the firechart.js file (a full listing of the completed code will be included at the end).
We’ll wrap our initialisation code inside a (document).ready() function. We’ll create a reference to the root of our database by newing up a Firebase object. Then we’ll add a reference to the sales node which is what we’ll be working with by using the child() method on our root reference.
$(document).ready(function() {
firebaseRef = new Firebase("https://scorching-fire-224.firebaseio.com");
salesRef = firebaseRef.child(“sales”);
});
Substitute the Firebase Url given with your own.
To retrieve data from a Firebase reference we call a method and pass it a callback function. The most common method to get data from Firebase is the on() method. This will get the data the first time that we call it and sets up a listener to constantly listen for the type of changes we want. In this code we’ll call on() for our salesRef reference that we assigned and tell Firebase we want to listen for any changes to the data on that reference.
salesRef.on(“value”, salesListener);
Add the above line as the last line in the (document).ready() function.
The first parameter is the event that we want to listen for. The value event that we’re passing is telling Firebase that we want to know when any value is changed in our sales data reference. This includes any change, whether data is added, updated or deleted, we want the event to fire for all changes. There are other event types we could listen for - for example if you just wanted to listen for when objects are added to that reference you’d pass in “child_added” as the first parameter instead.
The second parameter we pass is our callback function which contains the code we want to execute every time there is a change in the data. This is where Firebase’s realtime capabilities start to shine. We can make our dashboard realtime and dynamic simply by listening for changes on our sales data and calling a function which will redraw the charts based on the updated data. Remember that the on() method immediately fires the callback function so the code to initially draw our charts, or rather populate the data into our charts, is the same to initialise as it is to handle changes.
As an aside, if you find yourself needing to query a Firebase reference to get some data but don’t want to continue to listen for changes then you can call the once() method instead of on().
We now need to write the salesListener callback function to implement the logic which will utilise the data to populate our charts. Let’s start by implementing the Sales By Month chart.
It can be easy to get confused about how the data is structured so the best way to have a picture of the data you’re dealing with is to click on the relevant node on the Firebase dashboard, in this case the sales node. This tells you exactly what data we’ll be getting from that node reference. In this case it’s a series of child nodes with each one representing a particular month. Inside each of those month nodes are data objects containing the actual sales data and a property containing the month name. Our purpose is to sum up the sales and profit totals from all of the sales records for each month and grab the month name for the chart labels.
In our listener function we start off by initialising our values. We’ll be using 2 variables to sum up our sales and profit values for each month and once each month is dealt with we’ll push those values into our data arrays for the chart. Each time our listener function is called we’ll reset the chart’s data arrays first :
var salesByMonthListener = function salesByMonthListener(snapshot) {
dataSalesByMonth.labels = [];
dataSalesByMonth.datasets[0].data = [];
dataSalesByMonth.datasets[1].data = [];
Notice that in our callback function we have a parameter called snapshot. This is an object passed in by Firebase and it contains the data that we’ve asked for along with lots of other tools to deal with that data. The actual data is accessed by calling snapshot.val(). In this case simply calling val() straight on the snapshot won’t give us too much as the sales node itself doesn’t contain any useful data as it’s contained in child objects. Fortunately the snapshot object contains a forEach() method which enables us to iterate over it's child nodes. A quick look at the Firebase dashboard will show us that the immediate child nodes are objects each representing a month. So we need to use forEach() to iterate over our month objects.
forEach() expects another callback function which is triggered for each iteration over a child node. Our forEach() callback function also receives a snapshot parameter which contains just the data for that child node.
snapshot.forEach(function(salesMonthSnapshot) {
sales = 0; profit = 0;
Each time the forEach() callback function is called it represents a new month, so we’ll reset our variables for totalling up the sales and profit to zero. We can now use the salesMonthSnapshot to reference the data specific to the current child node which represents a month. Again we get at the actual data by using the val() method. As the name of the month is stored on the node we’re dealing with, we can use salesMonthSnapshot.val() . Once we’ve got the name of the month we can push it into the labels array on our chart.
thisMonthName = salesMonthSnapshot.val().month;
dataSalesByMonth.labels.push(thisMonthName);
Now things get interesting. The actual data necessary for us to sum up our monthly sales and profit values are nested in another level of child objects so we can’t simply use salesMonthSnapshot.val() to access that data as we did with the month name. So how can we access them? We use the forEach() method again for this next level of nodes and iterate around them. Each level of nested nodes has it’s own forEach() method to which we can assign a callback which receives the snapshot for data specific to that level. Again the Firebase dashboard is useful in giving us a visual picture of what we’re dealing with by clicking into each level of node to see what each snapshot will give us.
salesMonthSnapshot.forEach(function(salesSnapshot) { });
We’re now dealing with a third level snapshot which we’re calling salesSnapshot. The callback function to deal with this is relatively straightforward - it needs to call the val() method of the salesSnapshot and increase the value of the total sales and profit by the sales and profit properties on each sales object.
There is a fly in the ointment here though - the month property, which we grabbed using val() in the higher level callback, is still considered a child node and will be iterated over by forEach(). That node does not contain properties for sales and profit and when we attempt to increase the totals using these non-existent properties it will result in an incorrect value.
We can however deal with this quite simply using the hasChildren() method. Checking to see if the salesSnapshot of the current node has children effectively tells us whether the node we’re dealing with is an object with properties or if it’s simply a value. This means we can ignore anything that doesn’t have any child properties. Once we’ve determined that the current node is a valid object we can simply increase the sales and profit values by calling val() on the salesSnapshot and the name of the property we want.
salesMonthSnapshot.forEach(function(salesSnapshot) {
if (salesSnapshot.hasChildren()) {
sales += salesSnapshot.val().sales;
profit += salesSnapshot.val().profit;
}
});
We now have the total sales and profit values for the month we’re dealing with and back in the second level callback function we can pass these totals into the data arrays for the chart. Remember that we have 2 datasets on the chart data, one for sales and one for profit and we push each value in to the data array on the appropriate dataset.
dataSalesByMonth.datasets[0].data.push(sales);
dataSalesByMonth.datasets[1].data.push(profit);
The second level callback function, dealing with each specific month, has done it’s job so we fall back out to the original listener callback function. We now have all the data we need in the chart's data object so we now pass it into our chart. To make sure we have a fresh chart object each time a change is triggered we reset the chart object variable to a blank object and then re-create it. We then re-generate our chart’s legend :
chartSalesByMonth = {};
chartSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
$("#legendSBM").html(chartSalesByMonth.generateLegend());
Refresh your page in the browser and you’ll still have your 3 charts except the Sales By Month chart will now be generated from live data in your Firebase.
Open your page and the Firebase dashboard in two separate browser windows and put them side-by-side. In the sales data make changes to the sales and profit values and watch your chart update in realtime. Pretty cool or what?!
OK so we’ll close off here and in part 2 I’ll complete the app by going through how to query the data to populate the other 2 charts which are little bit more tricky. See you there!
PS : Here’s a listing of the finished JS file :
var ctxSalesByMonth = $("#salesByMonth").get(0).getContext("2d");
var ctxSalesByPerson = $("#salesByPerson").get(0).getContext("2d");
var ctxSalesByRegion = $("#salesByRegion").get(0).getContext("2d");
var dataSalesByMonth = {
labels : [],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: []
},
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: []
}
]};
var dataSalesByPerson = {
labels : ["Bill", "Bob", "Jane", "Judy", "Max", "Holly"],
datasets: [
{
label: "Total Sales",
fillColor: "rgba(67, 214, 92, 0.5)",
strokeColor: "rgba(67, 214, 92, 1)",
pointColor: "rgba(67, 214, 92,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(67, 214, 92,1)",
data: [20000, 25000, 30000, 22000, 28000, 34000]
},
{
label: "Gross Profit",
fillColor: "rgba(218, 233, 39, 0.5)",
strokeColor: "rgba(218, 233, 39, 1)",
pointColor: "rgba(218, 233, 39,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(218, 233, 39,1)",
data: [10000, 12500, 15000, 11000, 14000, 17000]
}
]};
var dataSalesByRegion = [
{
label: "West USA",
color: "rgba(67, 214, 92, 0.5)",
highlight: "rgba(67, 214, 92, 1)",
value: 20000
},
{
label: "UK",
color: "rgba(218, 233, 39, 0.5)",
highlight: "rgba(218, 233, 39, 1)",
value: 12500
},
{
label: "Asia",
color: "rgba(230, 150, 200, 0.5)",
highlight: "rgba(230, 150, 200, 1)",
value: 25000
},
{
label: "West Europe",
color: "rgba(200, 220, 50, 0.5)",
highlight: "rgba(200, 220, 50, 1)",
value: 18000
}
];
var lineSalesByMonth;
barSalesByPerson = new Chart(ctxSalesByPerson).Bar(dataSalesByPerson);
pieSalesByRegion = new Chart(ctxSalesByRegion).Doughnut(dataSalesByRegion);
$("#legendSBP").html(barSalesByPerson.generateLegend());
$("#legendSBR").html(pieSalesByRegion.generateLegend());
$(document).ready(function() {
firebaseRef = new Firebase("https://scorching-fire-224.firebaseio.com");
var salesRef = firebaseRef.child("sales");
salesRef.on('value', salesByMonthListener);
var salesByMonthListener = function salesByMonthListener(snapshot) {
dataSalesByMonth.labels = [];
dataSalesByMonth.datasets[0].data = [];
dataSalesByMonth.datasets[1].data = [];
snapshot.forEach(function(salesMonthSnapshot) {
sales = 0;
profit = 0;
thisMonthName = salesMonthSnapshot.val().month;
dataSalesByMonth.labels.push(thisMonthName);
salesMonthSnapshot.forEach(function(salesSnapshot) {
if (salesSnapshot.hasChildren()) {
sales += salesSnapshot.val().sales;
profit += salesSnapshot.val().profit;
}
dataSalesByMonth.datasets[0].data.push(sales);
dataSalesByMonth.datasets[1].data.push(profit);
lineSalesByMonth = {};
lineSalesByMonth = new Chart(ctxSalesByMonth).Line(dataSalesByMonth);
$("#legendSBM").html(lineSalesByMonth.generateLegend());