We are starting to get a better architecture for these UI apps. But we still have a problem of repeating code for the sliders, since they share so much in common in looks and functionality. This lessons shows how we can create a generic labeled slider as a main() function receiving properties as sources.
We're starting to get a better architecture for these UI apps, but we still have a problem. We shouldn't be repeating code. Since the weight and height slider are so similar in looks and functionality, we have repeated code here in the intent, these two are quite similar, and we have repeated code in the view, as well.
We need to be able to create a generic labeled slider component, and then somehow just reuse that for the weight and the height.
We're going to do something now a bit radical. Let's create a main function that only implements the generic labeled slider. Let's forget for now the BMI calculator. Let's make function main, which takes sources and returns an object of sinks, the DOM sink.
Then here we're going to have a DIV that has class name labeled slider, and then has children, has a label. Text content can be weight of 0.0 kilograms. The input has classname slider, is of type range, has a minimum of 40, maximum of 150, and the default is 70.
OK. It appeared there as elements. Now we need to capture the interactions. We're going to make a change stream. Take sources.DOM. Select the slider. Take the events of type input and map each event to the value and the target.
Then we have the value stream, or this is state stream but instead of state object, it's just number. It takes change stream, all of the future values that the user inputs, but we're going to start with the initial value of 70.
Then we can use this to map each of these values to these elements. We're going to put that value in the label, like that. We're going to also put in this slider, and ES 6 allows us to make a shortcut here. We're just going to write it like that.
Now the labeled slider works as it was in the BMI calculator, but now we just have only the labeled slider.
Let's refactor this with model view intent as we saw. We have intent here, and that takes DOM source and returns this here, except it's not sources.DOM, it's DOM source, because that's the parameter. That's how we get intent. We pass in sources.DOM. This one here will be in the model. Then the last one is in the view.
That's how we get the v tree stream, by giving the value stream here, and return the v tree stream as the DOM sink. Still works, we just refactored it.
Now we have a labeled slider that is not generic because, of course, we have always the hard-coded weight as the label here, and also kilograms and minimum and maximum.
How can we make this main, which is now a small program for a labeled slider, how can we make this receive properties such as which should be the label? Which should be the minimum and etc.?
Everything comes to main through the sources. If we want to provide something, it needs to come through sources. What creates sources are these drivers.
We need to make a driver here. It's called props. Drivers are always functions that take sink stream and return a source stream. Since this driver is meant only for providing data to the main, we don't need the sink. We only need to create a source that's an observable, that it's a stream of just an object. That object will have the properties that we want to pass to our app.
The label should be height. That's what we're seeing here. The units should be, in this case, centimeters. The minimum value is, let's say, 140, maximum of 220, and the initial value of 170. Now sources has not just .DOM, but it has .props.
We can pass it to the model because the model handles all the state and logic, so that's what's going to appear here, as a property stream. Sources.props is the source of this props driver. That's an observable, that's why you would call it prop stream.
We want to get the initial value and put it here. How do we actually get the initial value? Let's just create a stream for it. Let's call it initial value stream. It takes props stream. We're going to map that props object to props.init which is the actual number. We're going to take the first one. Just in case props emits multiple values we just are interested in the first one.
We cannot exactly do that because start with doesn't support that, but it doesn't matter because start with is just a version of concatenation. We can make an observable called value, which is initial value, stream concatenated with the change stream. Basically you could think of this as new values. It doesn't matter, it's just a name.
Now that we have value stream, we want to return this observable of state objects, but we need the values and also the props. We're going to use combine latest to combine the value stream and the props stream. Then I'm going to get a function that receives the value and the props. Then we're going to return that state object.
This is the state object. State object has label as props.label, has unit as props.unit, and then it has min as props.min, max as props.max. Then it has value. Now this is our state object with everything necessary to render that to the DOM.
What it returns is now a state object, just passed to the view, like that. Finally now we have the state object. We can use that. Let's use yes, [inaudible 7:06] strings. We can use the state.label: . Then what else do we have?
We still have state.value and state.unit. Then we also have state.min and state.max. Here this value is state.value.
There we go. We have a labeled slider that takes its label from the properties that are given from outside of the main. Then, centimeters is also from outside the main. We could change this to be whatever -- a, b, c, or a, s, d, and it changes over there.
The idea is that this program here is generic because we can pass these properties in as sources, and we can use those to customize our component so that it's not just static. It's not always the same, but it has some customization.