Like an array, Observable has a map method that allows us to transform a sequence into a new Observable.
Now that we've explained how you get data out of an Observable and how you can convert a DOM event into an Observable, let's take a look at how we can use the map function and the other functions to transform Observables into other Observables the same way we use map to transform Arrays into other Arrays.
What if I want to take this click stream and create a point stream? What I want to do is I want to take each one of the event objects that comes out of our click stream. I want to map it into the point, the (x, y) point at which the button was clicked.
If I want to take each item in that collection and I want to transform it into another item, I'm going to use the map method, just like I would if I was taking an Array of numbers and then map them and add one to each of them.
I'm going to map over this. I'm going to get out an event object. I'm going to return an object with an x that's equal to clientX and a y that's equal to the clientY property on the event object. That is going to create me another Observable.
I'm now going to change clicks to be points. I'm just going to subscribe to points instead of clicks. I'm going to alert. I'm going to change this now. It's no longer an event object. It's a point object. I'm going to now change my clicked message so we print out the point.
Now it should only print one point because we're still disposing of our subscription object as soon as the first one is clicked. As I click this, now we see the point on the screen where that button was clicked.
If I attempt to click more, nothing happens because after the first click, we unsubscribed. Just to drive the point home, if I delete this, it will just keep on printing out clicks. What I'll do is I'll put it back in here.
There's one more thing you need to understand about an Observable. An Observable is lazy. What do I mean when I say "Lazy"? Here, notice we created the points Observable by mapping over the clicks Observable. This is just one thing I want to drive home here.
When we map over Arrays, we get Arrays. When we filter Arrays, we get Arrays. When we concatAll Arrays, we get Arrays. When we map over Observables, we get Observables. When we filter Observables, we get Observables and so on and so forth.
As soon as you start with an Observable, you know you're going to have only Observables. As you call map and filter on that Observable, you're just going to create new Observables.
Having made that point, the other point that I want to make is that Observables are lazy. If I comment out...Notice I'm no longer forEach'ing over these points Observable. If I call "Click me," I want you to notice that nothing happens. As a matter of fact, addEventListener, under the hood, has not even been called by Observable.fromEvent.
The way Observable works is it waits until you call forEach to have any side effects, to carry out any side effects whatsoever. What we've done is we've really just built an Observable that promises that when you will call forEach on it, it will hook up an event listener.
When we map over that Observable, we've created another Observable that promises that when we forEach over it, it will forEach over the underlying data source, clicks, and then, as data arrives, will transform that data, using a projection function, into new data. Just simply creating Observables causes nothing to happen.
We have to forEach over the Observable in order for something to happen. That'll become clear, how that mechanism works, because later on, we're actually going to write our own Observable, from scratch, around a simple library. Once again, to drive the point home, I'll run this. We get the points that are clicked.