Welcome to Step 3 of our “Exploring Riot.js – Part 3”, humbly named:
Step #3 – an even even more amazing app than the even more amazing app (could it be?)
In the Step 2 of our “Exploring Riot.js – Part 3″, I’ve managed to sell you an amazing UX experience of the “Reload Stocks” button (maybe I should apply to our marketing office after all?). Yes, that was an awesome feature but unfortunately, by the time you’ve read the previous post, the feature was getting a bit old-fashioned… Why not go back to our roots? Going back to minimalist concepts? What if we were removing back the “Reload Stocks” button and at the same time we needn’t a F5 to refresh the browser? What if the table and the chart were refreshed every time the stocks change? In almost real-time…
Wait! Real-time you said? That reminds something… Yes! That’s it! An awesome technology that transforms a JSON API into a streaming API! And Stockmarket is a JSON API! It sounds like a job for Streamdata.io!
Let’s start discovering how we can make our amazing app even more amazing (let’s say awesome!).
To better understand how to give a real-time flavor to our app, let’s have an insight of our streamdata.io technology.
streamdata.io is a proxy that transforms a JSON API into a push API. Passing through streamdata.io offers several assets:
- first, it pushes data only when the data has changed. Just the opposite, polling a JSON API doesn’t guarantee you will get only the changes. Between two or more pollings, the data may have not changed, so, you may make useless network calls. Thus, push technologies saves useless server calls.
- secondly, streamdata.io pushes data as JSON-Patch (aka RFC-6902) which are in general smaller than the whole set of the data. Indeed, JSON-Patch is a series of operations to patch the original set of data. In other words, streamdata.io only sends the patch operations related to the changes; not the whole data. In general, the network exchanges are smaller than if we were getting the whole set of data (which includes the data rows that haven’t changed too). That’s a good news. Especially in a mobile network context.
- thirdly, streamdata.io offers also a cache, which means that if there are no changes on the API server, any new clients that pass through streamdata.io will get the data from the cache of our technology. From the API server, that’s a huge advantage: streamdata.io relieves the API server of the burden of the client calls. Let’s imagine thousands and thousands clients calling the API server. It may be overcrowded by the requests. When passing through streamdata.io, one call is performed to the API server and the result dispatched to the clients: the API server won’t be snowed under the number of the requests.
Here are some major features of streamdata.io. If you wish to get more information, I invite you to visit our website!
We have discussed the context. It’s time to see how to achieve our goal: making our app amazingly reactive!
First, we need to add the streamdata.io Javascript SDK and a Javascript JSON-Patch library. Here I choose the fast-json-patch one. To install those two packages, run the following commands:
npm install streamdataio-js-sdk --save
npm install fast-json-patch --save
Now we need to import those two libraries in our index.html:
<script src="../node_modules/streamdataio-js-sdk/dist/streamdataio.min.js">></script>
<script src="../node_modules/fast-json-patch/dist/json-patch-duplex.min.js"></script>
Secondly, you need to get an appToken to be able to use streamdata.io. To do so, go to the streamdata.io portal, create an account and follow the instructions. A few minutes later, you will become a lucky developer who owns the precious appToken!
As our Riot.js components are loosely-coupled thanks to the use of observables, there are no changes from this side. Furthemore, our architecture follows the Open-Close principle through the StockMarketService. So, the changes will only reside in this class.
'use strict';
class StockMarketService {
constructor(appToken, bus) {
var self = this;
var url = "http://stockmarket.streamdata.io/prices";
this.bus = bus;
var streamdata =
streamdataio.createEventSource(url, appToken, [], null);
self.data = [];
streamdata.onData(function(data) {
self.data = data;
self.bus.trigger('newStocksEvent', {stocks: self.data}, true);
}).onPatch(function(patches) {
jsonpatch.apply(self.data, patches);
self.bus.trigger('newStocksEvent', {stocks: self.data}, true);
}).onError(function(error) {
console.error(error);
self.bus.trigger('errorStocksEvent', error);
self.streamdata.close();
});
streamdata.open();
}
}
We pass the appToken to the constructor of the StockMarketService and create a streamdata object by calling streamdataio.createEventSource(.). Once this object is created, we can register to different callbacks. For us, the two most important callbacks to register are these passed to onData(.) and to onPatch(.):
- onData(.) enables to register a callback to receive the first set of data
- onPatch(.) enables to register a callback to receive the patch operations (i.e. what has changed compared to the initial set of data). In the onPatch(.) function, we also call the JSON-Patch library to apply the patches
Once the received data has been formatted, we send it through the bus.
Don’t forget to call the open() function to effectively start the streaming session!
Note also that you don’t need to use a solution for dealing with CORS on the Stockmarket API url anymore: streamdata.io handles it natively.
That’s it for our StockMarketService.
Now, let’s delete the stockmarket-button.tag and update the way we fetch the stocks from the Stockmarket API in the index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello World</title>
<link rel="stylesheet" type="text/css" href="node_modules/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="app/css/main.css" />
<link rel="stylesheet" type="text/css" href="app/css/stockmarket-barchart.css" />
</head>
<body>
<!-- mount point -->
<stockmarket-table></stockmarket-table>
<stockmarket-barchart height="500" width="960"></stockmarket-barchart>
<!-- include riot.js -->
<script src="../node_modules/riot/riot.min.js"></script>
<script src="../node_modules/d3/build/d3.min.js"></script>
<script src="../node_modules/fast-json-patch/dist/json-patch-duplex.min.js"></script>
<script src="../node_modules/streamdataio-js-sdk/dist/streamdataio.min.js"></script>
<script src="../app/js/stockmarket-service.js"></script>
<script src="../dist/js/stockmarket-table.js"></script>
<script src="../dist/js/stockmarket-barchart.js"></script>
<script>
var appToken = <YOUR STREAMDATAIO APP TOKEN>
var bus = riot.observable();
var stockMarketService = new StockMarketService(appToken, bus);
var areMounted = false;
bus.on('newStocksEvent', function (param) {
if (!areMounted) {
riot.mount('stockmarket-table', {title: 'Stocks', bus: bus, stocks: param.stocks});
riot.mount('stockmarket-barchart', {title: 'Graph', bus:bus, stocks: param.stocks});
areMounted = true;
}
});
</script>
</body>
</html>
That’s all folks! With few changes, we now have a truly awesome app which UI updates in real-time! UX looks better than ever! 😉
Conclusion
The use of Riot.js observables helps to loosely-coupled our custom components. The Open-Close principle helps to minimize the code to change. Streamdata.io helps to bring a reactive UX to our demo. That rocks!
Oh, by the way, the code can be found on our GitHub (eventDrivenApp branch, tag step3).
Want more about riot.js?
Exploring Riot.js – Introduction
Exploring Riot.js – Get your hands dirty
Exploring Riot.js – Event-driven app (Step1)
Exploring Riot.js – Event-driven app (Step2)
Exploring Riot.js – Event-driven app (Step3) … you are here!
Exploring Riot.js – En route to…
Exploring Riot.js – Route 66 (Takeaway)