So far we have been writing very small apps in Cycle.js. Let's start growing the size of the apps we build. This time, we will see how to build a simple Body-Mass Index Calculator.
So far we've been writing very small apps in Cycle.js, next let's try something a bit bigger. Say for instance a body mass index calculator from your weight and your height. We always start by returning an object to syncs, you have the DOM sync it's an observable with just one event. While we've seen that trick of starting by writing static views, we have a DIV container for everything, and then we're going to have two other DIVs, this one will be for the weight, and this one will be for the height.
Here we're going to have a label for the weight, we're going to have placeholder data there, 00 kilograms, and then we're going to have the input with class weight and type is range with a minimum of say 40 kilograms and a maximum of 150, default value can be 70. For the height we have almost the same thing, we have a label and we have the slider, except here we have the label slightly different, it's in centimeters, class name height, minimum can be 140 centimeters and 220 as the maximum, and the default 170.
Then after these two parts we have the header saying, "BMI is this much." So all of these are placeholder data, we just have static data here, and the objective of our app is whenever we change the slider, whenever the user inputs some change here on the slider, we should recalculate the BMI and then display the BMI. So let's write those three steps down. We have the text slider change, we have recalculate the BMI, and we have display the BMI.
As usual we know that displaying things on the DOM is a DOM write effect. We saw that in previous lessons. Detecting user events, user input events such as this one is a DOM read effect. But this one is neither read nor write effect, it's not an effect at all, it's just logic, it's just calculations, it's just math. This one is really just logic.
This gives us a guide on what to do, first we're going to detect our read effects by making the stream let's say changeweight stream is of sources.DOM, for that we're going to select the weight element, and we're interested in the input events. We're going to map each of these input events to the value in the target. Then we're going to do essentially the same for the height, just on a different element. We're going to get that element there.
Now we have both of these in read effects being detected, we just need to recalculate the BMI. How do we do that, we need to stream for the BMI and body mass index is calculated of both the values of weight and height, so merge is not enough, we need an operator like combine latest. We can combine this stream with the height stream, and then we can give the function to get those values that were emitted in these streams and return something. We're going to return the BMI there at some point.
So we need to calculate here the BMI. We first convert from centimeters to meters, and the BMI is math round of the weight divided by the height in meters squared. This gives us the BMI stream, but it's calculated from the change weight and change height streams. So initially this doesn't emit anything, so at the app startup nothing has happened yet on this, because no event happened. So it means that initially we wouldn't be able to calculate the BMI at all because this doesn't have an event yet.
What we need to do is the usual, is prepend a default event there so that we can calculate something initially. This is just like in previous lessons, we need to have a start with in order to bootstrap our app with data. Now that we have the BMI stream we can just map that to the DOM like this, and get that value which was calculated and put it here, and then it works. If you try to change this slider here, the BMI will be recalculated, but oddly the slider itself is not changing and the label here is not changing.
That's because we set the constant value here for the input slider, and also here as you can see the label, it's constant. For this we need the value of weight. The BMI is not enough, we cannot calculate the weight from the BMI anymore, it's lost, so we need to have a way of getting those values, not just the BMI, but the weight and the height. Ideally we want something like this, we want to have a stream of state objects and this is just the state object that has BMI but it also has the value for the weight, and it also has the value for the height.
How do we make that? It's actually pretty simple. Instead of making the BMI stream we can make the state stream, and instead of returning just the BMI we can return an object that has BMI, but also has weight and height. Now will work, we can just change the label as well and there we go, 70 kilos. Do almost the same here for the height, and there we go. It works as we want it to work.
Great. The lesson here was that sometimes you have a sandwich where the middle part here is not an effect at all, it's actually state handling, or basically calculations of data and at the streams you have a read effect and a write effect.