So far we have been writing very small apps in cycle.js. Let's try something a bit bigger now, let's say, for instance a body mass index calculator, which has a weight and height slider and then it shows the BMI value.
As usual we start by returning an object of sinks and here we're going to have only the DOM sink, which we're going to send out the VDOM stream. We need to create that stream. VDOM stream is xs.of, and then here we're going to put a div. We're going to have div for the weight, and then a similar div for the height.
Here for the weight we are going to have a label saying, "weight is these many kilos." Then we're going to have an input. Let's give it a class name weight.
Then attributes, we need a type for that input. It's going to be range. Then we have also the attributes, minimum value, let's say, 40 kilos, maximum, let's say, 150, and default value could be the minimum one.
We're going to have a very similar situation for the height, so I'm just going to copy-paste that and change some values here. The label should say, "height is these many centimeters," and the class name height. Let's change these values a little bit. Finally here we're going to have a header that says BMI is this much. We can display that on the DOM and we see the elements.
This is not interactive yet, we need to handle those. Let's write down what do we want. We basically want to detect this sliding event, recalculate the BMI formula, and then display the BMI.
Let's write down those steps, it helps. Detect sliding event, user event, and then recalculate BMI, and display the BMI. By the way, the formula for BMI is weight divided by height squared, where height is in meters.
We know that displaying stuff on the DOM is a DOM write effect, it's the easy part. We also know that getting user events is a DOM read effect. This part in the middle, we're not really doing any sort of read or write effect to the external world, we're just really updating a state variable here inside our app. It's neither of those.
We already have this part here for writing the DOM almost ready. Let's do this part here, detecting the sliding events. Let's make a stream called, "change weight stream," so we get that from sources.DOM. We can select our slider and we can get a stream of events of type input.
Once we have that we can actually just map each DOM event to the target, which gives us the element and the value. We want the number here for the slider every time an event fires. Something very similar for height, we're just going to change here these names, and that should be done.
We have this part ready, now we just need to do this part. We're going to make a stream called, "BMI stream." We need to combine the two values from these streams in order to make that formula. We use for that an operator called, "extreme combine." We put together change weight stream and change height stream.
This will return to us a stream of array. We have, for instance, the shape is an array that has here the weight value and the height value. We can map this array to our calculation. We just need to return here the BMI value. We need to calculate that first.
First we're going to convert height to meters, so we're just going to make a variable here called, "height in meters." That's the height in centimeters times 001. Then the BMI value is just that formula, weight divided by height in meters squared, like that, and we return it.
Now we have this BMI stream, we can just display that value in the stream by mapping that, mapping each BMI value to this virtual DOM that displays that value. Let's run this.
Well, usually we see emptiness. That's because this stream doesn't emit unless we have a value there, so we need to do start with. Then let's put here, for instance, my values. I weigh this much. Then 179.
Once we do that we see those values there, and the actual BMI value. I'm just going to simplify this by making the number and integers in a float. There we have, 22.
As you see, we don't have here this label value changing as we change that, but the BMI is being displayed correctly. Actually we need here, we need that value for weight. Unfortunately we only have the value for the BMI. We can't really extract, do the calculation the other way around in order to get the weight. I don't think that works.
We also need here the same thing for the height. It would be better if we had here an object, a state object that would have those properties like state.weight, state.height, and then I could also initialize here, for the slider, what's the default value. It would be better if we had that situation.
We just need this to be an object, so it's not going to be a BMI stream anymore, it's going to be a state stream and we need to transform our BMI stream to state stream. Gladly in this case it's quite simple. Here instead of returning the BMI value we can return the BMI plus the weight and the height all as an object. That's what I'm calling my state object. This should work.
Now the state object has all of these and they're going to be displayed on the DOM like that, and those values change as well while we slide here. The lesson here is that sometimes you have a sandwiched situation here where this part handles read effects, that part handles write effects, but this part in the middle is not effects with external world, it's just state handling internal to your application and the effects are just at the extremes here.