This post builds on Mike Bostock’s great tutorial on how selection works on nested data and his series on the update pattern. To make the example more realistic, let us build a table that shows counts of log messages for different applications and for the severity levels DEBUG
, INFO
, WARN
, ERROR
, and FATAL
. The table will update itself to changes in the log count data. Messages could be pushed by WebSockets, but we will just simulate this for now.
Here is how the finished logging table application looks like. Feel free to play around with it.
Try it out on jsFiddle: http://jsfiddle.net/skropp/k43r9qmc/10/
You can easily adapt this pattern to show nested bar charts or similar.
Let us now reconstruct the code piece by piece and start with looking at only one application. The first goal is to update individual counts. The HTML for the table looks like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
This table
uses Bootsrap CSS classes to make it look a little nicer.
We are now just hooking us into the DOM
at <tr id="Application1"></tr>
to create the <td>
’s on ENTER
and change the text of the <td>
on update. This is what is needed to make it work with just a few lines of code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
This is all normal D3, except that in order to make the changed values light up in red, we have to store the previous value for that cell and compare it with the old one. We have to do that because despite its name UPDATE
, D3 is not really detecting updates on the individual value level. UPDATE
is executing on each element in the row where a change has occurred and we do not want every cell transition to red.
We store the value in the DOM
with the attribute data-prevVal
. Depending on your situation, it might be a better to store these previous values in the property of the javascript DOM
element representation itself. This is how D3 does it with the __data__
property.
Additionally it would be better to use the selection.filter()
function to prevent UPDATE
to fire on the values that have not really changed. But this is advanced and harder to understand.
This is how table
looks like for one application:
Application Name | DEBUG | INFO | WARN | ERROR | FATAL |
http://jsfiddle.net/skropp/bkr4ao09/1/
Calling the renderLoop
all the time we change data
is a very imperative style of programing. What we actually want is that it automatically re-renders when data
changes. We could achieve that by using libraries like RxJS and make data
an Observable
collection.
Most of the time your JavaScript app is not receiving data in this simple array fashion of var data = ["Application1", 2, 3, 4, 5, 6]
. It is likely going to look more like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
You now have two option to adjust to that schema. One is to transform the data into a simple array whenever you receive these types of messages. The other way is to create the schema on read within D3 selection.data()
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This creates a name-value collection on the fly. Inside our UPDATE
and ENTER
sections we now reference d.name
and d.value
.
1 2 3 4 5 6 7 8 |
|
The other change that you will see in the code is that the table
is created from scratch with D3. We cannot work with static table
template on the page because we display a message once if there is no data coming in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Have fun coding!